diff options
Diffstat (limited to 'drivers/vhost/net.c')
-rw-r--r-- | drivers/vhost/net.c | 56 |
1 files changed, 28 insertions, 28 deletions
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index 831eb4fd197d..a0fa5de210cf 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c | |||
@@ -70,7 +70,12 @@ enum { | |||
70 | }; | 70 | }; |
71 | 71 | ||
72 | struct vhost_net_ubuf_ref { | 72 | struct vhost_net_ubuf_ref { |
73 | struct kref kref; | 73 | /* refcount follows semantics similar to kref: |
74 | * 0: object is released | ||
75 | * 1: no outstanding ubufs | ||
76 | * >1: outstanding ubufs | ||
77 | */ | ||
78 | atomic_t refcount; | ||
74 | wait_queue_head_t wait; | 79 | wait_queue_head_t wait; |
75 | struct vhost_virtqueue *vq; | 80 | struct vhost_virtqueue *vq; |
76 | }; | 81 | }; |
@@ -116,14 +121,6 @@ static void vhost_net_enable_zcopy(int vq) | |||
116 | vhost_net_zcopy_mask |= 0x1 << vq; | 121 | vhost_net_zcopy_mask |= 0x1 << vq; |
117 | } | 122 | } |
118 | 123 | ||
119 | static void vhost_net_zerocopy_done_signal(struct kref *kref) | ||
120 | { | ||
121 | struct vhost_net_ubuf_ref *ubufs; | ||
122 | |||
123 | ubufs = container_of(kref, struct vhost_net_ubuf_ref, kref); | ||
124 | wake_up(&ubufs->wait); | ||
125 | } | ||
126 | |||
127 | static struct vhost_net_ubuf_ref * | 124 | static struct vhost_net_ubuf_ref * |
128 | vhost_net_ubuf_alloc(struct vhost_virtqueue *vq, bool zcopy) | 125 | vhost_net_ubuf_alloc(struct vhost_virtqueue *vq, bool zcopy) |
129 | { | 126 | { |
@@ -134,21 +131,24 @@ vhost_net_ubuf_alloc(struct vhost_virtqueue *vq, bool zcopy) | |||
134 | ubufs = kmalloc(sizeof(*ubufs), GFP_KERNEL); | 131 | ubufs = kmalloc(sizeof(*ubufs), GFP_KERNEL); |
135 | if (!ubufs) | 132 | if (!ubufs) |
136 | return ERR_PTR(-ENOMEM); | 133 | return ERR_PTR(-ENOMEM); |
137 | kref_init(&ubufs->kref); | 134 | atomic_set(&ubufs->refcount, 1); |
138 | init_waitqueue_head(&ubufs->wait); | 135 | init_waitqueue_head(&ubufs->wait); |
139 | ubufs->vq = vq; | 136 | ubufs->vq = vq; |
140 | return ubufs; | 137 | return ubufs; |
141 | } | 138 | } |
142 | 139 | ||
143 | static void vhost_net_ubuf_put(struct vhost_net_ubuf_ref *ubufs) | 140 | static int vhost_net_ubuf_put(struct vhost_net_ubuf_ref *ubufs) |
144 | { | 141 | { |
145 | kref_put(&ubufs->kref, vhost_net_zerocopy_done_signal); | 142 | int r = atomic_sub_return(1, &ubufs->refcount); |
143 | if (unlikely(!r)) | ||
144 | wake_up(&ubufs->wait); | ||
145 | return r; | ||
146 | } | 146 | } |
147 | 147 | ||
148 | static void vhost_net_ubuf_put_and_wait(struct vhost_net_ubuf_ref *ubufs) | 148 | static void vhost_net_ubuf_put_and_wait(struct vhost_net_ubuf_ref *ubufs) |
149 | { | 149 | { |
150 | kref_put(&ubufs->kref, vhost_net_zerocopy_done_signal); | 150 | vhost_net_ubuf_put(ubufs); |
151 | wait_event(ubufs->wait, !atomic_read(&ubufs->kref.refcount)); | 151 | wait_event(ubufs->wait, !atomic_read(&ubufs->refcount)); |
152 | } | 152 | } |
153 | 153 | ||
154 | static void vhost_net_ubuf_put_wait_and_free(struct vhost_net_ubuf_ref *ubufs) | 154 | static void vhost_net_ubuf_put_wait_and_free(struct vhost_net_ubuf_ref *ubufs) |
@@ -306,23 +306,26 @@ static void vhost_zerocopy_callback(struct ubuf_info *ubuf, bool success) | |||
306 | { | 306 | { |
307 | struct vhost_net_ubuf_ref *ubufs = ubuf->ctx; | 307 | struct vhost_net_ubuf_ref *ubufs = ubuf->ctx; |
308 | struct vhost_virtqueue *vq = ubufs->vq; | 308 | struct vhost_virtqueue *vq = ubufs->vq; |
309 | int cnt = atomic_read(&ubufs->kref.refcount); | 309 | int cnt; |
310 | |||
311 | rcu_read_lock_bh(); | ||
310 | 312 | ||
311 | /* set len to mark this desc buffers done DMA */ | 313 | /* set len to mark this desc buffers done DMA */ |
312 | vq->heads[ubuf->desc].len = success ? | 314 | vq->heads[ubuf->desc].len = success ? |
313 | VHOST_DMA_DONE_LEN : VHOST_DMA_FAILED_LEN; | 315 | VHOST_DMA_DONE_LEN : VHOST_DMA_FAILED_LEN; |
314 | vhost_net_ubuf_put(ubufs); | 316 | cnt = vhost_net_ubuf_put(ubufs); |
315 | 317 | ||
316 | /* | 318 | /* |
317 | * Trigger polling thread if guest stopped submitting new buffers: | 319 | * Trigger polling thread if guest stopped submitting new buffers: |
318 | * in this case, the refcount after decrement will eventually reach 1 | 320 | * in this case, the refcount after decrement will eventually reach 1. |
319 | * so here it is 2. | ||
320 | * We also trigger polling periodically after each 16 packets | 321 | * We also trigger polling periodically after each 16 packets |
321 | * (the value 16 here is more or less arbitrary, it's tuned to trigger | 322 | * (the value 16 here is more or less arbitrary, it's tuned to trigger |
322 | * less than 10% of times). | 323 | * less than 10% of times). |
323 | */ | 324 | */ |
324 | if (cnt <= 2 || !(cnt % 16)) | 325 | if (cnt <= 1 || !(cnt % 16)) |
325 | vhost_poll_queue(&vq->poll); | 326 | vhost_poll_queue(&vq->poll); |
327 | |||
328 | rcu_read_unlock_bh(); | ||
326 | } | 329 | } |
327 | 330 | ||
328 | /* Expects to be always run from workqueue - which acts as | 331 | /* Expects to be always run from workqueue - which acts as |
@@ -420,7 +423,7 @@ static void handle_tx(struct vhost_net *net) | |||
420 | msg.msg_control = ubuf; | 423 | msg.msg_control = ubuf; |
421 | msg.msg_controllen = sizeof(ubuf); | 424 | msg.msg_controllen = sizeof(ubuf); |
422 | ubufs = nvq->ubufs; | 425 | ubufs = nvq->ubufs; |
423 | kref_get(&ubufs->kref); | 426 | atomic_inc(&ubufs->refcount); |
424 | nvq->upend_idx = (nvq->upend_idx + 1) % UIO_MAXIOV; | 427 | nvq->upend_idx = (nvq->upend_idx + 1) % UIO_MAXIOV; |
425 | } else { | 428 | } else { |
426 | msg.msg_control = NULL; | 429 | msg.msg_control = NULL; |
@@ -683,7 +686,7 @@ static int vhost_net_open(struct inode *inode, struct file *f) | |||
683 | struct vhost_net *n = kmalloc(sizeof *n, GFP_KERNEL); | 686 | struct vhost_net *n = kmalloc(sizeof *n, GFP_KERNEL); |
684 | struct vhost_dev *dev; | 687 | struct vhost_dev *dev; |
685 | struct vhost_virtqueue **vqs; | 688 | struct vhost_virtqueue **vqs; |
686 | int r, i; | 689 | int i; |
687 | 690 | ||
688 | if (!n) | 691 | if (!n) |
689 | return -ENOMEM; | 692 | return -ENOMEM; |
@@ -706,12 +709,7 @@ static int vhost_net_open(struct inode *inode, struct file *f) | |||
706 | n->vqs[i].vhost_hlen = 0; | 709 | n->vqs[i].vhost_hlen = 0; |
707 | n->vqs[i].sock_hlen = 0; | 710 | n->vqs[i].sock_hlen = 0; |
708 | } | 711 | } |
709 | r = vhost_dev_init(dev, vqs, VHOST_NET_VQ_MAX); | 712 | vhost_dev_init(dev, vqs, VHOST_NET_VQ_MAX); |
710 | if (r < 0) { | ||
711 | kfree(n); | ||
712 | kfree(vqs); | ||
713 | return r; | ||
714 | } | ||
715 | 713 | ||
716 | vhost_poll_init(n->poll + VHOST_NET_VQ_TX, handle_tx_net, POLLOUT, dev); | 714 | vhost_poll_init(n->poll + VHOST_NET_VQ_TX, handle_tx_net, POLLOUT, dev); |
717 | vhost_poll_init(n->poll + VHOST_NET_VQ_RX, handle_rx_net, POLLIN, dev); | 715 | vhost_poll_init(n->poll + VHOST_NET_VQ_RX, handle_rx_net, POLLIN, dev); |
@@ -785,7 +783,7 @@ static void vhost_net_flush(struct vhost_net *n) | |||
785 | vhost_net_ubuf_put_and_wait(n->vqs[VHOST_NET_VQ_TX].ubufs); | 783 | vhost_net_ubuf_put_and_wait(n->vqs[VHOST_NET_VQ_TX].ubufs); |
786 | mutex_lock(&n->vqs[VHOST_NET_VQ_TX].vq.mutex); | 784 | mutex_lock(&n->vqs[VHOST_NET_VQ_TX].vq.mutex); |
787 | n->tx_flush = false; | 785 | n->tx_flush = false; |
788 | kref_init(&n->vqs[VHOST_NET_VQ_TX].ubufs->kref); | 786 | atomic_set(&n->vqs[VHOST_NET_VQ_TX].ubufs->refcount, 1); |
789 | mutex_unlock(&n->vqs[VHOST_NET_VQ_TX].vq.mutex); | 787 | mutex_unlock(&n->vqs[VHOST_NET_VQ_TX].vq.mutex); |
790 | } | 788 | } |
791 | } | 789 | } |
@@ -805,6 +803,8 @@ static int vhost_net_release(struct inode *inode, struct file *f) | |||
805 | fput(tx_sock->file); | 803 | fput(tx_sock->file); |
806 | if (rx_sock) | 804 | if (rx_sock) |
807 | fput(rx_sock->file); | 805 | fput(rx_sock->file); |
806 | /* Make sure no callbacks are outstanding */ | ||
807 | synchronize_rcu_bh(); | ||
808 | /* We do an extra flush before freeing memory, | 808 | /* We do an extra flush before freeing memory, |
809 | * since jobs can re-queue themselves. */ | 809 | * since jobs can re-queue themselves. */ |
810 | vhost_net_flush(n); | 810 | vhost_net_flush(n); |