vhost: fix attach to cgroups regression

Since 2.6.36-rc1, non-root users of vhost-net fail to attach
if they are in any cgroups.

The reason is that when qemu uses vhost, vhost wants to attach
its thread to all cgroups that qemu has. But we got the API backwards,
so a non-priveledged process (Qemu) tried to control
the priveledged one (vhost), which fails.

Fix this by switching to the new cgroup_attach_task_all,
and running it from the vhost thread.

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>

+60 -25
+60 -25
drivers/vhost/vhost.c
··· 60 60 return 0; 61 61 } 62 62 63 - /* Init poll structure */ 64 - void vhost_poll_init(struct vhost_poll *poll, vhost_work_fn_t fn, 65 - unsigned long mask, struct vhost_dev *dev) 63 + static void vhost_work_init(struct vhost_work *work, vhost_work_fn_t fn) 66 64 { 67 - struct vhost_work *work = &poll->work; 68 - 69 - init_waitqueue_func_entry(&poll->wait, vhost_poll_wakeup); 70 - init_poll_funcptr(&poll->table, vhost_poll_func); 71 - poll->mask = mask; 72 - poll->dev = dev; 73 - 74 65 INIT_LIST_HEAD(&work->node); 75 66 work->fn = fn; 76 67 init_waitqueue_head(&work->done); 77 68 work->flushing = 0; 78 69 work->queue_seq = work->done_seq = 0; 70 + } 71 + 72 + /* Init poll structure */ 73 + void vhost_poll_init(struct vhost_poll *poll, vhost_work_fn_t fn, 74 + unsigned long mask, struct vhost_dev *dev) 75 + { 76 + init_waitqueue_func_entry(&poll->wait, vhost_poll_wakeup); 77 + init_poll_funcptr(&poll->table, vhost_poll_func); 78 + poll->mask = mask; 79 + poll->dev = dev; 80 + 81 + vhost_work_init(&poll->work, fn); 79 82 } 80 83 81 84 /* Start polling a file. We add ourselves to file's wait queue. The caller must ··· 98 95 remove_wait_queue(poll->wqh, &poll->wait); 99 96 } 100 97 101 - /* Flush any work that has been scheduled. When calling this, don't hold any 102 - * locks that are also used by the callback. */ 103 - void vhost_poll_flush(struct vhost_poll *poll) 98 + static void vhost_work_flush(struct vhost_dev *dev, struct vhost_work *work) 104 99 { 105 - struct vhost_work *work = &poll->work; 106 100 unsigned seq; 107 101 int left; 108 102 int flushing; 109 103 110 - spin_lock_irq(&poll->dev->work_lock); 104 + spin_lock_irq(&dev->work_lock); 111 105 seq = work->queue_seq; 112 106 work->flushing++; 113 - spin_unlock_irq(&poll->dev->work_lock); 107 + spin_unlock_irq(&dev->work_lock); 114 108 wait_event(work->done, ({ 115 - spin_lock_irq(&poll->dev->work_lock); 109 + spin_lock_irq(&dev->work_lock); 116 110 left = seq - work->done_seq <= 0; 117 - spin_unlock_irq(&poll->dev->work_lock); 111 + spin_unlock_irq(&dev->work_lock); 118 112 left; 119 113 })); 120 - spin_lock_irq(&poll->dev->work_lock); 114 + spin_lock_irq(&dev->work_lock); 121 115 flushing = --work->flushing; 122 - spin_unlock_irq(&poll->dev->work_lock); 116 + spin_unlock_irq(&dev->work_lock); 123 117 BUG_ON(flushing < 0); 124 118 } 125 119 126 - void vhost_poll_queue(struct vhost_poll *poll) 120 + /* Flush any work that has been scheduled. When calling this, don't hold any 121 + * locks that are also used by the callback. */ 122 + void vhost_poll_flush(struct vhost_poll *poll) 127 123 { 128 - struct vhost_dev *dev = poll->dev; 129 - struct vhost_work *work = &poll->work; 124 + vhost_work_flush(poll->dev, &poll->work); 125 + } 126 + 127 + static inline void vhost_work_queue(struct vhost_dev *dev, 128 + struct vhost_work *work) 129 + { 130 130 unsigned long flags; 131 131 132 132 spin_lock_irqsave(&dev->work_lock, flags); ··· 139 133 wake_up_process(dev->worker); 140 134 } 141 135 spin_unlock_irqrestore(&dev->work_lock, flags); 136 + } 137 + 138 + void vhost_poll_queue(struct vhost_poll *poll) 139 + { 140 + vhost_work_queue(poll->dev, &poll->work); 142 141 } 143 142 144 143 static void vhost_vq_reset(struct vhost_dev *dev, ··· 247 236 return dev->mm == current->mm ? 0 : -EPERM; 248 237 } 249 238 239 + struct vhost_attach_cgroups_struct { 240 + struct vhost_work work; 241 + struct task_struct *owner; 242 + int ret; 243 + }; 244 + 245 + static void vhost_attach_cgroups_work(struct vhost_work *work) 246 + { 247 + struct vhost_attach_cgroups_struct *s; 248 + s = container_of(work, struct vhost_attach_cgroups_struct, work); 249 + s->ret = cgroup_attach_task_all(s->owner, current); 250 + } 251 + 252 + static int vhost_attach_cgroups(struct vhost_dev *dev) 253 + { 254 + struct vhost_attach_cgroups_struct attach; 255 + attach.owner = current; 256 + vhost_work_init(&attach.work, vhost_attach_cgroups_work); 257 + vhost_work_queue(dev, &attach.work); 258 + vhost_work_flush(dev, &attach.work); 259 + return attach.ret; 260 + } 261 + 250 262 /* Caller should have device mutex */ 251 263 static long vhost_dev_set_owner(struct vhost_dev *dev) 252 264 { ··· 289 255 } 290 256 291 257 dev->worker = worker; 292 - err = cgroup_attach_task_current_cg(worker); 258 + wake_up_process(worker); /* avoid contributing to loadavg */ 259 + 260 + err = vhost_attach_cgroups(dev); 293 261 if (err) 294 262 goto err_cgroup; 295 - wake_up_process(worker); /* avoid contributing to loadavg */ 296 263 297 264 return 0; 298 265 err_cgroup: