aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason Wang <jasowang@redhat.com>2016-04-25 22:14:33 -0400
committerMichael S. Tsirkin <mst@redhat.com>2016-08-01 14:44:51 -0400
commit04b96e5528ca97199b429810fe963185a67dd40e (patch)
tree35cef78e529e6fb49c34159f58d19fb07f54de3e
parent7235acdb1144460d9f520f0d931f3cbb79eb244c (diff)
vhost: lockless enqueuing
We use spinlock to synchronize the work list now which may cause unnecessary contentions. So this patch switch to use llist to remove this contention. Pktgen tests shows about 5% improvement: Before: ~1300000 pps After: ~1370000 pps Signed-off-by: Jason Wang <jasowang@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
-rw-r--r--drivers/vhost/vhost.c52
-rw-r--r--drivers/vhost/vhost.h7
2 files changed, 29 insertions, 30 deletions
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index 73dd16d0f587..0061a7bd85f6 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -168,7 +168,7 @@ static int vhost_poll_wakeup(wait_queue_t *wait, unsigned mode, int sync,
168 168
169void vhost_work_init(struct vhost_work *work, vhost_work_fn_t fn) 169void vhost_work_init(struct vhost_work *work, vhost_work_fn_t fn)
170{ 170{
171 INIT_LIST_HEAD(&work->node); 171 clear_bit(VHOST_WORK_QUEUED, &work->flags);
172 work->fn = fn; 172 work->fn = fn;
173 init_waitqueue_head(&work->done); 173 init_waitqueue_head(&work->done);
174} 174}
@@ -246,15 +246,16 @@ EXPORT_SYMBOL_GPL(vhost_poll_flush);
246 246
247void vhost_work_queue(struct vhost_dev *dev, struct vhost_work *work) 247void vhost_work_queue(struct vhost_dev *dev, struct vhost_work *work)
248{ 248{
249 unsigned long flags; 249 if (!dev->worker)
250 return;
250 251
251 spin_lock_irqsave(&dev->work_lock, flags); 252 if (!test_and_set_bit(VHOST_WORK_QUEUED, &work->flags)) {
252 if (list_empty(&work->node)) { 253 /* We can only add the work to the list after we're
253 list_add_tail(&work->node, &dev->work_list); 254 * sure it was not in the list.
254 spin_unlock_irqrestore(&dev->work_lock, flags); 255 */
256 smp_mb();
257 llist_add(&work->node, &dev->work_list);
255 wake_up_process(dev->worker); 258 wake_up_process(dev->worker);
256 } else {
257 spin_unlock_irqrestore(&dev->work_lock, flags);
258 } 259 }
259} 260}
260EXPORT_SYMBOL_GPL(vhost_work_queue); 261EXPORT_SYMBOL_GPL(vhost_work_queue);
@@ -262,7 +263,7 @@ EXPORT_SYMBOL_GPL(vhost_work_queue);
262/* A lockless hint for busy polling code to exit the loop */ 263/* A lockless hint for busy polling code to exit the loop */
263bool vhost_has_work(struct vhost_dev *dev) 264bool vhost_has_work(struct vhost_dev *dev)
264{ 265{
265 return !list_empty(&dev->work_list); 266 return !llist_empty(&dev->work_list);
266} 267}
267EXPORT_SYMBOL_GPL(vhost_has_work); 268EXPORT_SYMBOL_GPL(vhost_has_work);
268 269
@@ -305,7 +306,8 @@ static void vhost_vq_reset(struct vhost_dev *dev,
305static int vhost_worker(void *data) 306static int vhost_worker(void *data)
306{ 307{
307 struct vhost_dev *dev = data; 308 struct vhost_dev *dev = data;
308 struct vhost_work *work = NULL; 309 struct vhost_work *work, *work_next;
310 struct llist_node *node;
309 mm_segment_t oldfs = get_fs(); 311 mm_segment_t oldfs = get_fs();
310 312
311 set_fs(USER_DS); 313 set_fs(USER_DS);
@@ -315,29 +317,25 @@ static int vhost_worker(void *data)
315 /* mb paired w/ kthread_stop */ 317 /* mb paired w/ kthread_stop */
316 set_current_state(TASK_INTERRUPTIBLE); 318 set_current_state(TASK_INTERRUPTIBLE);
317 319
318 spin_lock_irq(&dev->work_lock);
319
320 if (kthread_should_stop()) { 320 if (kthread_should_stop()) {
321 spin_unlock_irq(&dev->work_lock);
322 __set_current_state(TASK_RUNNING); 321 __set_current_state(TASK_RUNNING);
323 break; 322 break;
324 } 323 }
325 if (!list_empty(&dev->work_list)) {
326 work = list_first_entry(&dev->work_list,
327 struct vhost_work, node);
328 list_del_init(&work->node);
329 } else
330 work = NULL;
331 spin_unlock_irq(&dev->work_lock);
332 324
333 if (work) { 325 node = llist_del_all(&dev->work_list);
326 if (!node)
327 schedule();
328
329 node = llist_reverse_order(node);
330 /* make sure flag is seen after deletion */
331 smp_wmb();
332 llist_for_each_entry_safe(work, work_next, node, node) {
333 clear_bit(VHOST_WORK_QUEUED, &work->flags);
334 __set_current_state(TASK_RUNNING); 334 __set_current_state(TASK_RUNNING);
335 work->fn(work); 335 work->fn(work);
336 if (need_resched()) 336 if (need_resched())
337 schedule(); 337 schedule();
338 } else 338 }
339 schedule();
340
341 } 339 }
342 unuse_mm(dev->mm); 340 unuse_mm(dev->mm);
343 set_fs(oldfs); 341 set_fs(oldfs);
@@ -398,9 +396,9 @@ void vhost_dev_init(struct vhost_dev *dev,
398 dev->log_file = NULL; 396 dev->log_file = NULL;
399 dev->memory = NULL; 397 dev->memory = NULL;
400 dev->mm = NULL; 398 dev->mm = NULL;
401 spin_lock_init(&dev->work_lock);
402 INIT_LIST_HEAD(&dev->work_list);
403 dev->worker = NULL; 399 dev->worker = NULL;
400 init_llist_head(&dev->work_list);
401
404 402
405 for (i = 0; i < dev->nvqs; ++i) { 403 for (i = 0; i < dev->nvqs; ++i) {
406 vq = dev->vqs[i]; 404 vq = dev->vqs[i];
@@ -566,7 +564,7 @@ void vhost_dev_cleanup(struct vhost_dev *dev, bool locked)
566 /* No one will access memory at this point */ 564 /* No one will access memory at this point */
567 kvfree(dev->memory); 565 kvfree(dev->memory);
568 dev->memory = NULL; 566 dev->memory = NULL;
569 WARN_ON(!list_empty(&dev->work_list)); 567 WARN_ON(!llist_empty(&dev->work_list));
570 if (dev->worker) { 568 if (dev->worker) {
571 kthread_stop(dev->worker); 569 kthread_stop(dev->worker);
572 dev->worker = NULL; 570 dev->worker = NULL;
diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h
index d36d8beb3351..6690e645d2f8 100644
--- a/drivers/vhost/vhost.h
+++ b/drivers/vhost/vhost.h
@@ -15,13 +15,15 @@
15struct vhost_work; 15struct vhost_work;
16typedef void (*vhost_work_fn_t)(struct vhost_work *work); 16typedef void (*vhost_work_fn_t)(struct vhost_work *work);
17 17
18#define VHOST_WORK_QUEUED 1
18struct vhost_work { 19struct vhost_work {
19 struct list_head node; 20 struct llist_node node;
20 vhost_work_fn_t fn; 21 vhost_work_fn_t fn;
21 wait_queue_head_t done; 22 wait_queue_head_t done;
22 int flushing; 23 int flushing;
23 unsigned queue_seq; 24 unsigned queue_seq;
24 unsigned done_seq; 25 unsigned done_seq;
26 unsigned long flags;
25}; 27};
26 28
27/* Poll a file (eventfd or socket) */ 29/* Poll a file (eventfd or socket) */
@@ -126,8 +128,7 @@ struct vhost_dev {
126 int nvqs; 128 int nvqs;
127 struct file *log_file; 129 struct file *log_file;
128 struct eventfd_ctx *log_ctx; 130 struct eventfd_ctx *log_ctx;
129 spinlock_t work_lock; 131 struct llist_head work_list;
130 struct list_head work_list;
131 struct task_struct *worker; 132 struct task_struct *worker;
132}; 133};
133 134