diff options
Diffstat (limited to 'drivers/vhost/net.c')
-rw-r--r-- | drivers/vhost/net.c | 47 |
1 files changed, 26 insertions, 21 deletions
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index 9a68409580d5..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; |
@@ -780,7 +783,7 @@ static void vhost_net_flush(struct vhost_net *n) | |||
780 | 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); |
781 | mutex_lock(&n->vqs[VHOST_NET_VQ_TX].vq.mutex); | 784 | mutex_lock(&n->vqs[VHOST_NET_VQ_TX].vq.mutex); |
782 | n->tx_flush = false; | 785 | n->tx_flush = false; |
783 | kref_init(&n->vqs[VHOST_NET_VQ_TX].ubufs->kref); | 786 | atomic_set(&n->vqs[VHOST_NET_VQ_TX].ubufs->refcount, 1); |
784 | mutex_unlock(&n->vqs[VHOST_NET_VQ_TX].vq.mutex); | 787 | mutex_unlock(&n->vqs[VHOST_NET_VQ_TX].vq.mutex); |
785 | } | 788 | } |
786 | } | 789 | } |
@@ -800,6 +803,8 @@ static int vhost_net_release(struct inode *inode, struct file *f) | |||
800 | fput(tx_sock->file); | 803 | fput(tx_sock->file); |
801 | if (rx_sock) | 804 | if (rx_sock) |
802 | fput(rx_sock->file); | 805 | fput(rx_sock->file); |
806 | /* Make sure no callbacks are outstanding */ | ||
807 | synchronize_rcu_bh(); | ||
803 | /* We do an extra flush before freeing memory, | 808 | /* We do an extra flush before freeing memory, |
804 | * since jobs can re-queue themselves. */ | 809 | * since jobs can re-queue themselves. */ |
805 | vhost_net_flush(n); | 810 | vhost_net_flush(n); |