workqueue: Add an API to create a singlethread workqueue attached to the current task's cgroup
Subsystems (6)
PATCH 2/3 workqueue: Add an API to create a singlethread workqueue attached to the current task's cgroup
Add a new kernel API to create a singlethread workqueue and attach it's task to current task's cgroup and cpumask.
include/linux/workqueue.h +1 -0
@@ -211,6 +211,7 @@ __create_workqueue_key(const char *name, int singlethread,#define create_freezeable_workqueue (name) __create_workqueue((name), 1, 1, 0)#define create_singlethread_workqueue (name) __create_workqueue((name), 1, 0, 0) +extern struct workqueue_struct *create_singlethread_workqueue_in_current_cg (char *name);extern void destroy_workqueue (struct workqueue_struct *wq);extern int queue_work (struct workqueue_struct *wq,struct work_struct *work);
kernel/workqueue.c +40 -0
@@ -35,6 +35,7 @@#include <linux/lockdep.h> #define CREATE_TRACE_POINTS #include <trace/events/workqueue.h> +#include <linux/cgroup.h> /* * The per-CPU workqueue (if single thread, we always use the first
@@ -193,6 +194,45 @@ static const struct cpumask *cpu_singlethread_map __read_mostly;*/ static cpumask_var_t cpu_populated_map__read_mostly ; +static struct task_struct *get_singlethread_wq_task (struct workqueue_struct *wq) +{ +return (per_cpu_ptr (wq->cpu_wq, singlethread_cpu))->thread; +} + +/* Create a singlethread workqueue and attach it's task to the current task's +* cgroup and set it's cpumask to the current task's cpumask. +*/ +struct workqueue_struct *create_singlethread_workqueue_in_current_cg (char *name) +{ +struct workqueue_struct *wq; +struct task_struct *task; +cpumask_var_t mask; + + wq =create_singlethread_workqueue (name); +if (!wq) + goto out; + +if (!alloc_cpumask_var (&mask,GFP_KERNEL )) + goto err; + +if (sched_getaffinity (current->pid, mask)) + goto err; + + task =get_singlethread_wq_task (wq); +if (sched_setaffinity (task->pid, mask)) + goto err; + +if (cgroup_attach_task_current_cg (task)) + goto err; +out: +return wq; +err: +destroy_workqueue (wq); + wq =NULL ; + goto out; +} +EXPORT_SYMBOL_GPL (create_singlethread_workqueue_in_current_cg); +/* If it's single threaded, it isn't in the list of workqueues. */ static inline int is_wq_single_threaded (struct workqueue_struct *wq) {
Michael S. Tsirkin
maintainerQuoted 4 lines · Sridhar Samudrala
Add a new kernel API to create a singlethread workqueue and attach it's task to current task's cgroup and cpumask.
Signed-off-by Sridhar Samudrala <[email protected]>
Could someone familiar with workqueue code please comment on whether this patch is suitable for 2.6.35?
It is needed to fix the case where vhost user might cause a kernel thread to consume more CPU than allowed by the cgroup. Should I merge it through the vhost tree?
Ack for this?
Thanks!
Quoted 71 lines
include/linux/workqueue.h +1
-0@@ -211,6 +211,7 @@ __create_workqueue_key(const char *name, int singlethread,#define create_freezeable_workqueue (name) __create_workqueue((name), 1, 1, 0)#define create_singlethread_workqueue (name) __create_workqueue((name), 1, 0, 0) +extern struct workqueue_struct *create_singlethread_workqueue_in_current_cg (char *name);extern void destroy_workqueue (struct workqueue_struct *wq);extern int queue_work (struct workqueue_struct *wq,struct work_struct *work);kernel/workqueue.c +40
-0@@ -35,6 +35,7 @@#include <linux/lockdep.h> #define CREATE_TRACE_POINTS #include <trace/events/workqueue.h> +#include <linux/cgroup.h> /* * The per-CPU workqueue (if single thread, we always use the first @@ -193,6 +194,45 @@ static const struct cpumask *cpu_singlethread_map __read_mostly;*/ static cpumask_var_t cpu_populated_map__read_mostly ; +static struct task_struct *get_singlethread_wq_task (struct workqueue_struct *wq) +{ +return (per_cpu_ptr (wq->cpu_wq, singlethread_cpu))->thread; +} + +/* Create a singlethread workqueue and attach it's task to the current task's +* cgroup and set it's cpumask to the current task's cpumask. +*/ +struct workqueue_struct *create_singlethread_workqueue_in_current_cg (char *name) +{ +struct workqueue_struct *wq; +struct task_struct *task; +cpumask_var_t mask; + + wq =create_singlethread_workqueue (name); +if (!wq) + goto out; + +if (!alloc_cpumask_var (&mask,GFP_KERNEL )) + goto err; + +if (sched_getaffinity (current->pid, mask)) + goto err; + + task =get_singlethread_wq_task (wq); +if (sched_setaffinity (task->pid, mask)) + goto err; + +if (cgroup_attach_task_current_cg (task)) + goto err; +out: +return wq; +err: +destroy_workqueue (wq); + wq =NULL ; + goto out; +} +EXPORT_SYMBOL_GPL (create_singlethread_workqueue_in_current_cg); +/* If it's single threaded, it isn't in the list of workqueues. */ static inline int is_wq_single_threaded (struct workqueue_struct *wq) {
Quoted 7 lines · Michael S. Tsirkin
Quoted 4 lines · Sridhar Samudrala
Add a new kernel API to create a singlethread workqueue and attach it's task to current task's cgroup and cpumask.
Signed-off-by Sridhar Samudrala <[email protected]>
Quoted 7 lines
Could someone familiar with workqueue code please comment on whether this patch is suitable for 2.6.35?
It is needed to fix the case where vhost user might cause a kernel thread to consume more CPU than allowed by the cgroup. Should I merge it through the vhost tree?
Ack for this?
I don't understand the reasons for this patch, but this doesn't matter.
I don't really see any need to change workqueue.c,
Quoted 4 lines
kernel/workqueue.c static const struct cpumask *cpu_singlethread_map __read_mostly; · line 200
Quoted 4 lines
kernel/workqueue.c +4
-0+static struct task_struct *get_singlethread_wq_task (struct workqueue_struct *wq) +{ +return (per_cpu_ptr (wq->cpu_wq, singlethread_cpu))->thread; +}
(Not sure this trivial static helper with the single caller makes sense, but
see below)
Quoted 24 lines
Quoted 24 lines
kernel/workqueue.c +24
-0+/* Create a singlethread workqueue and attach it's task to the current task's +* cgroup and set it's cpumask to the current task's cpumask. +*/ +struct workqueue_struct *create_singlethread_workqueue_in_current_cg (char *name) +{ +struct workqueue_struct *wq; +struct task_struct *task; +cpumask_var_t mask; + + wq =create_singlethread_workqueue (name); +if (!wq) + goto out; + +if (!alloc_cpumask_var (&mask,GFP_KERNEL )) + goto err; + +if (sched_getaffinity (current->pid, mask)) + goto err; + + task =get_singlethread_wq_task (wq); +if (sched_setaffinity (task->pid, mask)) + goto err; + +if (cgroup_attach_task_current_cg (task))
Quoted 8 lines
kernel/workqueue.c static const struct cpumask *cpu_singlethread_map __read_mostly; · line 233
Quoted 8 lines
kernel/workqueue.c +8
-0+ goto err; +out: +return wq; +err: +destroy_workqueue (wq); + wq =NULL ; + goto out; +}
Instead, cgroup.c (or whoever needs this) can do
struct move_struct {
struct work_struct work;
int ret;
};
static void move_func(struct work_struct *work)
{
struct move_struct *move = container_of(...);
if (cgroup_attach_task_current_cg(current))
ret = -EANY;
}
static struct workqueue_struct *create_singlethread_workqueue_in_current_cg(char *name)
{
struct workqueue_struct *wq;
struct move_struct move = {
.work = __WORK_INITIALIZER(move_func);
};
wq = create_singlethread_workqueue(name);
if (!wq)
return NULL;
queue_work(&move.work);
flush_work(&move.work);
if (move.ret) {
destroy_workqueue(wq);
wq = NULL;
}
return wq;
}Or. Just export wq_per_cpu() from workqueue.c (probably with a better name) and use it like the patch does.
But, imho, create_singlethread_workqueue_in_current_cg() does not belong to workqueue.c.
Oleg.
Michael S. Tsirkin
maintainerQuoted 11 lines · Oleg Nesterov
Quoted 10 lines · Michael S. Tsirkin
Quoted 4 lines · Sridhar Samudrala
Add a new kernel API to create a singlethread workqueue and attach it's task to current task's cgroup and cpumask.
Signed-off-by Sridhar Samudrala <[email protected]>Could someone familiar with workqueue code please comment on whether this patch is suitable for 2.6.35?
Quoted 6 lines
Quoted 4 lines
It is needed to fix the case where vhost user might cause a kernel thread to consume more CPU than allowed by the cgroup. Should I merge it through the vhost tree?
Ack for this?I don't understand the reasons for this patch, but this doesn't matter.
Depending on userspace application, driver can create a lot of work for a workqueue to handle. By making the workqueue thread belong in a cgroup, we make it possible to the CPU and other resources thus consumed.
Signature · 1 line
MST
Quoted 18 lines · Michael S. Tsirkin
Quoted 16 lines · Oleg Nesterov
Quoted 14 lines · Michael S. Tsirkin
Quoted 4 lines · Sridhar Samudrala
Add a new kernel API to create a singlethread workqueue and attach it's task to current task's cgroup and cpumask.
Signed-off-by Sridhar Samudrala <[email protected]>Could someone familiar with workqueue code please comment on whether this patch is suitable for 2.6.35?
It is needed to fix the case where vhost user might cause a kernel thread to consume more CPU than allowed by the cgroup. Should I merge it through the vhost tree?
Ack for this?
Quoted 6 lines
Quoted 1 line
I don't understand the reasons for this patch, but this doesn't matter.
Depending on userspace application, driver can create a lot of work for a workqueue to handle. By making the workqueue thread belong in a cgroup, we make it possible to the CPU and other resources thus consumed.
OK, I see, thanks for your explanation.
in case I wasn't clear... I didn't mean I dislike this idea, only the the implementation of create_singlethread_workqueue_in_current_cg(), it doesn't belong to workqueue.c imho.
Oleg.
Tejun Heo
maintainerHello,
Quoted 6 lines · Michael S. Tsirkin
Quoted 1 line
I don't understand the reasons for this patch, but this doesn't matter.
Depending on userspace application, driver can create a lot of work for a workqueue to handle. By making the workqueue thread belong in a cgroup, we make it possible to the CPU and other resources thus consumed.
Hmmm.... I don't really get it. The unit of scheduling in workqueue is a work. Unless you're gonna convert every driver to use this special kind of workqueue (and what happens when multiple tasks from different cgroups share the driver?), I can't see how this is gonna be useful. If you really wanna impose cgroup control on workqueue items, you'll have to do it per work item which might lead to the problem of priority inversion. Can you please describe what you're trying to do in more detail?
Thank you.
Signature · 1 line
tejun
Michael S. Tsirkin
maintainerQuoted 5 lines · Tejun Heo
Hello,
Quoted 2 lines · Michael S. Tsirkin
Quoted 1 line
I don't understand the reasons for this patch, but this doesn't matter.
Quoted 7 lines
Quoted 4 lines
Depending on userspace application, driver can create a lot of work for a workqueue to handle. By making the workqueue thread belong in a cgroup, we make it possible to the CPU and other resources thus consumed.
Hmmm.... I don't really get it. The unit of scheduling in workqueue is a work.
Yes. However, we use cgroups to limit when the workqueue itself is scheduled. This affects all of work done on this workqueue, so it's a bit
of a blunt intrument. Thus we are not trying to apply this
to all drivers, we intend to start with vhost-net.
Quoted 3 lines
Unless you're gonna convert every driver to use this special kind of workqueue (and what happens when multiple tasks from different cgroups share the driver?),
We'll then create a workqueue per task. Each workqueue will have the right cgroup. But we are not trying to selve the problem for every driver.
Quoted 4 lines
I can't see how this is gonna be
useful. If you really wanna impose cgroup control on workqueue items, you'll have to do it per work item which might lead to the problem of priority inversion.
Exactly. cgroup is per-workqueue not per work item. If driver wants to let administrators control priority for different kinds of items separately, driver will have to submit them to separate workqueues.
Quoted 4 lines
Can you please describe what you're trying to do
in more detail?Thank you.
vhost-net driver is under control from userspace,
it queues potentially a lot of work into the workqueue, which might load the system beyond the cgroup limits.
And staying within cgroups limits is important for virtualization where vhost is used.
Quoted 2 lines
Signature · 1 line
tejun
Tejun Heo
maintainerHello,
Quoted 7 lines · Michael S. Tsirkin
Quoted 3 lines
Unless you're gonna convert every driver to use this special kind of workqueue (and what happens when multiple tasks from different cgroups share the driver?),
We'll then create a workqueue per task. Each workqueue will have the right cgroup. But we are not trying to selve the problem for every driver.
Ah... I see. You're gonna use multiple workqueues. Once concern that I have is that this is abuse of workqueue interface to certain level and depends on the implementation detail of workqueue rather than its intended usage model. stop_machine() was a similar case and in the end it was better served by a different mechanism built on kthread directly (cpu_stop). Wouldn't it be cleaner to use kthread directly for your case too? You're basically trying to use workqueue as a frontend to kthread, so...
Thanks.
Signature · 1 line
tejun
Michael S. Tsirkin
maintainerQuoted 7 lines · Tejun Heo
Hello,
Quoted 4 lines · Michael S. Tsirkin
Quoted 3 lines
Unless you're gonna convert every driver to use this special kind of workqueue (and what happens when multiple tasks from different cgroups share the driver?),
Quoted 8 lines
Quoted 3 lines
We'll then create a workqueue per task. Each workqueue will have the right cgroup. But we are not trying to selve the problem for every driver.
Ah... I see. You're gonna use multiple workqueues. Once concern that I have is that this is abuse of workqueue interface to certain level and depends on the implementation detail of workqueue rather than its intended usage model.
Well, this is why I proposed adding a new API for creating workqueue within workqueue.c, rather than exposing the task and attaching it to cgroups in our driver: so that workqueue maintainers can fix the implementation if it ever changes.
And after all, it's an internal API, we can always change it later if we need.
Quoted 7 lines
stop_machine()was a similar case and in the
end it was better served by a different mechanism built on kthread directly (cpu_stop). Wouldn't it be cleaner to use kthread directly for your case too? You're basically trying to use workqueue as a frontend to kthread, so...Thanks.
Well, yes but we are using APIs like flush_work etc. These are very handy. It seems much easier than rolling our own queue on top of kthread.
Makes sense?
Quoted 2 lines
Signature · 1 line
tejun
Tejun Heo
maintainerHello, Michael.
Quoted 7 lines · Michael S. Tsirkin
Well, this is why I proposed adding a new API for creating workqueue within
workqueue.c, rather than exposing the task and attaching it to cgroups in our driver: so that workqueue maintainers can fix the implementation if it ever changes.And after all, it's an internal API, we can always change it later if we need.
...
Quoted 2 lines
Well, yes but we are using APIs like
flush_worketc. These are very handy. It seems much easier than rolling our own queue on top of kthread.
The thing is that this kind of one-off usage becomes problemetic when you're trying to change the implementation detail. All current workqueue users don't care which thread they run on and they shouldn't as each work owns the context only for the duration the work is executing. If this sort of fundamental guidelines are followed, the implementation can be improved in pretty much transparent way but when you start depending on specific implementation details, things become messy pretty quickly.
If this type of usage were more common, adding proper way to account work usage according to cgroups would make sense but that's not the case here and I removed the only exception case recently while trying to implement cmwq and if this is added. So, this would be the only one which makes such extra assumptions in the whole kernel. One way or the other, workqueue needs to be improved and I don't really think adding the single exception at this point is a good idea.
The thing I realized after stop_machine conversion was that there was no reason to use workqueue there at all. There already are more than enough not-too-difficult synchronization constructs and if you're using a thread for dedicated purposes, code complexity isn't that different either way. Plus, it would also be clearer that dedicated threads are required there for what reason. So, I strongly suggest using a kthread. If there are issues which are noticeably difficult to solve with kthread, we can definitely talk about that and think about things again.
Thank you.
Signature · 1 line
tejun
Michael S. Tsirkin
maintainerQuoted 41 lines · Tejun Heo
Hello, Michael.
Quoted 7 lines · Michael S. Tsirkin
Well, this is why I proposed adding a new API for creating workqueue within
workqueue.c, rather than exposing the task and attaching it to cgroups in our driver: so that workqueue maintainers can fix the implementation if it ever changes.And after all, it's an internal API, we can always change it later if we need.
...
Quoted 2 lines
Well, yes but we are using APIs like
flush_worketc. These are very handy. It seems much easier than rolling our own queue on top of kthread.The thing is that this kind of one-off usage becomes problemetic when you're trying to change the implementation detail. All current workqueue users don't care which thread they run on and they shouldn't as each work owns the context only for the duration the work is executing. If this sort of fundamental guidelines are followed, the implementation can be improved in pretty much transparent way but when you start depending on specific implementation details, things become messy pretty quickly.
If this type of usage were more common, adding proper way to account work usage according to cgroups would make sense but that's not the case here and I removed the only exception case recently while trying to implement cmwq and if this is added. So, this would be the only one which makes such extra assumptions in the whole kernel. One way or the other, workqueue needs to be improved and I don't really think adding the single exception at this point is a good idea.
The thing I realized after
stop_machineconversion was that there was no reason to use workqueue there at all. There already are more than enough not-too-difficult synchronization constructs and if you're using a thread for dedicated purposes, code complexity isn't that different either way. Plus, it would also be clearer that dedicated threads are required there for what reason. So, I strongly suggest using a kthread. If there are issues which are noticeably difficult to solve with kthread, we can definitely talk about that and think about things again.
Quoted 1 line
Thank you.
Well, we have create_singlethread_workqueue, right? This is not very different ... is it?
Just copying structures and code from workqueue.c,
adding vhost_ in front of it will definitely work:
there is nothing magic about the workqueue library.
But this just involves cut and paste which might be best avoided. One final idea before we go the cut and paste way: how about 'create_workqueue_from_task' that would get a thread and have workqueue run there?
Quoted 2 lines
Signature · 1 line
tejun
Tejun Heo
maintainerHello,
Quoted 5 lines · Michael S. Tsirkin
Well, we have
create_singlethread_workqueue, right? This is not very different ... is it?Just copying structures and code from
workqueue.c, addingvhost_in front of it will definitely work:
Sure it will, but you'll probably be able to get away with much less.
Quoted 2 lines
there is nothing magic about the workqueue library.
But this just involves cut and paste which might be best avoided.
What I'm saying is that some magic needs to be added to workqueue and if you add this single(!) exception, it will have to be backed out pretty soon, so it would be better to do it properly now.
Quoted 3 lines
One final idea before we go the cut and paste way: how about '
create_workqueue_from_task' that would get a thread and have workqueue run there?
You can currently depend on that implementation detail but it's not the workqueue interface is meant to do. The single threadedness is there as execution ordering and concurrency specification and it doesn't (or rather won't) necessarily mean that a specific single thread is bound to certain workqueue.
Can you please direct me to have a look at the code. I'll be happy to do the conversion for you.
Thanks.
Signature · 1 line
tejun
Michael S. Tsirkin
maintainerQuoted 22 lines · Tejun Heo
Hello,
Quoted 5 lines · Michael S. Tsirkin
Well, we have
create_singlethread_workqueue, right? This is not very different ... is it?Just copying structures and code from
workqueue.c, addingvhost_in front of it will definitely work:Sure it will, but you'll probably be able to get away with much less.
Quoted 2 lines
there is nothing magic about the workqueue library.
But this just involves cut and paste which might be best avoided.What I'm saying is that some magic needs to be added to workqueue and if you add this single(!) exception, it will have to be backed out pretty soon, so it would be better to do it properly now.
Quoted 3 lines
One final idea before we go the cut and paste way: how about '
create_workqueue_from_task' that would get a thread and have workqueue run there?
Quoted 8 lines
You can currently depend on that implementation detail but it's not the workqueue interface is meant to do. The single threadedness is there as execution ordering and concurrency specification and it doesn't (or rather won't) necessarily mean that a specific single thread is bound to certain workqueue.
Can you please direct me to have a look at the code. I'll be happy to do the conversion for you.
Great, thanks! The code in question is in drivers/vhost/vhost.c It is used from drivers/vhost/net.c
On top of this, we have patchset from Sridhar Samudrala, titled '0/3 Make vhost multi-threaded and associate each thread to its guest's cgroup':
cgroups: Add an API to attach a task to current task's cgroup
workqueue: Add an API to create a singlethread workqueue attached to the current task's cgroup
vhost: make it more scalable by creating a vhost thread per device
I have bounced the last three your way.
Quoted 4 lines
Thanks.
Signature · 1 line
tejun
Tejun Heo
maintainerPATCH 1/3 vhost: replace vhost_workqueue with per-vhost kthread
Replace vhost_workqueue with per-vhost kthread. Other than callback argument change from struct work_struct * to struct vhost_poll *, there's no visible change to vhost_poll_*() interface.
This conversion is to make each vhost use a dedicated kthread so that resource control via cgroup can be applied.
Partially based on Sridhar Samudrala's patch.
---
Okay, here is three patch series to convert vhost to use per-vhost kthread, add cgroup_attach_task_current_cg() and apply it to the vhost kthreads. The conversion is mostly straight forward although flush is slightly tricky.
The problem is that I have no idea how to test this. It builds fine and I read it several times but it's entirely possible / likely that I missed something. Please proceed with caution (so, no sign off yet).
Thanks.
drivers/vhost/net.c | 58 +++++++++++++---------------- drivers/vhost/vhost.c | 99 ++++++++++++++++++++++++++++++++++++-------------- drivers/vhost/vhost.h | 32 +++++++++------- 3 files changed, 117 insertions(+), 72 deletions(-)
drivers/vhost/net.c +26 -32
@@ -294,54 +294,60 @@ static void handle_rx(struct vhost_net *unuse_mm (net->dev.mm); } -static void handle_tx_kick (struct work_struct *work) +static void handle_tx_kick (struct vhost_poll *poll) { -struct vhost_virtqueue *vq; -struct vhost_net *net; - vq = container_of(work, struct vhost_virtqueue, poll.work); - net = container_of(vq->dev, struct vhost_net, dev); +struct vhost_virtqueue *vq = + container_of(poll, struct vhost_virtqueue, poll); + struct vhost_net *net = container_of(vq->dev, struct vhost_net, dev); +handle_tx (net); } -static void handle_rx_kick (struct work_struct *work) +static void handle_rx_kick (struct vhost_poll *poll) { -struct vhost_virtqueue *vq; -struct vhost_net *net; - vq = container_of(work, struct vhost_virtqueue, poll.work); - net = container_of(vq->dev, struct vhost_net, dev); +struct vhost_virtqueue *vq = + container_of(poll, struct vhost_virtqueue, poll); + struct vhost_net *net = container_of(vq->dev, struct vhost_net, dev); +handle_rx (net); } -static void handle_tx_net (struct work_struct *work) +static void handle_tx_net (struct vhost_poll *poll) { -struct vhost_net *net; - net = container_of(work, struct vhost_net, poll[VHOST_NET_VQ_TX].work); +struct vhost_net *net = + container_of(poll, struct vhost_net, poll[VHOST_NET_VQ_TX]); +handle_tx (net); } -static void handle_rx_net (struct work_struct *work) +static void handle_rx_net (struct vhost_poll *poll) { -struct vhost_net *net; - net = container_of(work, struct vhost_net, poll[VHOST_NET_VQ_RX].work); +struct vhost_net *net = + container_of(poll, struct vhost_net, poll[VHOST_NET_VQ_RX]); +handle_rx (net); }static int vhost_net_open (struct inode *inode,struct file *f) {struct vhost_net *n =kmalloc (sizeof *n,GFP_KERNEL ); +struct vhost_dev *dev;int r; +if (!n)return -ENOMEM ; + + dev = &n->dev; n->vqs[VHOST_NET_VQ_TX ].handle_kick = handle_tx_kick; n->vqs[VHOST_NET_VQ_RX ].handle_kick = handle_rx_kick; - r =vhost_dev_init (&n->dev, n->vqs,VHOST_NET_VQ_MAX ); + r =vhost_dev_init (dev, n->vqs,VHOST_NET_VQ_MAX );if (r <0 ) {kfree (n);return r; } -vhost_poll_init (n->poll +VHOST_NET_VQ_TX , handle_tx_net,POLLOUT ); -vhost_poll_init (n->poll +VHOST_NET_VQ_RX , handle_rx_net,POLLIN ); +vhost_poll_init (n->poll +VHOST_NET_VQ_TX , handle_tx_net,POLLOUT , dev); +vhost_poll_init (n->poll +VHOST_NET_VQ_RX , handle_rx_net,POLLIN , dev); n->tx_poll_state =VHOST_NET_POLL_DISABLED ; f->private_data = n;
@@ -644,25 +650,13 @@ static struct miscdevice vhost_net_miscstatic int vhost_net_init (void ) { -int r =vhost_init (); -if (r) - goto err_init; - r =misc_register (&vhost_net_misc); -if (r) - goto err_reg; -return 0 ; -err_reg: -vhost_cleanup (); -err_init: -return r; - +return misc_register (&vhost_net_misc); }module_init (vhost_net_init);static void vhost_net_exit (void ) {misc_deregister (&vhost_net_misc); -vhost_cleanup (); }module_exit (vhost_net_exit);
drivers/vhost/vhost.c +72 -27
177 lines not on this page · raw message
drivers/vhost/vhost.h +19 -13
@@ -5,7 +5,6 @@#include <linux/vhost.h> #include <linux/mm.h> #include <linux/mutex.h> -#include <linux/workqueue.h> #include <linux/poll.h> #include <linux/file.h> #include <linux/skbuff.h>
@@ -20,19 +19,26 @@ enum { VHOST_NET_MAX_SG = MAX_SKB_FRAGS + 2, }; +struct vhost_poll ; +typedef void (*vhost_poll_fn_t )(struct vhost_poll *poll); +/* Poll a file (eventfd or socket) */ /* Note: there's nothing vhost specific about this structure. */ struct vhost_poll { +vhost_poll_fn_t fn;poll_table table;wait_queue_head_t *wqh;wait_queue_t wait; -/* struct which will handle all actual work. */ -struct work_struct work; +struct list_head node; +wait_queue_head_t done;unsigned long mask; +struct vhost_dev *dev; +int queue_seq; +int done_seq; }; -void vhost_poll_init (struct vhost_poll *poll,work_func_t func, -unsigned long mask); +void vhost_poll_init (struct vhost_poll *poll,vhost_poll_fn_t fn, +unsigned long mask,struct vhost_dev *dev);void vhost_poll_start (struct vhost_poll *poll,struct file *file);void vhost_poll_stop (struct vhost_poll *poll);void vhost_poll_flush (struct vhost_poll *poll);
@@ -63,7 +69,7 @@ struct vhost_virtqueue {struct vhost_poll poll;/* The routine to call when the Guest pings us, or timeout. */ -work_func_t handle_kick; +vhost_poll_fn_t handle_kick;/* Last available index we saw. */ u16 last_avail_idx;
@@ -86,11 +92,11 @@ struct vhost_virtqueue {struct iovec hdr[VHOST_NET_MAX_SG ];size_t hdr_size;/* We use a kind of RCU to access private pointer. -* All readers access it from workqueue, which makes it possible to -* flush the workqueue instead of synchronize_rcu. Therefore readers do +* All readers access it from poller, which makes it possible to +* flush the vhost_poll instead of synchronize_rcu. Therefore readers do * not need to call rcu_read_lock/rcu_read_unlock: the beginning of -* work item execution acts instead of rcu_read_lock() and the end of -* work item execution acts instead of rcu_read_lock(). +* vhost_poll execution acts instead of rcu_read_lock() and the end of +* vhost_poll execution acts instead of rcu_read_lock(). * Writers use virtqueue mutex. */ void *private_data;/* Log write descriptors */
@@ -110,6 +116,9 @@ struct vhost_dev {int nvqs;struct file *log_file;struct eventfd_ctx *log_ctx; +spinlock_t poller_lock; +struct list_head poll_list; +struct task_struct *poller; };long vhost_dev_init (struct vhost_dev *,struct vhost_virtqueue *vqs,int nvqs);
@@ -136,9 +145,6 @@ bool vhost_enable_notify(struct vhost_viint vhost_log_write (struct vhost_virtqueue *vq,struct vhost_log *log,unsigned int log_num,u64 len); -int vhost_init (void ); -void vhost_cleanup (void ); -#define vq_err (vq, fmt, ...) do { \ pr_debug(pr_fmt(fmt), ##__VA_ARGS__); \ if ((vq)->error_ctx) \
Re: [PATCH 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Quoted 3 lines · Tejun Heo
This conversion is to make each vhost use a dedicated kthread so that resource control via cgroup can be applied.
Personally, I agree. I think This is better than play with workqueue thread.
A couple of simple questions after the quick glance at the unapplied patch...
drivers/vhost/vhost.c void vhost_poll_stop(struct vhost_poll * · line 100
Quoted 7 lines
void
vhost_poll_flush(struct vhost_poll *poll)
{drivers/vhost/vhost.c +4
-1-flush_work (&poll->work); +int seq = poll->queue_seq; + +if (seq - poll->done_seq >0 ) +wait_event (poll->done, seq - poll->done_seq <=0 );
The check before wait_event() is not needed, please note that wait_event() checks the condition before __wait_event().
What I can't understand is why we do have ->queue_seq and ->done_seq.
Isn't the single "bool poll->active" enough? vhost_poll_queue() sets ->active == T, vhost_poller() clears it before wake_up_all(poll->done).
drivers/vhost/vhost.c static void vhost_vq_reset(struct vhost_ · line 148
Quoted 7 lines
drivers/vhost/vhost.c +7
-0+static int vhost_poller (void *data) +{ +struct vhost_dev *dev = data; +struct vhost_poll *poll; + +repeat: +set_current_state (TASK_INTERRUPTIBLE );/* mb paired w/ kthread_stop */
I don't understand the comment... why do we need this barrier?
Quoted 15 lines
drivers/vhost/vhost.c +15
-0+if (kthread_should_stop ()) { +__set_current_state (TASK_RUNNING ); +return 0 ; + } + + poll =NULL ; +spin_lock (&dev->poller_lock); +if (!list_empty (&dev->poll_list)) { + poll =list_first_entry (&dev->poll_list, + struct vhost_poll, node); +list_del_init (&poll->node); + } +spin_unlock (&dev->poller_lock); + +if (poll) {
drivers/vhost/vhost.c static void vhost_vq_reset(struct vhost_ · line 174
Quoted 10 lines
drivers/vhost/vhost.c +10
-0+__set_current_state (TASK_RUNNING ); + poll->fn (poll); +smp_wmb ();/* paired with rmb in vhost_poll_flush() */ + poll->done_seq = poll->queue_seq; +wake_up_all (&poll->done); + }else +schedule (); + + goto repeat; +}
Given that vhost_poll_queue() does list_add() and wake_up_process() under ->poller_lock, I don't think we need any barriers to change ->state.
IOW, can't vhost_poller() simply do
while(!kthread_should_stop()) {
poll = NULL;
spin_lock(&dev->poller_lock);
if (!list_empty(&dev->poll_list)) {
...
} else
__set_current_state(TASK_INTERRUPTIBLE);
spin_unlock(&dev->poller_lock);
if (poll) {
...
} else
schedule();
}?
Oleg.
Tejun Heo
maintainerRe: [PATCH 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Hello,
Quoted 6 lines · Oleg Nesterov
Quoted 3 lines · Tejun Heo
This conversion is to make each vhost use a dedicated kthread so that resource control via cgroup can be applied.
Personally, I agree. I think This is better than play with workqueue thread.
Yeap, I think so too. In vhost's case tho, as it exports a lot of workqueue characteristics to vhost users, it's a bit more complex than I wish it were. It can probably be simplified more if someone who knows the code better takes a look or maybe we need to make this kind of things easier by providing a generic helpers if more cases like this spring up, but if that happens probably the RTTD would be somehow teaching workqueue how to deal with cgroups. As this is the first case, I guess open coding is okay for now.
Quoted 12 lines
A couple of simple questions after the quick glance at the unapplied patch...
drivers/vhost/vhost.c void vhost_poll_stop(struct vhost_poll * · line 100
Quoted 7 lines
void
vhost_poll_flush(struct vhost_poll *poll)
{drivers/vhost/vhost.c +4
-1-flush_work (&poll->work); +int seq = poll->queue_seq; + +if (seq - poll->done_seq >0 ) +wait_event (poll->done, seq - poll->done_seq <=0 );The check before
wait_event()is not needed, please note thatwait_event()checks the condition before__wait_event().
Heh... right, I was looking at __wait_event() and thinking "ooh... we can skip lock in the fast path". :-)
Quoted 4 lines
What I can't understand is why we do have ->queue_seq and ->done_seq.
Isn't the single "bool
poll->active" enough?vhost_poll_queue()sets ->active == T,vhost_poller()clears it beforewake_up_all(poll->done).
I might have slightly over engineered this part not knowing the expected workload. ->queue_seq/->done_seq pair is to guarantee that flushers never get starved. Without sequencing queueings and executions, flushers should wait for !pending && !active which can take some time to come if the poll in question is very busy.
Quoted 9 lines
drivers/vhost/vhost.c static void vhost_vq_reset(struct vhost_ · line 148
Quoted 7 lines
drivers/vhost/vhost.c +7
-0+static int vhost_poller (void *data) +{ +struct vhost_dev *dev = data; +struct vhost_poll *poll; + +repeat: +set_current_state (TASK_INTERRUPTIBLE );/* mb paired w/ kthread_stop */ I don't understand the comment... why do we need this barrier?
So that either kthread_stop()'s should_stop = 1 in kthread_stop() is visible to kthread_should_stop() or task state is set to RUNNING.
Quoted 46 lines
Quoted 15 lines
drivers/vhost/vhost.c +15
-0+if (kthread_should_stop ()) { +__set_current_state (TASK_RUNNING ); +return 0 ; + } + + poll =NULL ; +spin_lock (&dev->poller_lock); +if (!list_empty (&dev->poll_list)) { + poll =list_first_entry (&dev->poll_list, + struct vhost_poll, node); +list_del_init (&poll->node); + } +spin_unlock (&dev->poller_lock); + +if (poll) {drivers/vhost/vhost.c static void vhost_vq_reset(struct vhost_ · line 174
Quoted 10 lines
drivers/vhost/vhost.c +10
-0+__set_current_state (TASK_RUNNING ); + poll->fn (poll); +smp_wmb ();/* paired with rmb in vhost_poll_flush() */ + poll->done_seq = poll->queue_seq; +wake_up_all (&poll->done); + }else +schedule (); + + goto repeat; +}Given that
vhost_poll_queue()doeslist_add()andwake_up_process()under ->poller_lock, I don't think we need any barriers to change ->state.IOW, can't
vhost_poller()simply dowhile(!kthread_should_stop()) { poll = NULL; spin_lock(&dev->poller_lock); if (!list_empty(&dev->poll_list)) { ... } else __set_current_state(TASK_INTERRUPTIBLE); spin_unlock(&dev->poller_lock); if (poll) { ... } else schedule(); }
Quoted 1 line
?
But then kthread_stop() can happen between kthread_should_stop() and __set_current_state(TASK_INTERRUPTIBLE) and poller can just sleep in schedule() not knowing that.
Thank you.
Signature · 1 line
tejun
Re: [PATCH 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Quoted 11 lines · Tejun Heo
Quoted 4 lines · Oleg Nesterov
What I can't understand is why we do have ->queue_seq and ->done_seq.
Isn't the single "bool
poll->active" enough?vhost_poll_queue()sets ->active == T,vhost_poller()clears it beforewake_up_all(poll->done).I might have slightly over engineered this part not knowing the expected workload. ->queue_seq/->done_seq pair is to guarantee that flushers never get starved.
Ah, indeed.
Well, afaics we do not need 2 counters anyway, both vhost_poll_queue() and vhost_poller() could increment the single counter and the flusher can take bit 0 into account. But I agree 2 counters are much more clean.
Quoted 10 lines
Quoted 9 lines
drivers/vhost/vhost.c static void vhost_vq_reset(struct vhost_ · line 148
Quoted 7 lines
drivers/vhost/vhost.c +7
-0+static int vhost_poller (void *data) +{ +struct vhost_dev *dev = data; +struct vhost_poll *poll; + +repeat: +set_current_state (TASK_INTERRUPTIBLE );/* mb paired w/ kthread_stop */ I don't understand the comment... why do we need this barrier?
Quoted 2 lines
So that either kthread_stop()'s
should_stop= 1 inkthread_stop()is visible tokthread_should_stop()or task state is set to RUNNING.
Of course, you are right. I am really surprized I asked this question ;)
Thanks,
Oleg.
Tejun Heo
maintainerRe: [PATCH 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Hello,
Quoted 9 lines · Oleg Nesterov
Quoted 3 lines
I might have slightly over engineered this part not knowing the expected workload. ->queue_seq/->done_seq pair is to guarantee that flushers never get starved.
Ah, indeed.
Well, afaics we do not need 2 counters anyway, both
vhost_poll_queue()andvhost_poller()could increment the single counter and the flusher can take bit 0 into account. But I agree 2 counters are much more clean.
Right, we can do that too. Cool. :-)
Quoted 10 lines
Quoted 10 lines
Quoted 9 lines
drivers/vhost/vhost.c static void vhost_vq_reset(struct vhost_ · line 148
Quoted 7 lines
drivers/vhost/vhost.c +7
-0+static int vhost_poller (void *data) +{ +struct vhost_dev *dev = data; +struct vhost_poll *poll; + +repeat: +set_current_state (TASK_INTERRUPTIBLE );/* mb paired w/ kthread_stop */ I don't understand the comment... why do we need this barrier?
Quoted 4 lines
Quoted 2 lines
So that either kthread_stop()'s
should_stop= 1 inkthread_stop()is visible tokthread_should_stop()or task state is set to RUNNING.Of course, you are right. I am really surprized I asked this question ;)
This part is always a bit tricky tho. Maybe it would be a good idea to make kthread_stop() do periodic wakeups. It's already injecting one rather unexpected wake up into the mix anyway so there isn't much point in avoiding multiple and it would make designing kthread loops easier. Or maybe what we need is something like kthread_idle() which encapsulates the check and sleep part.
Thanks.
Signature · 1 line
tejun
Michael S. Tsirkin
maintainerRe: [PATCH 1/3] vhost: replace vhost_workqueue with per-vhost kthread
In reply to Tejun HeoQuoted 3 lines · Tejun Heo
Replace
vhost_workqueuewith per-vhost kthread. Other than callback argument change from structwork_struct* to structvhost_poll*, there's no visible change to vhost_poll_*() interface.
I would prefer a substructure vhost_work, even just to make the code easier to review and compare to workqueue.c.
Quoted 7 lines
This conversion is to make each vhost use a dedicated kthread so that resource control via cgroup can be applied.
Partially based on Sridhar Samudrala's patch.
Cc Michael S. Tsirkin <[email protected]>Cc Sridhar Samudrala <[email protected]>
Quoted 7 lines
---
Okay, here is three patch series to convert vhost to use per-vhost kthread, addcgroup_attach_task_current_cg()and apply it to the vhost kthreads. The conversion is mostly straight forward although flush is slightly tricky.The problem is that I have no idea how to test this.
It's a 3 step process:
1.
Install qemu-kvm under fc13, or build recent one from source, get it from here:git://git.kernel.org/pub/scm/virt/kvm/qemu-kvm.git
2. install guest under it:
qemu-img create -f qcow2 disk.qcow2 100G
Now get some image (e.g. fedora 13 in fc13.iso)
and install guest:
qemu-kvm -enable-kvm -m 1G -cdrom fc13.iso -drive file=disk.qcow2
3. set up networking. I usually simply do host to guest on a special subnet for testing purposes:
Set up a bridge named mstbr0:
ifconfig mstbr0 down
brctl delbr mstbr0
brctl addbr mstbr0
brctl setfd mstbr0 0
ifconfig mstbr0 11.0.0.1
cat > ifup << EOF
#!/bin/sh -x/sbin/ifconfig msttap0 0.0.0.0 up
brctl addif mstbr0 msttap0
EOF
qemu-kvm -enable-kvm -m 1G -cdrom fc13.iso -drive file=disk.qcow2
-net nic,model=virtio,netdev=foo -netdev
tap,id=foo,ifname=msttap0,script=/home/mst/ifup,downscript=no,vhost=on
after you set up the guest, log into it and
ifconfig eth0 11.0.0.2
You should now be able to ping guest to host and back. Use something like netperf to stress the connection. Close qemu with kill -9 and unload module to test flushing code.
Quoted 4 lines
Index:
work/drivers/vhost/vhost.c
===================================================================drivers/vhost/vhost.c +0
-0
...
Quoted 22 lines
drivers/vhost/vhost.c +18
-0@@ -125,10 +139,50 @@ static void vhost_vq_reset(struct vhost_ vq->log_ctx =NULL ; } +static int vhost_poller (void *data) +{ +struct vhost_dev *dev = data; +struct vhost_poll *poll; + +repeat: +set_current_state (TASK_INTERRUPTIBLE );/* mb paired w/ kthread_stop */ + +if (kthread_should_stop ()) { +__set_current_state (TASK_RUNNING ); +return 0 ; + } + + poll =NULL ; +spin_lock (&dev->poller_lock); +if (!list_empty (&dev->poll_list)) { + poll =list_first_entry (&dev->poll_list, + struct vhost_poll, node);
drivers/vhost/vhost.c static void vhost_vq_reset(struct vhost_ · line 169
Quoted 10 lines
drivers/vhost/vhost.c +10
-0+list_del_init (&poll->node); + } +spin_unlock (&dev->poller_lock); + +if (poll) { +__set_current_state (TASK_RUNNING ); + poll->fn (poll); +smp_wmb ();/* paired with rmb in vhost_poll_flush() */ + poll->done_seq = poll->queue_seq; +wake_up_all (&poll->done);
This seems to add wakeups on data path, which uses spinlocks etc. OTOH workqueue.c adds a special barrier
entry which only does a wakeup when needed.
Right?
Quoted 54 lines
drivers/vhost/vhost.c +18
-4+ }else +schedule (); + + goto repeat; +} +long vhost_dev_init (struct vhost_dev *dev,struct vhost_virtqueue *vqs,int nvqs) { +struct task_struct *poller;int i; + + poller =kthread_create (vhost_poller, dev,"vhost-%d" , current->pid); +if (IS_ERR (poller)) +return PTR_ERR (poller); + dev->vqs = vqs; dev->nvqs = nvqs;mutex_init (&dev->mutex);@@ -136,6 +190,9 @@ long vhost_dev_init(struct vhost_dev *de dev->log_file =NULL ; dev->memory =NULL ; dev->mm =NULL ; +spin_lock_init (&dev->poller_lock); +INIT_LIST_HEAD (&dev->poll_list); + dev->poller = poller;for (i =0 ; i < dev->nvqs; ++i) { dev->vqs[i].dev = dev;@@ -143,8 +200,7 @@ long vhost_dev_init(struct vhost_dev *devhost_vq_reset (dev, dev->vqs + i);if (dev->vqs[i].handle_kick)vhost_poll_init (&dev->vqs[i].poll, - dev->vqs[i].handle_kick, -POLLIN ); + dev->vqs[i].handle_kick,POLLIN , dev); }return 0 ; }@@ -217,6 +273,8 @@ void vhost_dev_cleanup(struct vhost_devif (dev->mm)mmput (dev->mm); dev->mm =NULL ; + +kthread_stop (dev->poller); }static int log_access_ok (void __user *log_base,u64 addr,unsigned long sz)@@ -1113,16 +1171,3 @@ void vhost_disable_notify(struct vhost_vvq_err (vq,"Failed to enable notification at %p: %d\n" , &vq->used->flags, r); } - -int vhost_init (void )
drivers/vhost/vhost.c void vhost_disable_notify(struct vhost_v · line 1127
Quoted 10 lines
drivers/vhost/vhost.c +0
-10-{ - vhost_workqueue =create_singlethread_workqueue ("vhost" ); -if (!vhost_workqueue) -return -ENOMEM ; -return 0 ; -} - -void vhost_cleanup (void ) -{ -destroy_workqueue (vhost_workqueue);
I note that destroy_workqueue does a flush, kthread_stop doesn't. Right? Sure we don't need to check nothing is in one of the lists? Maybe add a BUG_ON?
Quoted 89 lines
drivers/vhost/vhost.c +0
-1-}Index:
work/drivers/vhost/vhost.h
===================================================================drivers/vhost/vhost.h +19
-13@@ -5,7 +5,6 @@#include <linux/vhost.h> #include <linux/mm.h> #include <linux/mutex.h> -#include <linux/workqueue.h> #include <linux/poll.h> #include <linux/file.h> #include <linux/skbuff.h> @@ -20,19 +19,26 @@ enum { VHOST_NET_MAX_SG = MAX_SKB_FRAGS + 2, }; +struct vhost_poll ; +typedef void (*vhost_poll_fn_t )(struct vhost_poll *poll); +/* Poll a file (eventfd or socket) */ /* Note: there's nothing vhost specific about this structure. */ struct vhost_poll { +vhost_poll_fn_t fn;poll_table table;wait_queue_head_t *wqh;wait_queue_t wait; -/* struct which will handle all actual work. */ -struct work_struct work; +struct list_head node; +wait_queue_head_t done;unsigned long mask; +struct vhost_dev *dev; +int queue_seq; +int done_seq; }; -void vhost_poll_init (struct vhost_poll *poll,work_func_t func, -unsigned long mask); +void vhost_poll_init (struct vhost_poll *poll,vhost_poll_fn_t fn, +unsigned long mask,struct vhost_dev *dev);void vhost_poll_start (struct vhost_poll *poll,struct file *file);void vhost_poll_stop (struct vhost_poll *poll);void vhost_poll_flush (struct vhost_poll *poll);@@ -63,7 +69,7 @@ struct vhost_virtqueue {struct vhost_poll poll;/* The routine to call when the Guest pings us, or timeout. */ -work_func_t handle_kick; +vhost_poll_fn_t handle_kick;/* Last available index we saw. */ u16 last_avail_idx;@@ -86,11 +92,11 @@ struct vhost_virtqueue {struct iovec hdr[VHOST_NET_MAX_SG ];size_t hdr_size;/* We use a kind of RCU to access private pointer. -* All readers access it from workqueue, which makes it possible to -* flush the workqueue instead of synchronize_rcu. Therefore readers do +* All readers access it from poller, which makes it possible to +* flush the vhost_poll instead of synchronize_rcu. Therefore readers do * not need to call rcu_read_lock/rcu_read_unlock: the beginning of -* work item execution acts instead of rcu_read_lock() and the end of -* work item execution acts instead of rcu_read_lock(). +* vhost_poll execution acts instead of rcu_read_lock() and the end of +* vhost_poll execution acts instead of rcu_read_lock(). * Writers use virtqueue mutex. */ void *private_data;/* Log write descriptors */ @@ -110,6 +116,9 @@ struct vhost_dev {int nvqs;struct file *log_file;struct eventfd_ctx *log_ctx; +spinlock_t poller_lock; +struct list_head poll_list; +struct task_struct *poller; };long vhost_dev_init (struct vhost_dev *,struct vhost_virtqueue *vqs,int nvqs);@@ -136,9 +145,6 @@ bool vhost_enable_notify(struct vhost_viint vhost_log_write (struct vhost_virtqueue *vq,struct vhost_log *log,unsigned int log_num,u64 len); -int vhost_init (void ); -void vhost_cleanup (void ); -#define vq_err (vq, fmt, ...) do { \ pr_debug(pr_fmt(fmt), ##__VA_ARGS__); \ if ((vq)->error_ctx) \
Tejun Heo
maintainerRe: [PATCH 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Hello,
Quoted 7 lines · Michael S. Tsirkin
Quoted 3 lines · Tejun Heo
Replace
vhost_workqueuewith per-vhost kthread. Other than callback argument change from structwork_struct* to structvhost_poll*, there's no visible change to vhost_poll_*() interface.I would prefer a substructure
vhost_work, even just to make the code easier to review and compare toworkqueue.c.
Yeap, sure.
Quoted 3 lines
Quoted 1 line
The problem is that I have no idea how to test this.
It's a 3 step process:
...
Quoted 3 lines
You should now be able to ping guest to host and back. Use something like netperf to stress the connection. Close qemu with kill -9 and unload module to test flushing code.
Thanks for the instruction. I'll see if there's a way to do it without building qemu myself on opensuse. But please feel free to go ahead and test it. It might just work! :-)
Quoted 11 lines
drivers/vhost/vhost.c static void vhost_vq_reset(struct vhost_ · line 169
Quoted 6 lines
drivers/vhost/vhost.c +6
-0+if (poll) { +__set_current_state (TASK_RUNNING ); + poll->fn (poll); +smp_wmb ();/* paired with rmb in vhost_poll_flush() */ + poll->done_seq = poll->queue_seq; +wake_up_all (&poll->done);This seems to add wakeups on data path, which uses spinlocks etc. OTOH
workqueue.cadds a special barrier entry which only does a wakeup when needed. Right?
Yeah, well, if it's a really hot path sure we can avoid wake_up_all() in most cases. Do you think that would be necessary?
Quoted 7 lines
drivers/vhost/vhost.c void vhost_disable_notify(struct vhost_v · line 1127
Quoted 3 lines
drivers/vhost/vhost.c +0
-3-void vhost_cleanup (void ) -{ -destroy_workqueue (vhost_workqueue);I note that
destroy_workqueuedoes a flush,kthread_stopdoesn't. Right? Sure we don't need to check nothing is in one of the lists? Maybe add aBUG_ON?
There were a bunch of flushes before kthread_stop() and they seemed to stop and flush everything. Aren't they enough? We can definitely add BUG_ON() after kthread_should_stop() check succeeds either way tho.
Thanks.
Signature · 1 line
tejun
Michael S. Tsirkin
maintainerRe: [PATCH 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Quoted 15 lines · Tejun Heo
Hello,
Quoted 7 lines · Michael S. Tsirkin
Quoted 3 lines · Tejun Heo
Replace
vhost_workqueuewith per-vhost kthread. Other than callback argument change from structwork_struct* to structvhost_poll*, there's no visible change to vhost_poll_*() interface.I would prefer a substructure
vhost_work, even just to make the code easier to review and compare toworkqueue.c.Yeap, sure.
Quoted 2 lines
Quoted 1 line
The problem is that I have no idea how to test this.
Quoted 8 lines
Quoted 1 line
It's a 3 step process:
...
Quoted 3 lines
You should now be able to ping guest to host and back. Use something like netperf to stress the connection. Close qemu with kill -9 and unload module to test flushing code.
Thanks for the instruction. I'll see if there's a way to do it without building qemu myself on opensuse.
My guess is no, there was no stable qemu release with vhost net support yet. Building it is mostly configure/make/make install,
as far as I remember you only need devel versions of X libraries, SDL and curses installed.
Quoted 11 lines
But please feel free to go
ahead and test it. It might just work! :-)Quoted 7 lines
Quoted 6 lines
drivers/vhost/vhost.c +6
-0+if (poll) { +__set_current_state (TASK_RUNNING ); + poll->fn (poll); +smp_wmb ();/* paired with rmb in vhost_poll_flush() */ + poll->done_seq = poll->queue_seq; +wake_up_all (&poll->done);
Quoted 6 lines
Quoted 3 lines
This seems to add wakeups on data path, which uses spinlocks etc. OTOH
workqueue.cadds a special barrier entry which only does a wakeup when needed. Right?Yeah, well, if it's a really hot path sure we can avoid
wake_up_all()in most cases. Do you think that would be necessary?
My guess is yes. This is really very hot path in code, and we are close to 100% CPU in some benchmarks.
Quoted 10 lines
Quoted 7 lines
drivers/vhost/vhost.c void vhost_disable_notify(struct vhost_v · line 1127
Quoted 3 lines
drivers/vhost/vhost.c +0
-3-void vhost_cleanup (void ) -{ -destroy_workqueue (vhost_workqueue);I note that
destroy_workqueuedoes a flush,kthread_stopdoesn't. Right? Sure we don't need to check nothing is in one of the lists? Maybe add aBUG_ON?There were a bunch of flushes before
kthread_stop()and they seemed to stop and flush everything. Aren't they enough?
I was just asking, I'll need to review the code in depth.
Quoted 7 lines
We can definitely add
BUG_ON()afterkthread_should_stop()check succeeds either way tho.Thanks.
Signature · 1 line
tejun
Tejun Heo
maintainerPATCH 1/3 vhost: replace vhost_workqueue with per-vhost kthread
Replace vhost_workqueue with per-vhost kthread. Other than callback argument change from struct work_struct * to struct vhost_work *, there's no visible change to vhost_poll_*() interface.
This conversion is to make each vhost use a dedicated kthread so that resource control via cgroup can be applied.
Partially based on Sridhar Samudrala's patch.
* Updated to use sub structure vhost_work instead of directly using vhost_poll at Michael's suggestion.
* Added flusher wake_up() optimization at Michael's suggestion.
NOT_SIGNED_OFF_YET
Cc: Michael S. Tsirkin <[email protected]>
Cc: Sridhar Samudrala <[email protected]>
---
Okay, here's the updated version. I'm still in the process of setting up the testing environment. I'll be traveling from this afternoon till tomorrow so I don't think I'll be able to test it before that. If you can give it a shot, it would be great.
Thanks.
drivers/vhost/net.c | 56 ++++++++++--------------- drivers/vhost/vhost.c | 110 ++++++++++++++++++++++++++++++++++++++------------ drivers/vhost/vhost.h | 38 +++++++++++------ 3 files changed, 133 insertions(+), 71 deletions(-)
drivers/vhost/net.c +24 -32
@@ -294,54 +294,58 @@ static void handle_rx(struct vhost_net *unuse_mm (net->dev.mm); } -static void handle_tx_kick (struct work_struct *work) +static void handle_tx_kick (struct vhost_work *work) { -struct vhost_virtqueue *vq; -struct vhost_net *net; - vq = container_of(work, struct vhost_virtqueue, poll.work); - net = container_of(vq->dev, struct vhost_net, dev); + struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue, + poll.work); + struct vhost_net *net = container_of(vq->dev, struct vhost_net, dev); +handle_tx (net); } -static void handle_rx_kick (struct work_struct *work) +static void handle_rx_kick (struct vhost_work *work) { -struct vhost_virtqueue *vq; -struct vhost_net *net; - vq = container_of(work, struct vhost_virtqueue, poll.work); - net = container_of(vq->dev, struct vhost_net, dev); + struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue, + poll.work); + struct vhost_net *net = container_of(vq->dev, struct vhost_net, dev); +handle_rx (net); } -static void handle_tx_net (struct work_struct *work) +static void handle_tx_net (struct vhost_work *work) { -struct vhost_net *net; - net = container_of(work, struct vhost_net, poll[VHOST_NET_VQ_TX].work); + struct vhost_net *net = container_of(work, struct vhost_net, + poll[VHOST_NET_VQ_TX ].work);handle_tx (net); } -static void handle_rx_net (struct work_struct *work) +static void handle_rx_net (struct vhost_work *work) { -struct vhost_net *net; - net = container_of(work, struct vhost_net, poll[VHOST_NET_VQ_RX].work); + struct vhost_net *net = container_of(work, struct vhost_net, + poll[VHOST_NET_VQ_RX ].work);handle_rx (net); }static int vhost_net_open (struct inode *inode,struct file *f) {struct vhost_net *n =kmalloc (sizeof *n,GFP_KERNEL ); +struct vhost_dev *dev;int r; +if (!n)return -ENOMEM ; + + dev = &n->dev; n->vqs[VHOST_NET_VQ_TX ].handle_kick = handle_tx_kick; n->vqs[VHOST_NET_VQ_RX ].handle_kick = handle_rx_kick; - r =vhost_dev_init (&n->dev, n->vqs,VHOST_NET_VQ_MAX ); + r =vhost_dev_init (dev, n->vqs,VHOST_NET_VQ_MAX );if (r <0 ) {kfree (n);return r; } -vhost_poll_init (n->poll +VHOST_NET_VQ_TX , handle_tx_net,POLLOUT ); -vhost_poll_init (n->poll +VHOST_NET_VQ_RX , handle_rx_net,POLLIN ); +vhost_poll_init (n->poll +VHOST_NET_VQ_TX , handle_tx_net,POLLOUT , dev); +vhost_poll_init (n->poll +VHOST_NET_VQ_RX , handle_rx_net,POLLIN , dev); n->tx_poll_state =VHOST_NET_POLL_DISABLED ; f->private_data = n;
@@ -644,25 +648,13 @@ static struct miscdevice vhost_net_miscstatic int vhost_net_init (void ) { -int r =vhost_init (); -if (r) - goto err_init; - r =misc_register (&vhost_net_misc); -if (r) - goto err_reg; -return 0 ; -err_reg: -vhost_cleanup (); -err_init: -return r; - +return misc_register (&vhost_net_misc); }module_init (vhost_net_init);static void vhost_net_exit (void ) {misc_deregister (&vhost_net_misc); -vhost_cleanup (); }module_exit (vhost_net_exit);
drivers/vhost/vhost.c +84 -26
186 lines not on this page · raw message
drivers/vhost/vhost.h +25 -13
@@ -5,13 +5,13 @@#include <linux/vhost.h> #include <linux/mm.h> #include <linux/mutex.h> -#include <linux/workqueue.h> #include <linux/poll.h> #include <linux/file.h> #include <linux/skbuff.h> #include <linux/uio.h> #include <linux/virtio_config.h> #include <linux/virtio_ring.h> +#include <asm/atomic.h> struct vhost_device ;
@@ -20,19 +20,31 @@ enum { VHOST_NET_MAX_SG = MAX_SKB_FRAGS + 2, }; +struct vhost_work ; +typedef void (*vhost_work_fn_t )(struct vhost_work *work); + +struct vhost_work { +struct list_head node; +vhost_work_fn_t fn; +wait_queue_head_t done; +atomic_t flushing; +int queue_seq; +int done_seq; +}; +/* Poll a file (eventfd or socket) */ /* Note: there's nothing vhost specific about this structure. */ struct vhost_poll {poll_table table;wait_queue_head_t *wqh;wait_queue_t wait; -/* struct which will handle all actual work. */ -struct work_struct work; +struct vhost_work work;unsigned long mask; +struct vhost_dev *dev; }; -void vhost_poll_init (struct vhost_poll *poll,work_func_t func, -unsigned long mask); +void vhost_poll_init (struct vhost_poll *poll,vhost_work_fn_t fn, +unsigned long mask,struct vhost_dev *dev);void vhost_poll_start (struct vhost_poll *poll,struct file *file);void vhost_poll_stop (struct vhost_poll *poll);void vhost_poll_flush (struct vhost_poll *poll);
@@ -63,7 +75,7 @@ struct vhost_virtqueue {struct vhost_poll poll;/* The routine to call when the Guest pings us, or timeout. */ -work_func_t handle_kick; +vhost_work_fn_t handle_kick;/* Last available index we saw. */ u16 last_avail_idx;
@@ -86,11 +98,11 @@ struct vhost_virtqueue {struct iovec hdr[VHOST_NET_MAX_SG ];size_t hdr_size;/* We use a kind of RCU to access private pointer. -* All readers access it from workqueue, which makes it possible to -* flush the workqueue instead of synchronize_rcu. Therefore readers do +* All readers access it from worker, which makes it possible to +* flush the vhost_work instead of synchronize_rcu. Therefore readers do * not need to call rcu_read_lock/rcu_read_unlock: the beginning of -* work item execution acts instead of rcu_read_lock() and the end of -* work item execution acts instead of rcu_read_lock(). +* vhost_work execution acts instead of rcu_read_lock() and the end of +* vhost_work execution acts instead of rcu_read_lock(). * Writers use virtqueue mutex. */ void *private_data;/* Log write descriptors */
@@ -110,6 +122,9 @@ struct vhost_dev {int nvqs;struct file *log_file;struct eventfd_ctx *log_ctx; +spinlock_t work_lock; +struct list_head work_list; +struct task_struct *worker; };long vhost_dev_init (struct vhost_dev *,struct vhost_virtqueue *vqs,int nvqs);
@@ -136,9 +151,6 @@ bool vhost_enable_notify(struct vhost_viint vhost_log_write (struct vhost_virtqueue *vq,struct vhost_log *log,unsigned int log_num,u64 len); -int vhost_init (void ); -void vhost_cleanup (void ); -#define vq_err (vq, fmt, ...) do { \ pr_debug(pr_fmt(fmt), ##__VA_ARGS__); \ if ((vq)->error_ctx) \
Tejun Heo
maintainerPATCH 1/3 vhost: replace vhost_workqueue with per-vhost kthread
Replace vhost_workqueue with per-vhost kthread. Other than callback argument change from struct work_struct * to struct vhost_work *, there's no visible change to vhost_poll_*() interface.
This conversion is to make each vhost use a dedicated kthread so that resource control via cgroup can be applied.
Partially based on Sridhar Samudrala's patch.
* Updated to use sub structure vhost_work instead of directly using vhost_poll at Michael's suggestion.
* Added flusher wake_up() optimization at Michael's suggestion.
---
Okay, just tested it. dev->work_lock had to be updated to use irq operations but other than that it worked just fine. Copied a large file using scp and it seems to perform pretty well although I don't have any reference of comparison. So, here's the updated version with the sign off.
Thanks.
drivers/vhost/net.c | 56 ++++++++++--------------- drivers/vhost/vhost.c | 111 ++++++++++++++++++++++++++++++++++++++------------ drivers/vhost/vhost.h | 38 +++++++++++------ 3 files changed, 134 insertions(+), 71 deletions(-)
drivers/vhost/net.c +24 -32
@@ -294,54 +294,58 @@ static void handle_rx(struct vhost_net *unuse_mm (net->dev.mm); } -static void handle_tx_kick (struct work_struct *work) +static void handle_tx_kick (struct vhost_work *work) { -struct vhost_virtqueue *vq; -struct vhost_net *net; - vq = container_of(work, struct vhost_virtqueue, poll.work); - net = container_of(vq->dev, struct vhost_net, dev); + struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue, + poll.work); + struct vhost_net *net = container_of(vq->dev, struct vhost_net, dev); +handle_tx (net); } -static void handle_rx_kick (struct work_struct *work) +static void handle_rx_kick (struct vhost_work *work) { -struct vhost_virtqueue *vq; -struct vhost_net *net; - vq = container_of(work, struct vhost_virtqueue, poll.work); - net = container_of(vq->dev, struct vhost_net, dev); + struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue, + poll.work); + struct vhost_net *net = container_of(vq->dev, struct vhost_net, dev); +handle_rx (net); } -static void handle_tx_net (struct work_struct *work) +static void handle_tx_net (struct vhost_work *work) { -struct vhost_net *net; - net = container_of(work, struct vhost_net, poll[VHOST_NET_VQ_TX].work); + struct vhost_net *net = container_of(work, struct vhost_net, + poll[VHOST_NET_VQ_TX ].work);handle_tx (net); } -static void handle_rx_net (struct work_struct *work) +static void handle_rx_net (struct vhost_work *work) { -struct vhost_net *net; - net = container_of(work, struct vhost_net, poll[VHOST_NET_VQ_RX].work); + struct vhost_net *net = container_of(work, struct vhost_net, + poll[VHOST_NET_VQ_RX ].work);handle_rx (net); }static int vhost_net_open (struct inode *inode,struct file *f) {struct vhost_net *n =kmalloc (sizeof *n,GFP_KERNEL ); +struct vhost_dev *dev;int r; +if (!n)return -ENOMEM ; + + dev = &n->dev; n->vqs[VHOST_NET_VQ_TX ].handle_kick = handle_tx_kick; n->vqs[VHOST_NET_VQ_RX ].handle_kick = handle_rx_kick; - r =vhost_dev_init (&n->dev, n->vqs,VHOST_NET_VQ_MAX ); + r =vhost_dev_init (dev, n->vqs,VHOST_NET_VQ_MAX );if (r <0 ) {kfree (n);return r; } -vhost_poll_init (n->poll +VHOST_NET_VQ_TX , handle_tx_net,POLLOUT ); -vhost_poll_init (n->poll +VHOST_NET_VQ_RX , handle_rx_net,POLLIN ); +vhost_poll_init (n->poll +VHOST_NET_VQ_TX , handle_tx_net,POLLOUT , dev); +vhost_poll_init (n->poll +VHOST_NET_VQ_RX , handle_rx_net,POLLIN , dev); n->tx_poll_state =VHOST_NET_POLL_DISABLED ; f->private_data = n;
@@ -644,25 +648,13 @@ static struct miscdevice vhost_net_miscstatic int vhost_net_init (void ) { -int r =vhost_init (); -if (r) - goto err_init; - r =misc_register (&vhost_net_misc); -if (r) - goto err_reg; -return 0 ; -err_reg: -vhost_cleanup (); -err_init: -return r; - +return misc_register (&vhost_net_misc); }module_init (vhost_net_init);static void vhost_net_exit (void ) {misc_deregister (&vhost_net_misc); -vhost_cleanup (); }module_exit (vhost_net_exit);
drivers/vhost/vhost.c +85 -26
187 lines not on this page · raw message
drivers/vhost/vhost.h +25 -13
@@ -5,13 +5,13 @@#include <linux/vhost.h> #include <linux/mm.h> #include <linux/mutex.h> -#include <linux/workqueue.h> #include <linux/poll.h> #include <linux/file.h> #include <linux/skbuff.h> #include <linux/uio.h> #include <linux/virtio_config.h> #include <linux/virtio_ring.h> +#include <asm/atomic.h> struct vhost_device ;
@@ -20,19 +20,31 @@ enum { VHOST_NET_MAX_SG = MAX_SKB_FRAGS + 2, }; +struct vhost_work ; +typedef void (*vhost_work_fn_t )(struct vhost_work *work); + +struct vhost_work { +struct list_head node; +vhost_work_fn_t fn; +wait_queue_head_t done; +atomic_t flushing; +int queue_seq; +int done_seq; +}; +/* Poll a file (eventfd or socket) */ /* Note: there's nothing vhost specific about this structure. */ struct vhost_poll {poll_table table;wait_queue_head_t *wqh;wait_queue_t wait; -/* struct which will handle all actual work. */ -struct work_struct work; +struct vhost_work work;unsigned long mask; +struct vhost_dev *dev; }; -void vhost_poll_init (struct vhost_poll *poll,work_func_t func, -unsigned long mask); +void vhost_poll_init (struct vhost_poll *poll,vhost_work_fn_t fn, +unsigned long mask,struct vhost_dev *dev);void vhost_poll_start (struct vhost_poll *poll,struct file *file);void vhost_poll_stop (struct vhost_poll *poll);void vhost_poll_flush (struct vhost_poll *poll);
@@ -63,7 +75,7 @@ struct vhost_virtqueue {struct vhost_poll poll;/* The routine to call when the Guest pings us, or timeout. */ -work_func_t handle_kick; +vhost_work_fn_t handle_kick;/* Last available index we saw. */ u16 last_avail_idx;
@@ -86,11 +98,11 @@ struct vhost_virtqueue {struct iovec hdr[VHOST_NET_MAX_SG ];size_t hdr_size;/* We use a kind of RCU to access private pointer. -* All readers access it from workqueue, which makes it possible to -* flush the workqueue instead of synchronize_rcu. Therefore readers do +* All readers access it from worker, which makes it possible to +* flush the vhost_work instead of synchronize_rcu. Therefore readers do * not need to call rcu_read_lock/rcu_read_unlock: the beginning of -* work item execution acts instead of rcu_read_lock() and the end of -* work item execution acts instead of rcu_read_lock(). +* vhost_work execution acts instead of rcu_read_lock() and the end of +* vhost_work execution acts instead of rcu_read_lock(). * Writers use virtqueue mutex. */ void *private_data;/* Log write descriptors */
@@ -110,6 +122,9 @@ struct vhost_dev {int nvqs;struct file *log_file;struct eventfd_ctx *log_ctx; +spinlock_t work_lock; +struct list_head work_list; +struct task_struct *worker; };long vhost_dev_init (struct vhost_dev *,struct vhost_virtqueue *vqs,int nvqs);
@@ -136,9 +151,6 @@ bool vhost_enable_notify(struct vhost_viint vhost_log_write (struct vhost_virtqueue *vq,struct vhost_log *log,unsigned int log_num,u64 len); -int vhost_init (void ); -void vhost_cleanup (void ); -#define vq_err (vq, fmt, ...) do { \ pr_debug(pr_fmt(fmt), ##__VA_ARGS__); \ if ((vq)->error_ctx) \
Re: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Quoted 17 lines · Tejun Heo
Replace
vhost_workqueuewith per-vhost kthread. Other than callback argument change from structwork_struct* to structvhost_work*, there's no visible change to vhost_poll_*() interface.This conversion is to make each vhost use a dedicated kthread so that resource control via cgroup can be applied.
Partially based on Sridhar Samudrala's patch.
* Updated to use sub structure
vhost_workinstead of directly usingvhost_pollat Michael's suggestion.* Added flusher
wake_up()optimization at Michael's suggestion.Signed-off-by Tejun Heo<[email protected]>Cc Michael S. Tsirkin<[email protected]>Cc Sridhar Samudrala<[email protected]>
Quoted 7 lines
---
Okay, just tested it.dev->work_lockhad to be updated to use irq operations but other than that it worked just fine. Copied a large file using scp and it seems to perform pretty well although I don't have any reference of comparison. So, here's the updated version with the sign off.
I tested this with 4 VMs running netperf TCP stream tests from guest to host and i am seeing similar level of scalability in throughput i saw with the multi-thread workqueue patch.
11200Mb/s - default (host cpu utilization: 40%)
21600Mb/s - multi-thread (host cpu utilization: 86%)
Thanks
Sridhar
Quoted 417 lines
Thanks.
drivers/vhost/net.c | 56 ++++++++++--------------- drivers/vhost/vhost.c | 111 ++++++++++++++++++++++++++++++++++++++------------ drivers/vhost/vhost.h | 38 +++++++++++------ 3 files changed, 134 insertions(+), 71 deletions(-)Index:
work/drivers/vhost/net.c
===================================================================drivers/vhost/net.c +24
-32@@ -294,54 +294,58 @@ static void handle_rx(struct vhost_net *unuse_mm (net->dev.mm); } -static void handle_tx_kick (struct work_struct *work) +static void handle_tx_kick (struct vhost_work *work) { -struct vhost_virtqueue *vq; -struct vhost_net *net; - vq = container_of(work, struct vhost_virtqueue, poll.work); - net = container_of(vq->dev, struct vhost_net, dev); + struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue, + poll.work); + struct vhost_net *net = container_of(vq->dev, struct vhost_net, dev); +handle_tx (net); } -static void handle_rx_kick (struct work_struct *work) +static void handle_rx_kick (struct vhost_work *work) { -struct vhost_virtqueue *vq; -struct vhost_net *net; - vq = container_of(work, struct vhost_virtqueue, poll.work); - net = container_of(vq->dev, struct vhost_net, dev); + struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue, + poll.work); + struct vhost_net *net = container_of(vq->dev, struct vhost_net, dev); +handle_rx (net); } -static void handle_tx_net (struct work_struct *work) +static void handle_tx_net (struct vhost_work *work) { -struct vhost_net *net; - net = container_of(work, struct vhost_net, poll[VHOST_NET_VQ_TX].work); + struct vhost_net *net = container_of(work, struct vhost_net, + poll[VHOST_NET_VQ_TX ].work);handle_tx (net); } -static void handle_rx_net (struct work_struct *work) +static void handle_rx_net (struct vhost_work *work) { -struct vhost_net *net; - net = container_of(work, struct vhost_net, poll[VHOST_NET_VQ_RX].work); + struct vhost_net *net = container_of(work, struct vhost_net, + poll[VHOST_NET_VQ_RX ].work);handle_rx (net); }static int vhost_net_open (struct inode *inode,struct file *f) {struct vhost_net *n =kmalloc (sizeof *n,GFP_KERNEL ); +struct vhost_dev *dev;int r; +if (!n)return -ENOMEM ; + + dev =&n->dev; n->vqs[VHOST_NET_VQ_TX ].handle_kick = handle_tx_kick; n->vqs[VHOST_NET_VQ_RX ].handle_kick = handle_rx_kick; - r =vhost_dev_init (&n->dev, n->vqs,VHOST_NET_VQ_MAX ); + r =vhost_dev_init (dev, n->vqs,VHOST_NET_VQ_MAX );if (r<0 ) {kfree (n);return r; } -vhost_poll_init (n->poll +VHOST_NET_VQ_TX , handle_tx_net,POLLOUT ); -vhost_poll_init (n->poll +VHOST_NET_VQ_RX , handle_rx_net,POLLIN ); +vhost_poll_init (n->poll +VHOST_NET_VQ_TX , handle_tx_net,POLLOUT , dev); +vhost_poll_init (n->poll +VHOST_NET_VQ_RX , handle_rx_net,POLLIN , dev); n->tx_poll_state =VHOST_NET_POLL_DISABLED ; f->private_data = n;@@ -644,25 +648,13 @@ static struct miscdevice vhost_net_miscstatic int vhost_net_init (void ) { -int r =vhost_init (); -if (r) - goto err_init; - r =misc_register (&vhost_net_misc); -if (r) - goto err_reg; -return 0 ; -err_reg: -vhost_cleanup (); -err_init: -return r; - +return misc_register (&vhost_net_misc); }module_init (vhost_net_init);static void vhost_net_exit (void ) {misc_deregister (&vhost_net_misc); -vhost_cleanup (); }module_exit (vhost_net_exit);Index:
work/drivers/vhost/vhost.c
===================================================================drivers/vhost/vhost.c +85
-26@@ -17,12 +17,12 @@#include <linux/mm.h> #include <linux/miscdevice.h> #include <linux/mutex.h> -#include <linux/workqueue.h> #include <linux/rcupdate.h> #include <linux/poll.h> #include <linux/file.h> #include <linux/highmem.h> #include <linux/slab.h> +#include <linux/kthread.h> #include <linux/net.h> #include <linux/if_packet.h> @@ -37,8 +37,6 @@ enum { VHOST_MEMORY_F_LOG = 0x1, }; -static struct workqueue_struct *vhost_workqueue; -static void vhost_poll_func (struct file *file,wait_queue_head_t *wqh,poll_table *pt) {@@ -52,23 +50,31 @@ static void vhost_poll_func(struct filestatic int vhost_poll_wakeup (wait_queue_t *wait,unsigned mode,int sync,void *key) { -struct vhost_poll *poll; - poll = container_of(wait, struct vhost_poll, wait); + struct vhost_poll *poll = container_of(wait, struct vhost_poll, wait); +if (!((unsigned long )key& poll->mask))return 0 ; -queue_work (vhost_workqueue,&poll->work); +vhost_poll_queue (poll);return 0 ; }/* Init poll structure */ -void vhost_poll_init (struct vhost_poll *poll,work_func_t func, -unsigned long mask) +void vhost_poll_init (struct vhost_poll *poll,vhost_work_fn_t fn, +unsigned long mask,struct vhost_dev *dev) { -INIT_WORK (&poll->work, func); +struct vhost_work *work =&poll->work; +init_waitqueue_func_entry (&poll->wait, vhost_poll_wakeup);init_poll_funcptr (&poll->table, vhost_poll_func); poll->mask = mask; + poll->dev = dev; + +INIT_LIST_HEAD (&work->node); + work->fn = fn; +init_waitqueue_head (&work->done); +atomic_set (&work->flushing,0 ); + work->queue_seq = work->done_seq =0 ; }/* Start polling a file. We add ourselves to file's wait queue. The caller must @@ -92,12 +98,29 @@ void vhost_poll_stop(struct vhost_poll ** locks that are also used by the callback. */ void vhost_poll_flush (struct vhost_poll *poll) { -flush_work (&poll->work); +struct vhost_work *work =&poll->work; +int seq = work->queue_seq; + +atomic_inc (&work->flushing); +smp_mb__after_atomic_inc ();/* mb flush-b0 paired with worker-b1 */ +wait_event (work->done, seq - work->done_seq<=0 ); +atomic_dec (&work->flushing); +smp_mb__after_atomic_dec ();/* rmb flush-b1 paired with worker-b0 */ }void vhost_poll_queue (struct vhost_poll *poll) { -queue_work (vhost_workqueue,&poll->work); +struct vhost_dev *dev = poll->dev; +struct vhost_work *work =&poll->work; +unsigned long flags; + +spin_lock_irqsave (&dev->work_lock, flags); +if (list_empty (&work->node)) { +list_add_tail (&work->node,&dev->work_list); + work->queue_seq++; +wake_up_process (dev->worker); + } +spin_unlock_irqrestore (&dev->work_lock, flags); } static void vhost_vq_reset(struct vhost_dev *dev,@@ -125,10 +148,52 @@ static void vhost_vq_reset(struct vhost_ vq->log_ctx =NULL ; } +static int vhost_worker (void *data) +{ +struct vhost_dev *dev = data; +struct vhost_work *work; + +repeat: +set_current_state (TASK_INTERRUPTIBLE );/* mb paired w/ kthread_stop */ + +if (kthread_should_stop ()) { +__set_current_state (TASK_RUNNING ); +return 0 ; + } + + work =NULL ; +spin_lock_irq (&dev->work_lock); +if (!list_empty (&dev->work_list)) { + work =list_first_entry (&dev->work_list, + struct vhost_work, node); +list_del_init (&work->node); + } +spin_unlock_irq (&dev->work_lock); + +if (work) { +__set_current_state (TASK_RUNNING ); + work->fn (work); +smp_wmb ();/* wmb worker-b0 paired with flush-b1 */ + work->done_seq = work->queue_seq; +smp_mb ();/* mb worker-b1 paired with flush-b0 */ +if (atomic_read (&work->flushing)) +wake_up_all (&work->done); + }else +schedule (); + + goto repeat; +} +long vhost_dev_init (struct vhost_dev *dev,struct vhost_virtqueue *vqs,int nvqs) { +struct task_struct *worker;int i; + + worker =kthread_create (vhost_worker, dev,"vhost-%d" , current->pid); +if (IS_ERR (worker)) +return PTR_ERR (worker); + dev->vqs = vqs; dev->nvqs = nvqs;mutex_init (&dev->mutex);@@ -136,6 +201,9 @@ long vhost_dev_init(struct vhost_dev *de dev->log_file =NULL ; dev->memory =NULL ; dev->mm =NULL ; +spin_lock_init (&dev->work_lock); +INIT_LIST_HEAD (&dev->work_list); + dev->worker = worker;for (i =0 ; i< dev->nvqs; ++i) { dev->vqs[i].dev = dev;@@ -143,9 +211,10 @@ long vhost_dev_init(struct vhost_dev *devhost_vq_reset (dev, dev->vqs + i);if (dev->vqs[i].handle_kick)vhost_poll_init (&dev->vqs[i].poll, - dev->vqs[i].handle_kick, -POLLIN ); + dev->vqs[i].handle_kick,POLLIN , dev); } + +wake_up_process (worker);/* avoid contributing to loadavg */ return 0 ; }@@ -217,6 +286,9 @@ void vhost_dev_cleanup(struct vhost_devif (dev->mm)mmput (dev->mm); dev->mm =NULL ; + +WARN_ON (!list_empty (&dev->work_list)); +kthread_stop (dev->worker); }static int log_access_ok (void __user *log_base,u64 addr,unsigned long sz)@@ -1113,16 +1185,3 @@ void vhost_disable_notify(struct vhost_vvq_err (vq,"Failed to enable notification at %p: %d\n" , &vq->used->flags, r); } - -int vhost_init (void ) -{ - vhost_workqueue =create_singlethread_workqueue ("vhost" ); -if (!vhost_workqueue) -return -ENOMEM ; -return 0 ; -} - -void vhost_cleanup (void ) -{ -destroy_workqueue (vhost_workqueue); -}Index:
work/drivers/vhost/vhost.h
===================================================================drivers/vhost/vhost.h +25
-13@@ -5,13 +5,13 @@#include <linux/vhost.h> #include <linux/mm.h> #include <linux/mutex.h> -#include <linux/workqueue.h> #include <linux/poll.h> #include <linux/file.h> #include <linux/skbuff.h> #include <linux/uio.h> #include <linux/virtio_config.h> #include <linux/virtio_ring.h> +#include <asm/atomic.h> struct vhost_device ;@@ -20,19 +20,31 @@ enum { VHOST_NET_MAX_SG = MAX_SKB_FRAGS + 2, }; +struct vhost_work ; +typedef void (*vhost_work_fn_t )(struct vhost_work *work); + +struct vhost_work { +struct list_head node; +vhost_work_fn_t fn; +wait_queue_head_t done; +atomic_t flushing; +int queue_seq; +int done_seq; +}; +/* Poll a file (eventfd or socket) */ /* Note: there's nothing vhost specific about this structure. */ struct vhost_poll {poll_table table;wait_queue_head_t *wqh;wait_queue_t wait; -/* struct which will handle all actual work. */ -struct work_struct work; +struct vhost_work work;unsigned long mask; +struct vhost_dev *dev; }; -void vhost_poll_init (struct vhost_poll *poll,work_func_t func, -unsigned long mask); +void vhost_poll_init (struct vhost_poll *poll,vhost_work_fn_t fn, +unsigned long mask,struct vhost_dev *dev);void vhost_poll_start (struct vhost_poll *poll,struct file *file);void vhost_poll_stop (struct vhost_poll *poll);void vhost_poll_flush (struct vhost_poll *poll);@@ -63,7 +75,7 @@ struct vhost_virtqueue {struct vhost_poll poll;/* The routine to call when the Guest pings us, or timeout. */ -work_func_t handle_kick; +vhost_work_fn_t handle_kick;/* Last available index we saw. */ u16 last_avail_idx;@@ -86,11 +98,11 @@ struct vhost_virtqueue {struct iovec hdr[VHOST_NET_MAX_SG ];size_t hdr_size;/* We use a kind of RCU to access private pointer. -* All readers access it from workqueue, which makes it possible to -* flush the workqueue instead of synchronize_rcu. Therefore readers do +* All readers access it from worker, which makes it possible to +* flush the vhost_work instead of synchronize_rcu. Therefore readers do * not need to call rcu_read_lock/rcu_read_unlock: the beginning of -* work item execution acts instead of rcu_read_lock() and the end of -* work item execution acts instead of rcu_read_lock(). +* vhost_work execution acts instead of rcu_read_lock() and the end of +* vhost_work execution acts instead of rcu_read_lock(). * Writers use virtqueue mutex. */ void *private_data;/* Log write descriptors */ @@ -110,6 +122,9 @@ struct vhost_dev {int nvqs;struct file *log_file;struct eventfd_ctx *log_ctx; +spinlock_t work_lock; +struct list_head work_list; +struct task_struct *worker; };long vhost_dev_init (struct vhost_dev *,struct vhost_virtqueue *vqs,int nvqs);@@ -136,9 +151,6 @@ bool vhost_enable_notify(struct vhost_viint vhost_log_write (struct vhost_virtqueue *vq,struct vhost_log *log,unsigned int log_num,u64 len); -int vhost_init (void ); -void vhost_cleanup (void ); -#define vq_err (vq, fmt, ...) do { \ pr_debug(pr_fmt(fmt), ##__VA_ARGS__); \ if ((vq)->error_ctx) \
Michael S. Tsirkin
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
In reply to Tejun HeoQuoted 9 lines · Tejun Heo
Replace
vhost_workqueuewith per-vhost kthread. Other than callback argument change from structwork_struct* to structvhost_work*, there's no visible change to vhost_poll_*() interface.This conversion is to make each vhost use a dedicated kthread so that resource control via cgroup can be applied.
Partially based on Sridhar Samudrala's patch.
Quoted 8 lines
* Updated to use sub structure
vhost_workinstead of directly usingvhost_pollat Michael's suggestion.* Added flusher
wake_up()optimization at Michael's suggestion.Signed-off-by Tejun Heo <[email protected]>Cc Michael S. Tsirkin <[email protected]>Cc Sridhar Samudrala <[email protected]>
All the tricky barrier pairing made me uncomfortable. So I came up with this on top (untested): if we do all operations under the spinlock, we can get by without barriers and atomics. And since we need the lock for list operations anyway, this should have no paerformance impact.
What do you think?
drivers/vhost/vhost.c +44 -34
@@ -73,7 +73,7 @@ void vhost_poll_init(struct vhost_poll *poll, vhost_work_fn_t fn,INIT_LIST_HEAD (&work->node); work->fn = fn;init_waitqueue_head (&work->done); -atomic_set (&work->flushing,0 ); + work->flushing =0 ; work->queue_seq = work->done_seq =0 ; }
@@ -99,13 +99,23 @@ void vhost_poll_stop(struct vhost_poll *poll)void vhost_poll_flush (struct vhost_poll *poll) {struct vhost_work *work = &poll->work; -int seq = work->queue_seq; +unsigned seq, left; +int flushing; -atomic_inc (&work->flushing); -smp_mb__after_atomic_inc ();/* mb flush-b0 paired with worker-b1 */ -wait_event (work->done, seq - work->done_seq <=0 ); -atomic_dec (&work->flushing); -smp_mb__after_atomic_dec ();/* rmb flush-b1 paired with worker-b0 */ +spin_lock_irq (&dev->work_lock); + seq = work->queue_seq; + work->flushing++; +spin_unlock_irq (&dev->work_lock); +wait_event (work->done, { +spin_lock_irq (&dev->work_lock); + left = work->done_seq - seq; +spin_unlock_irq (&dev->work_lock); + left <UINT_MAX /2 ; + }); +spin_lock_irq (&dev->work_lock); + flushing = --work->flushing; +spin_unlock_irq (&dev->work_lock); +BUG_ON (flushing <0 ); }void vhost_poll_queue (struct vhost_poll *poll)
@@ -151,37 +161,37 @@ static void vhost_vq_reset(struct vhost_dev *dev,static int vhost_worker (void *data) {struct vhost_dev *dev = data; -struct vhost_work *work; +struct vhost_work *work =NULL ; -repeat: -set_current_state (TASK_INTERRUPTIBLE );/* mb paired w/ kthread_stop */ +for (;;) { +set_current_state (TASK_INTERRUPTIBLE );/* mb paired w/ kthread_stop */ -if (kthread_should_stop ()) { -__set_current_state (TASK_RUNNING ); -return 0 ; - } +if (kthread_should_stop ()) { +__set_current_state (TASK_RUNNING ); +return 0 ; + } - work =NULL ; -spin_lock_irq (&dev->work_lock); -if (!list_empty (&dev->work_list)) { - work =list_first_entry (&dev->work_list, - struct vhost_work, node); -list_del_init (&work->node); - } -spin_unlock_irq (&dev->work_lock); +spin_lock_irq (&dev->work_lock); +if (work) { + work->done_seq = work->queue_seq; +if (work->flushing) +wake_up_all (&work->done); + } +if (!list_empty (&dev->work_list)) { + work =list_first_entry (&dev->work_list, + struct vhost_work, node); +list_del_init (&work->node); + }else + work =NULL ; +spin_unlock_irq (&dev->work_lock); + +if (work) { +__set_current_state (TASK_RUNNING ); + work->fn (work); + }else +schedule (); -if (work) { -__set_current_state (TASK_RUNNING ); - work->fn (work); -smp_wmb ();/* wmb worker-b0 paired with flush-b1 */ - work->done_seq = work->queue_seq; -smp_mb ();/* mb worker-b1 paired with flush-b0 */ -if (atomic_read (&work->flushing)) -wake_up_all (&work->done); - }else -schedule (); - - goto repeat; + } } long vhost_dev_init(struct vhost_dev *dev,
drivers/vhost/vhost.h +3 -3
@@ -27,9 +27,9 @@ struct vhost_work {struct list_head node;vhost_work_fn_t fn;wait_queue_head_t done; -atomic_t flushing; -int queue_seq; -int done_seq; +int flushing; +unsigned queue_seq; +unsigned done_seq; };/* Poll a file (eventfd or socket) */
Tejun Heo
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Hello,
Quoted 6 lines · Michael S. Tsirkin
All the tricky barrier pairing made me uncomfortable. So I came up with this on top (untested): if we do all operations under the spinlock, we can get by without barriers and atomics. And since we need the lock for list operations anyway, this should have no paerformance impact.
What do you think?
I've created kthread_worker in wq#for-next tree and already converted ivtv to use it. Once this lands in mainline, I think converting vhost to use it would be better choice. kthread worker code uses basically the same logic used in the vhost_workqueue code but is better organized and documented. So, I think it would be better to stick with the original implementation, as otherwise we're likely to just decrease test coverage without much gain.
Quoted 24 lines
drivers/vhost/vhost.c +7
-10@@ -151,37 +161,37 @@ static void vhost_vq_reset(struct vhost_dev *dev,static int vhost_worker (void *data) {struct vhost_dev *dev = data; -struct vhost_work *work; +struct vhost_work *work =NULL ; -repeat: -set_current_state (TASK_INTERRUPTIBLE );/* mb paired w/ kthread_stop */ +for (;;) { +set_current_state (TASK_INTERRUPTIBLE );/* mb paired w/ kthread_stop */ -if (kthread_should_stop ()) { -__set_current_state (TASK_RUNNING ); -return 0 ; - } +if (kthread_should_stop ()) { +__set_current_state (TASK_RUNNING ); +return 0 ; + } - work =NULL ; -spin_lock_irq (&dev->work_lock); -if (!list_empty (&dev->work_list)) {
drivers/vhost/vhost.c static void vhost_vq_reset(struct vhost_dev *dev, · line 178
Quoted 10 lines
drivers/vhost/vhost.c +5
-5- work =list_first_entry (&dev->work_list, - struct vhost_work, node); -list_del_init (&work->node); - } -spin_unlock_irq (&dev->work_lock); +spin_lock_irq (&dev->work_lock); +if (work) { + work->done_seq = work->queue_seq; +if (work->flushing) +wake_up_all (&work->done);
I don't think doing this before executing the function is correct, so you'll have to release the lock, execute the function, regrab the lock and then do the flush processing.
Thanks.
Signature · 1 line
tejun
Michael S. Tsirkin
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Quoted 18 lines · Tejun Heo
Hello,
Quoted 6 lines · Michael S. Tsirkin
All the tricky barrier pairing made me uncomfortable. So I came up with this on top (untested): if we do all operations under the spinlock, we can get by without barriers and atomics. And since we need the lock for list operations anyway, this should have no paerformance impact.
What do you think?
I've created
kthread_workerin wq#for-next tree and already converted ivtv to use it. Once this lands in mainline, I think converting vhost to use it would be better choice. kthread worker code uses basically the same logic used in thevhost_workqueuecode but is better organized and documented. So, I think it would be better to stick with the original implementation, as otherwise we're likely to just decrease test coverage without much gain.
Sure, if we keep using workqueue. But I'd like to investigate this direction a bit more because there's discussion to switching from kthread to regular threads altogether.
Quoted 21 lines
Quoted 21 lines
drivers/vhost/vhost.c +7
-7@@ -151,37 +161,37 @@ static void vhost_vq_reset(struct vhost_dev *dev,static int vhost_worker (void *data) {struct vhost_dev *dev = data; -struct vhost_work *work; +struct vhost_work *work =NULL ; -repeat: -set_current_state (TASK_INTERRUPTIBLE );/* mb paired w/ kthread_stop */ +for (;;) { +set_current_state (TASK_INTERRUPTIBLE );/* mb paired w/ kthread_stop */ -if (kthread_should_stop ()) { -__set_current_state (TASK_RUNNING ); -return 0 ; - } +if (kthread_should_stop ()) { +__set_current_state (TASK_RUNNING ); +return 0 ; + }
Quoted 15 lines
drivers/vhost/vhost.c static void vhost_vq_reset(struct vhost_dev *dev, · line 178
Quoted 13 lines
drivers/vhost/vhost.c +5
-8- work =NULL ; -spin_lock_irq (&dev->work_lock); -if (!list_empty (&dev->work_list)) { - work =list_first_entry (&dev->work_list, - struct vhost_work, node); -list_del_init (&work->node); - } -spin_unlock_irq (&dev->work_lock); +spin_lock_irq (&dev->work_lock); +if (work) { + work->done_seq = work->queue_seq; +if (work->flushing) +wake_up_all (&work->done);I don't think doing this before executing the function is correct,
Well, before I execute the function work is NULL, so this is skipped. Correct?
Quoted 5 lines
so
you'll have to release the lock, execute the function, regrab the lock and then do the flush processing.Thanks.
It's done in the loop, so I thought we can reuse the locking done for the sake of processing the next work item. Makes sense?
Quoted 2 lines
Signature · 1 line
tejun
Tejun Heo
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Hello,
Quoted 8 lines · Michael S. Tsirkin
Quoted 8 lines
I've created
kthread_workerin wq#for-next tree and already converted ivtv to use it. Once this lands in mainline, I think converting vhost to use it would be better choice. kthread worker code uses basically the same logic used in thevhost_workqueuecode but is better organized and documented. So, I think it would be better to stick with the original implementation, as otherwise we're likely to just decrease test coverage without much gain.
Quoted 5 lines
Sure, if we keep using workqueue. But I'd like to investigate this direction a bit more because there's discussion to switching from kthread to regular threads altogether.
Hmmm? It doesn't have much to do with workqueue. kthread_worker is a simple wrapper around kthread. It now assumes kthread but changing it to be useable with any thread shouldn't be too hard. Wouldn't that be better?
Quoted 9 lines
Quoted 1 line
I don't think doing this before executing the function is correct,
Well, before I execute the function work is NULL, so this is skipped. Correct?
Quoted 4 lines
so
you'll have to release the lock, execute the function, regrab the lock and then do the flush processing.
Quoted 5 lines
Quoted 1 line
Thanks.
It's done in the loop, so I thought we can reuse the locking done for the sake of processing the next work item. Makes sense?
Yeap, right. I think it would make much more sense to use common code when it becomes available but if you think the posted change is necessary till then, please feel free to go ahead.
Thanks.
Signature · 1 line
tejun
Michael S. Tsirkin
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Quoted 13 lines · Tejun Heo
Hello,
Quoted 10 lines · Michael S. Tsirkin
Quoted 9 lines
I've created
kthread_workerin wq#for-next tree and already converted ivtv to use it. Once this lands in mainline, I think converting vhost to use it would be better choice. kthread worker code uses basically the same logic used in thevhost_workqueuecode but is better organized and documented. So, I think it would be better to stick with the original implementation, as otherwise we're likely to just decrease test coverage without much gain.
Quoted 8 lines
Quoted 3 lines
Sure, if we keep using workqueue. But I'd like to investigate this direction a bit more because there's discussion to switching from kthread to regular threads altogether.
Hmmm? It doesn't have much to do with workqueue.
kthread_workeris a simple wrapper around kthread. It now assumes kthread but changing it to be useable with any thread shouldn't be too hard. Wouldn't that be better?
Yes, of course, when common code becomes available we should switch to that.
Quoted 23 lines
Quoted 9 lines
Quoted 1 line
I don't think doing this before executing the function is correct,
Well, before I execute the function work is NULL, so this is skipped. Correct?
Quoted 4 lines
so
you'll have to release the lock, execute the function, regrab the lock and then do the flush processing.Quoted 5 lines
Quoted 1 line
Thanks.
It's done in the loop, so I thought we can reuse the locking done for the sake of processing the next work item. Makes sense?
Yeap, right. I think it would make much more sense to use common code when it becomes available but if you think the posted change is necessary till then, please feel free to go ahead.
Thanks.
Signature · 1 line
tejun
Michael S. Tsirkin
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
In reply to Tejun HeoQuoted 13 lines · Tejun Heo
Hello,
Quoted 10 lines · Michael S. Tsirkin
Quoted 9 lines
I've created
kthread_workerin wq#for-next tree and already converted ivtv to use it. Once this lands in mainline, I think converting vhost to use it would be better choice. kthread worker code uses basically the same logic used in thevhost_workqueuecode but is better organized and documented. So, I think it would be better to stick with the original implementation, as otherwise we're likely to just decrease test coverage without much gain.
Quoted 8 lines
Quoted 3 lines
Sure, if we keep using workqueue. But I'd like to investigate this direction a bit more because there's discussion to switching from kthread to regular threads altogether.
Hmmm? It doesn't have much to do with workqueue.
kthread_workeris a simple wrapper around kthread. It now assumes kthread but changing it to be useable with any thread shouldn't be too hard. Wouldn't that be better?
BTW, kthread_worker would benefit from the optimization I implemented here as well.
Signature · 1 line
MST
Tejun Heo
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Hello,
Quoted 2 lines · Michael S. Tsirkin
BTW,
kthread_workerwould benefit from the optimization I implemented here as well.
Hmmm... I'm not quite sure whether it's an optimization. I thought the patch was due to feeling uncomfortable about using barriers? Is it an optimization?
Thanks.
Signature · 1 line
tejun
Tejun Heo
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Quoted 9 lines · Tejun Heo
Hello,
Quoted 2 lines · Michael S. Tsirkin
BTW,
kthread_workerwould benefit from the optimization I implemented here as well.Hmmm... I'm not quite sure whether it's an optimization. I thought the patch was due to feeling uncomfortable about using barriers? Is it an optimization?
Yeah, one less smp_mb() in execution path. The lock dancing in flush() is ugly but then again mucking with barriers could be harder to understand. Care to send a patch against wq#for-next tree?
Thanks.
Signature · 1 line
tejun
Michael S. Tsirkin
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Quoted 11 lines · Tejun Heo
Quoted 9 lines · Tejun Heo
Hello,
Quoted 2 lines · Michael S. Tsirkin
BTW,
kthread_workerwould benefit from the optimization I implemented here as well.Hmmm... I'm not quite sure whether it's an optimization. I thought the patch was due to feeling uncomfortable about using barriers? Is it an optimization?
Quoted 5 lines
Yeah, one less
smp_mb()in execution path. The lock dancing inflush()is ugly but then again mucking with barriers could be harder to understand. Care to send a patch against wq#for-next tree?Thanks.
Sure. Where's that, exactly?
Quoted 2 lines
Signature · 1 line
tejun
Michael S. Tsirkin
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
In reply to Tejun HeoQuoted 8 lines · Tejun Heo
Hello,
Quoted 2 lines · Michael S. Tsirkin
BTW,
kthread_workerwould benefit from the optimization I implemented here as well.Hmmm... I'm not quite sure whether it's an optimization. I thought the patch was due to feeling uncomfortable about using barriers?
Oh yes. But getting rid of barriers is what motivated me originally.
Quoted 3 lines
Is it an optimization?
Thanks.
Yes, sure. This removes atomic read and 2 barrier operations on data path. And it does not add any new synchronization: instead, we reuse the lock that we take anyway. The relevant part is:
+ if (work) {
+ __set_current_state(TASK_RUNNING);
+ work->fn(work);
+ } else
+ schedule();
- if (work) {
- __set_current_state(TASK_RUNNING); - work->fn(work); - smp_wmb(); /* wmb worker-b0 paired with flush-b1 */ - work->done_seq = work->queue_seq; - smp_mb(); /* mb worker-b1 paired with flush-b0 */ - if (atomic_read(&work->flushing))
- wake_up_all(&work->done);
- } else
- schedule();
-
- goto repeat;
Is there a git tree with kthread_worker applied?
I might do this just for fun ...
Quoted 2 lines
Signature · 1 line
tejun
Tejun Heo
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Hello,
Quoted 4 lines · Michael S. Tsirkin
Quoted 2 lines
Hmmm... I'm not quite sure whether it's an optimization. I thought the patch was due to feeling uncomfortable about using barriers?
Oh yes. But getting rid of barriers is what motivated me originally.
Yeah, getting rid of barriers is always good. :-)
Quoted 2 lines
Is there a git tree with
kthread_workerapplied?
I might do this just for fun ...
git://git.kernel.org/pub/scm/linux/kernel/git/tj/wq.git for-next
For the original implementaiton, please take a look at commit b56c0d8937e665a27d90517ee7a746d0aa05af46.
* Can you please keep the outer goto repeat loop? I just don't like outermost for (;;).
* Placing try_to_freeze() could be a bit annoying. It shouldn't be executed when there's a work to flush.
* I think A - B <= 0 test would be more familiar. At least time_before/after() are implemented that way.
Thanks.
Signature · 1 line
tejun
Tejun Heo
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Just one more thing.
Quoted 2 lines · Tejun Heo
* Placing
try_to_freeze()could be a bit annoying. It shouldn't be executed when there's a work to flush.
* Similar issue exists for kthread_stop(). The kthread shouldn't exit while there's a work to flush (please note that kthread_worker interface allows detaching / attaching worker kthread during operation, so it should remain in consistent state with regard to flushing).
Thanks.
Signature · 1 line
tejun
Michael S. Tsirkin
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Quoted 5 lines · Tejun Heo
Just one more thing.
Quoted 2 lines · Tejun Heo
* Placing
try_to_freeze()could be a bit annoying. It shouldn't be executed when there's a work to flush.
BTW why is this important?
We could always get another work and flush right after try_to_freeze, and then flush would block for a long time.
BTW the vhost patch you sent does not do this at all. I am guessing it is because our thread is not freezable?
Quoted 7 lines
* Similar issue exists for
kthread_stop(). The kthread shouldn't exit while there's a work to flush (please note thatkthread_workerinterface allows detaching / attaching worker kthread during operation, so it should remain in consistent state with regard to flushing).Thanks.
Not sure I agree here. Users must synchronise flush and stop calls. Otherwise a work might get queued after stop is called, and you won't be able to flush it.
Quoted 2 lines
Signature · 1 line
tejun
Tejun Heo
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Hello,
Quoted 10 lines · Michael S. Tsirkin
Quoted 3 lines · Tejun Heo
Quoted 2 lines
* Placing
try_to_freeze()could be a bit annoying. It shouldn't be executed when there's a work to flush.BTW why is this important?
We could always get another work and flush right aftertry_to_freeze, and then flush would block for a long time.BTW the vhost patch you sent does not do this at all. I am guessing it is because our thread is not freezable?
Yeap, I think so.
Quoted 9 lines
Quoted 5 lines
* Similar issue exists for
kthread_stop(). The kthread shouldn't exit while there's a work to flush (please note thatkthread_workerinterface allows detaching / attaching worker kthread during operation, so it should remain in consistent state with regard to flushing).Not sure I agree here. Users must synchronise flush and stop calls. Otherwise a work might get queued after stop is called, and you won't be able to flush it.
For freeze, it probably is okay but for stop, I think it's better to keep the semantics straight forward. It may be okay to do otherwise but having such oddity in generic interface is nasty and may lead to surprises which can be pretty difficult to track down later on. It's just a bit more of annoyance while writing the generic code, so...
Thanks.
Signature · 1 line
tejun
Michael S. Tsirkin
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Quoted 22 lines · Tejun Heo
Hello,
Quoted 10 lines · Michael S. Tsirkin
Quoted 3 lines · Tejun Heo
Quoted 2 lines
* Placing
try_to_freeze()could be a bit annoying. It shouldn't be executed when there's a work to flush.BTW why is this important?
We could always get another work and flush right aftertry_to_freeze, and then flush would block for a long time.BTW the vhost patch you sent does not do this at all. I am guessing it is because our thread is not freezable?
Yeap, I think so.
Quoted 6 lines
Quoted 5 lines
* Similar issue exists for
kthread_stop(). The kthread shouldn't exit while there's a work to flush (please note thatkthread_workerinterface allows detaching / attaching worker kthread during operation, so it should remain in consistent state with regard to flushing).
Quoted 6 lines
Quoted 3 lines
Not sure I agree here. Users must synchronise flush and stop calls. Otherwise a work might get queued after stop is called, and you won't be able to flush it.
For freeze, it probably is okay but for stop, I think it's better to keep the semantics straight forward.
What are the semantics then? What do we want stop followed by queue and flush to do?
Quoted 9 lines
It may be okay to do otherwise
but having such oddity in generic interface is nasty and may lead to surprises which can be pretty difficult to track down later on. It's just a bit more of annoyance while writing the generic code, so...Thanks.
Signature · 1 line
tejun
Tejun Heo
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Hello,
Quoted 5 lines · Michael S. Tsirkin
Quoted 2 lines
For freeze, it probably is okay but for stop, I think it's better to keep the semantics straight forward.
What are the semantics then? What do we want stop followed by queue and flush to do?
One scenario I can think of is the following.
kthread_worker allows kthreads to be attached and stopped anytime, so if the caller stops the current worker while flushing is pending and attaches a new worker, the flushing which was pending will never happen.
But, in general, it's nasty to allow execution and its completion to be separated. Things like that are likely to bite us back in obscure ways. I think it would be silly to have such oddity in generic code when it can be avoided without too much trouble.
Thanks.
Signature · 1 line
tejun
Michael S. Tsirkin
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
In reply to Tejun HeoQuoted 1 line · Tejun Heo
Just one more thing.
I noticed that with vhost, flush_work was getting the worker pointer as well. Can we live with this API change?
Signature · 1 line
MST
Tejun Heo
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Quoted 5 lines · Michael S. Tsirkin
Quoted 1 line · Tejun Heo
Just one more thing.
I noticed that with vhost,
flush_workwas getting the worker pointer as well. Can we live with this API change?
Yeah, the flushing mechanism wouldn't work reliably if the work is queued to a different worker without flushing, so yeah passing in @worker might actually be better.
Thanks.
Signature · 1 line
tejun
Tejun Heo
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Hello,
Quoted 7 lines · Tejun Heo
Quoted 2 lines · Michael S. Tsirkin
I noticed that with vhost,
flush_workwas getting the worker pointer as well. Can we live with this API change?Yeah, the flushing mechanism wouldn't work reliably if the work is queued to a different worker without flushing, so yeah passing in @worker might actually be better.
Thinking a bit more about it, it kind of sucks that queueing to another worker from worker->func() breaks flush. Maybe the right thing to do there is using atomic_t for done_seq? It pays a bit more overhead but maybe that's justifiable to keep the API saner? It would be great if it can be fixed somehow even if it means that the work has to be separately flushed for each worker it has been on before being destroyed.
Or, if flushing has to be associated with a specific worker anyway, maybe it would be better to move the sequence counter to kthread_worker and do it similarly with the original workqueue so that work can be destroyed once execution starts? Then, it can at least remain semantically identical to the original workqueue.
Thanks.
Signature · 1 line
tejun
Michael S. Tsirkin
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Quoted 19 lines · Tejun Heo
Hello,
Quoted 7 lines · Tejun Heo
Quoted 2 lines · Michael S. Tsirkin
I noticed that with vhost,
flush_workwas getting the worker pointer as well. Can we live with this API change?Yeah, the flushing mechanism wouldn't work reliably if the work is queued to a different worker without flushing, so yeah passing in @worker might actually be better.
Thinking a bit more about it, it kind of sucks that queueing to another worker from
worker->func()breaks flush. Maybe the right thing to do there is usingatomic_tfordone_seq? It pays a bit more overhead but maybe that's justifiable to keep the API saner? It would be great if it can be fixed somehow even if it means that the work has to be separately flushed for each worker it has been on before being destroyed.
Quoted 7 lines
Or, if flushing has to be associated with a specific worker anyway, maybe it would be better to move the sequence counter to
kthread_workerand do it similarly with the original workqueue so that work can be destroyed once execution starts? Then, it can at least remain semantically identical to the original workqueue.Thanks.
This last sounds sane: in fact I didn't know there is any difference.
Quoted 2 lines
Signature · 1 line
tejun
Michael S. Tsirkin
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
In reply to Tejun HeoQuoted 7 lines · Tejun Heo
Hello,
Quoted 4 lines · Tejun Heo
Quoted 2 lines · Michael S. Tsirkin
I noticed that with vhost,
flush_workwas getting the worker pointer as well. Can we live with this API change?
Quoted 7 lines
Quoted 3 lines
Yeah, the flushing mechanism wouldn't work reliably if the work is queued to a different worker without flushing, so yeah passing in @worker might actually be better.
Thinking a bit more about it, it kind of sucks that queueing to another worker from
worker->func()breaks flush. Maybe the right thing to do there is usingatomic_tfordone_seq?
I don't believe it will help: we might have:
worker1 runs work
work requeues itself queued index = 1 worker1 reads queued index = 1 worker2 runs work
work requeues itself queued index = 2 worker2 runs work
worker2 reads queued index = 2 worker2 writes done index = 2 worker1 writes done index = 1
As you see, done index got moved back.
Quoted 16 lines
It pays a bit more
overhead but maybe that's justifiable to keep the API saner? It would be great if it can be fixed somehow even if it means that the work has to be separately flushed for each worker it has been on before being destroyed.Or, if flushing has to be associated with a specific worker anyway, maybe it would be better to move the sequence counter to
kthread_workerand do it similarly with the original workqueue so that work can be destroyed once execution starts? Then, it can at least remain semantically identical to the original workqueue.Thanks.
Signature · 1 line
tejun
Tejun Heo
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Quoted 16 lines · Michael S. Tsirkin
Quoted 3 lines
Thinking a bit more about it, it kind of sucks that queueing to another worker from
worker->func()breaks flush. Maybe the right thing to do there is usingatomic_tfordone_seq?I don't believe it will help: we might have:
worker1 runs work
work requeues itself queued index = 1 worker1 reads queued index = 1 worker2 runs work
work requeues itself queued index = 2 worker2 runs work
worker2 reads queued index = 2 worker2 writes done index = 2 worker1 writes done index = 1
Quoted 1 line
As you see, done index got moved back.
Yeah, I think the flushing logic should be moved to the worker. Are you interested in doing it w/ your change?
Thanks.
Signature · 1 line
tejun
Michael S. Tsirkin
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Quoted 17 lines · Tejun Heo
Quoted 16 lines · Michael S. Tsirkin
Quoted 3 lines
Thinking a bit more about it, it kind of sucks that queueing to another worker from
worker->func()breaks flush. Maybe the right thing to do there is usingatomic_tfordone_seq?I don't believe it will help: we might have:
worker1 runs work
work requeues itself queued index = 1 worker1 reads queued index = 1 worker2 runs work
work requeues itself queued index = 2 worker2 runs work
worker2 reads queued index = 2 worker2 writes done index = 2 worker1 writes done index = 1
Quoted 6 lines
Quoted 1 line
As you see, done index got moved back.
Yeah, I think the flushing logic should be moved to the worker. Are you interested in doing it w/ your change?
Thanks.
I'm unsure how flush_work operates under these conditions. E.g. in workqueue.c, this seems to work by keeping a pointer to current workqueue in the work. But what prevents us from destroying the workqueue when work might not be running?
Is this currently broken if you use multiple workqueues for the same work? If yes, I propose we do as I did, making flush_work get worker pointer, and only flushing on that worker.
Quoted 2 lines
Signature · 1 line
tejun
Tejun Heo
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Hello,
Quoted 4 lines · Michael S. Tsirkin
I'm unsure how
flush_workoperates under these conditions. E.g. inworkqueue.c, this seems to work by keeping a pointer to current workqueue in the work. But what prevents us from destroying the workqueue when work might not be running?
In cmwq, work points to the gcwq it was on, which keeps track of all the works in progress, so flushing work which is on a destroyed workqueue should be fine, but in the original implementation, it would end up accessing freed memory.
Quoted 4 lines
Is this currently broken if you use multiple workqueues for the same work? If yes, I propose we do as I did, making
flush_workget worker pointer, and only flushing on that worker.
The original semantics of workqueue is that flush_work() guarantees that the work has finished executing on the workqueue it was last queued on. Adding @worker to flush_work() is okay, I think.
Thanks.
Signature · 1 line
tejun
Michael S. Tsirkin
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
In reply to Tejun HeoHere's an untested patch forward-ported from vhost (works fine for vhost).
kthread_worker: replace barriers+atomics with a lock
We can save some cycles and make code simpler by reusing worker lock for flush, instead of atomics. flush_kthread_work needs to get worker pointer for this to work.
---
include/linux/kthread.h +4 -3
@@ -58,7 +58,7 @@ struct kthread_work {struct list_head node;kthread_work_func_t func;wait_queue_head_t done; -atomic_t flushing; +int flushing;int queue_seq;int done_seq; };
@@ -72,7 +72,7 @@ struct kthread_work { .node = LIST_HEAD_INIT((work).node), \ .func = (fn), \ .done = __WAIT_QUEUE_HEAD_INITIALIZER((work).done), \ - .flushing = ATOMIC_INIT(0), \ + .flushing = 0, \ }#define DEFINE_KTHREAD_WORKER (worker) \
@@ -96,7 +96,8 @@ int kthread_worker_fn(void *worker_ptr);bool queue_kthread_work (struct kthread_worker *worker,struct kthread_work *work); -void flush_kthread_work (struct kthread_work *work); +void flush_kthread_work (struct kthread_worker *worker, +struct kthread_work *work);void flush_kthread_worker (struct kthread_worker *worker);#endif /* _LINUX_KTHREAD_H */
kernel/kthread.c +29 -25
93 lines not on this page · raw message
Michael S. Tsirkin
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
In reply to Tejun HeoQuoted 13 lines · Tejun Heo
Hello,
Quoted 4 lines · Michael S. Tsirkin
Quoted 2 lines
Hmmm... I'm not quite sure whether it's an optimization. I thought the patch was due to feeling uncomfortable about using barriers?
Oh yes. But getting rid of barriers is what motivated me originally.
Yeah, getting rid of barriers is always good. :-)
Quoted 2 lines
Is there a git tree with
kthread_workerapplied?
I might do this just for fun ...
Quoted 7 lines
git://git.kernel.org/pub/scm/linux/kernel/git/tj/wq.gitfor-nextFor the original implementaiton, please take a look at commit
b56c0d8937e665a27d90517ee7a746d0aa05af46.* Can you please keep the outer goto repeat loop? I just don't like outermost for (;;).
Okay ... can we put the code in a {} scope to make it clear where does the loop starts and ends?
Quoted 2 lines
* Placing
try_to_freeze()could be a bit annoying. It shouldn't be executed when there's a work to flush.
It currently seems to be executed when there is work to flush. Is this wrong?
Quoted 2 lines
* I think A - B <= 0 test would be more familiar. At least time_before/after() are implemented that way.
I am concerned that this overflows a signed integer - which I seem to remeber that C99 disallows.
timer macros are on data path so might be worth the risk there, but flush is slow path so better be safe?
Quoted 4 lines
Thanks.
Signature · 1 line
tejun
Tejun Heo
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Quoted 5 lines · Michael S. Tsirkin
Quoted 2 lines
* Can you please keep the outer goto repeat loop? I just don't like outermost for (;;).
Okay ... can we put the code in a {} scope to make it clear where does the loop starts and ends?
If we're gonna do that, it would be better to put it inside a loop construct. The reason why I don't like it is that loops like that don't really help read/writeability much while indenting the whole logic unnecessarily and look more like a result of obsession against goto rather than any practical reason. It's just a cosmetic preference and I might as well be the weirdo here, so if you feel strong about it, please feel free to put everything in a loop.
Quoted 5 lines
Quoted 2 lines
* Placing
try_to_freeze()could be a bit annoying. It shouldn't be executed when there's a work to flush.It currently seems to be executed when there is work to flush. Is this wrong?
Oh, does it? As I wrote in the other mail, things like that wouldn't necessarily break correctness but I think it would be better to avoid surprises in the generic code if not too difficult.
Quoted 5 lines
Quoted 2 lines
* I think A - B <= 0 test would be more familiar. At least time_before/after() are implemented that way.
I am concerned that this overflows a signed integer - which I seem to remeber that C99 disallows.
Really? Overflows of pointer isn't expected and that's why we have weird RELOC_HIDE() macro for such calculations but integers not expected to overflow is a news to me. Are you sure? That basically means time_before/after() aren't safe either.
Quoted 2 lines
timer macros are on data path so might be worth the risk there, but flush is slow path so better be safe?
I don't think performance matters much here. I just think the sign test is clearer / more familiar for the logic.
Thanks.
Signature · 1 line
tejun
Michael S. Tsirkin
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Quoted 18 lines · Tejun Heo
Quoted 5 lines · Michael S. Tsirkin
Quoted 2 lines
* Can you please keep the outer goto repeat loop? I just don't like outermost for (;;).
Okay ... can we put the code in a {} scope to make it clear where does the loop starts and ends?
If we're gonna do that, it would be better to put it inside a loop construct. The reason why I don't like it is that loops like that don't really help read/writeability much while indenting the whole logic unnecessarily and look more like a result of obsession against goto rather than any practical reason. It's just a cosmetic preference and I might as well be the weirdo here, so if you feel strong about it, please feel free to put everything in a loop.
Quoted 3 lines
Quoted 2 lines
* Placing
try_to_freeze()could be a bit annoying. It shouldn't be executed when there's a work to flush.
Quoted 6 lines
Quoted 2 lines
It currently seems to be executed when there is work to flush. Is this wrong?
Oh, does it? As I wrote in the other mail, things like that wouldn't necessarily break correctness but I think it would be better to avoid surprises in the generic code if not too difficult.
Let's try to define what do we want to achieve then. Do you want code that flushes workers not to block when workers are frozen? How will we handle work submitted when worker is frozen?
Quoted 10 lines
Quoted 5 lines
Quoted 2 lines
* I think A - B <= 0 test would be more familiar. At least time_before/after() are implemented that way.
I am concerned that this overflows a signed integer - which I seem to remeber that C99 disallows.
Really? Overflows of pointer isn't expected and that's why we have weird
RELOC_HIDE()macro for such calculations but integers not expected to overflow is a news to me. Are you sure? That basically means time_before/after() aren't safe either.
As I said, in C99.
However, the kernel is built with -fno-strict-overflow, so it will work.
Quoted 10 lines
Quoted 2 lines
timer macros are on data path so might be worth the risk there, but flush is slow path so better be safe?
I don't think performance matters much here. I just think the sign test is clearer / more familiar for the logic.
Thanks.
Signature · 1 line
tejun
Tejun Heo
maintainerRe: [PATCH UPDATED 1/3] vhost: replace vhost_workqueue with per-vhost kthread
Hello,
Quoted 3 lines · Michael S. Tsirkin
Let's try to define what do we want to achieve then. Do you want code that flushes workers not to block when workers are frozen? How will we handle work submitted when worker is frozen?
As I wrote earlier, it's not necessarily about correctness but rather avoiding unnecessary surprises and of course flushing can and should stall if the queue is frozen but let's not separate execution of a work and its completion with something which can take undeterminate amount of time.
Thanks.
Signature · 1 line
tejun
Tejun Heo
maintainerPATCH 2/3 cgroups: Add an API to attach a task to current task's cgroup
In reply to Michael S. TsirkinFrom: Sridhar Samudrala <[email protected]>
Add a new kernel API to attach a task to current task's cgroup in all the active hierarchies.
--- include/linux/cgroup.h | 1 + kernel/cgroup.c | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+)
include/linux/cgroup.h +1 -0
@@ -570,6 +570,7 @@ struct task_struct *cgroup_iter_next(strvoid cgroup_iter_end (struct cgroup *cgrp,struct cgroup_iter *it);int cgroup_scan_tasks (struct cgroup_scanner *scan);int cgroup_attach_task (struct cgroup *,struct task_struct *); +int cgroup_attach_task_current_cg (struct task_struct *);/* * CSS ID is ID for cgroup_subsys_state structs under subsys. This only works
kernel/cgroup.c +23 -0
@@ -1788,6 +1788,29 @@ out:return retval; } +/** +* cgroup_attach_task_current_cg - attach task 'tsk' to current task's cgroup +* @tsk: the task to be attached +*/ +int cgroup_attach_task_current_cg (struct task_struct *tsk) +{ +struct cgroupfs_root *root; +struct cgroup *cur_cg; +int retval =0 ; + +cgroup_lock (); +for_each_active_root (root) { + cur_cg =task_cgroup_from_root (current, root); + retval =cgroup_attach_task (cur_cg, tsk); +if (retval) +break ; + } +cgroup_unlock (); + +return retval; +} +EXPORT_SYMBOL_GPL (cgroup_attach_task_current_cg); +/* * Attach task with pid 'pid' to cgroup 'cgrp'. Call with cgroup_mutex * held. May take task_lock of task
Tejun Heo
maintainerPATCH 3/3 vhost: apply cpumask and cgroup to vhost workers
In reply to Michael S. TsirkinApply the cpumask and cgroup of the initializing task to the created vhost worker.
Based on Sridhar Samudrala's patch. Li Zefan spotted a bug in error path (twice), fixed (twice).
--- drivers/vhost/vhost.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-)
drivers/vhost/vhost.c +30 -4
@@ -23,6 +23,7 @@#include <linux/highmem.h> #include <linux/slab.h> #include <linux/kthread.h> +#include <linux/cgroup.h> #include <linux/net.h> #include <linux/if_packet.h>
@@ -187,11 +188,29 @@ long vhost_dev_init(struct vhost_dev *de struct vhost_virtqueue *vqs, int nvqs) {struct task_struct *worker; -int i; +cpumask_var_t mask; +int i, ret = -ENOMEM ; + +if (!alloc_cpumask_var (&mask,GFP_KERNEL )) + goto out_free_mask; worker =kthread_create (vhost_worker, dev,"vhost-%d" , current->pid); -if (IS_ERR (worker)) -return PTR_ERR (worker); +if (IS_ERR (worker)) { + ret =PTR_ERR (worker); + goto out_free_mask; + } + + ret =sched_getaffinity (current->pid, mask); +if (ret) + goto out_stop_worker; + + ret =sched_setaffinity (worker->pid, mask); +if (ret) + goto out_stop_worker; + + ret =cgroup_attach_task_current_cg (worker); +if (ret) + goto out_stop_worker; dev->vqs = vqs; dev->nvqs = nvqs;
@@ -214,7 +233,14 @@ long vhost_dev_init(struct vhost_dev *de }wake_up_process (worker);/* avoid contributing to loadavg */ -return 0 ; + ret =0 ; + goto out_free_mask; + +out_stop_worker: +kthread_stop (worker); +out_free_mask: +free_cpumask_var (mask); +return ret; }/* Caller should have device mutex */
Michael S. Tsirkin
maintainerRe: [PATCH 3/3] vhost: apply cpumask and cgroup to vhost workers
Quoted 10 lines · Tejun Heo
Apply the cpumask and cgroup of the initializing task to the created vhost worker.
Based on Sridhar Samudrala's patch. Li Zefan spotted a bug in error path (twice), fixed (twice).
Signed-off-by Tejun Heo <[email protected]>Cc Michael S. Tsirkin <[email protected]>Cc Sridhar Samudrala <[email protected]>Cc Li Zefan <[email protected]>
Something that I wanted to figure out - what happens if the CPU mask limits us to a certain CPU that subsequently goes offline? Will e.g. flush block forever or until that CPU comes back? Also, does singlethreaded workqueue behave in the same way?
Quoted 65 lines
--- drivers/vhost/vhost.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-)Index:
work/drivers/vhost/vhost.c
===================================================================drivers/vhost/vhost.c +30
-4@@ -23,6 +23,7 @@#include <linux/highmem.h> #include <linux/slab.h> #include <linux/kthread.h> +#include <linux/cgroup.h> #include <linux/net.h> #include <linux/if_packet.h> @@ -187,11 +188,29 @@ long vhost_dev_init(struct vhost_dev *de struct vhost_virtqueue *vqs, int nvqs) {struct task_struct *worker; -int i; +cpumask_var_t mask; +int i, ret = -ENOMEM ; + +if (!alloc_cpumask_var (&mask,GFP_KERNEL )) + goto out_free_mask; worker =kthread_create (vhost_worker, dev,"vhost-%d" , current->pid); -if (IS_ERR (worker)) -return PTR_ERR (worker); +if (IS_ERR (worker)) { + ret =PTR_ERR (worker); + goto out_free_mask; + } + + ret =sched_getaffinity (current->pid, mask); +if (ret) + goto out_stop_worker; + + ret =sched_setaffinity (worker->pid, mask); +if (ret) + goto out_stop_worker; + + ret =cgroup_attach_task_current_cg (worker); +if (ret) + goto out_stop_worker; dev->vqs = vqs; dev->nvqs = nvqs;@@ -214,7 +233,14 @@ long vhost_dev_init(struct vhost_dev *de }wake_up_process (worker);/* avoid contributing to loadavg */ -return 0 ; + ret =0 ; + goto out_free_mask; + +out_stop_worker: +kthread_stop (worker); +out_free_mask: +free_cpumask_var (mask); +return ret; }/* Caller should have device mutex */
Tejun Heo
maintainerRe: [PATCH 3/3] vhost: apply cpumask and cgroup to vhost workers
Hello,
Quoted 2 lines · Michael S. Tsirkin
Something that I wanted to figure out - what happens if the CPU mask limits us to a certain CPU that subsequently goes offline?
The thread gets unbound during the last steps of cpu offlining.
Quoted 2 lines
Will e.g. flush block forever or until that CPU comes back? Also, does singlethreaded workqueue behave in the same way?
So, things will proceed as usual although the thread will lose its affinity. Singlethread wqs don't bind their workers (and they shouldn't! :-). MT ones explicitly manage workers according to cpu up/down events.
Thanks.
Signature · 1 line
tejun
Re: [PATCH 3/3] vhost: apply cpumask and cgroup to vhost workers
In reply to Tejun HeoQuoted 27 lines · Tejun Heo
Apply the cpumask and cgroup of the initializing task to the created vhost worker.
Based on Sridhar Samudrala's patch. Li Zefan spotted a bug in error path (twice), fixed (twice).
Signed-off-by Tejun Heo <[email protected]>Cc Michael S. Tsirkin <[email protected]>Cc Sridhar Samudrala <[email protected]>Cc Li Zefan <[email protected]>--- drivers/vhost/vhost.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-)Index:
work/drivers/vhost/vhost.c
===================================================================drivers/vhost/vhost.c +1
-0@@ -23,6 +23,7 @@#include <linux/highmem.h> #include <linux/slab.h> #include <linux/kthread.h> +#include <linux/cgroup.h> #include <linux/net.h> #include <linux/if_packet.h>
drivers/vhost/vhost.c long vhost_dev_init(struct vhost_dev *de · line 195
Quoted 9 lines
drivers/vhost/vhost.c +5
-1@@ -187,11 +188,29 @@ long vhost_dev_init(struct vhost_dev *de struct vhost_virtqueue *vqs, int nvqs) {struct task_struct *worker; -int i; +cpumask_var_t mask; +int i, ret = -ENOMEM ; + +if (!alloc_cpumask_var (&mask,GFP_KERNEL )) + goto out_free_mask;
I think this is another bug in the error path. You should simply do a return instead of a goto here when aloc_cpu_mask fails.
Thanks
Sridhar
Quoted 43 lines
drivers/vhost/vhost.c +24
-4worker =kthread_create (vhost_worker, dev,"vhost-%d" , current->pid); -if (IS_ERR (worker)) -return PTR_ERR (worker); +if (IS_ERR (worker)) { + ret =PTR_ERR (worker); + goto out_free_mask; + } + + ret =sched_getaffinity (current->pid, mask); +if (ret) + goto out_stop_worker; + + ret =sched_setaffinity (worker->pid, mask); +if (ret) + goto out_stop_worker; + + ret =cgroup_attach_task_current_cg (worker); +if (ret) + goto out_stop_worker; dev->vqs = vqs; dev->nvqs = nvqs;@@ -214,7 +233,14 @@ long vhost_dev_init(struct vhost_dev *de }wake_up_process (worker);/* avoid contributing to loadavg */ -return 0 ; + ret =0 ; + goto out_free_mask; + +out_stop_worker: +kthread_stop (worker); +out_free_mask: +free_cpumask_var (mask); +return ret; }/* Caller should have device mutex */ --To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Tejun Heo
maintainerRe: [PATCH 3/3] vhost: apply cpumask and cgroup to vhost workers
Quoted 9 lines · Sridhar Samudrala
drivers/vhost/vhost.c long vhost_dev_init(struct vhost_dev *de · line 195
Quoted 6 lines
drivers/vhost/vhost.c +5
-1-int i; +cpumask_var_t mask; +int i, ret = -ENOMEM ; + +if (!alloc_cpumask_var (&mask,GFP_KERNEL )) + goto out_free_mask;I think this is another bug in the error path. You should simply do a return instead of a goto here when
aloc_cpu_maskfails.
Oh... it's always safe to call free_cpumask_var() after failed alloc_cpumask_var(), so that part isn't broken.
Thanks.
Signature · 1 line
tejun
Re: [PATCH 1/3] vhost: replace vhost_workqueue with per-vhost kthread
In reply to Michael S. TsirkinQuoted 7 lines · Michael S. Tsirkin
Quoted 3 lines · Tejun Heo
Replace
vhost_workqueuewith per-vhost kthread. Other than callback argument change from structwork_struct* to structvhost_poll*, there's no visible change to vhost_poll_*() interface.I would prefer a substructure
vhost_work, even just to make the code easier to review and compare toworkqueue.c.
Either way this plays out, the rcu_dereference_check() calls will need to be adjusted to reflect the change.
Thanx, Paul
Quoted 269 lines
Quoted 7 lines
This conversion is to make each vhost use a dedicated kthread so that resource control via cgroup can be applied.
Partially based on Sridhar Samudrala's patch.
Cc Michael S. Tsirkin <[email protected]>Cc Sridhar Samudrala <[email protected]>Quoted 7 lines
---
Okay, here is three patch series to convert vhost to use per-vhost kthread, addcgroup_attach_task_current_cg()and apply it to the vhost kthreads. The conversion is mostly straight forward although flush is slightly tricky.The problem is that I have no idea how to test this.
It's a 3 step process:
1.
Install qemu-kvm under fc13, or build recent one from source, get it from here:git://git.kernel.org/pub/scm/virt/kvm/qemu-kvm.git2. install guest under it:
qemu-img create -f qcow2 disk.qcow2 100G
Now get some image (e.g. fedora 13 in fc13.iso)
and install guest:
qemu-kvm -enable-kvm -m 1G -cdrom fc13.iso -drive file=disk.qcow23. set up networking. I usually simply do host to guest on a special subnet for testing purposes:
Set up a bridge named mstbr0:
ifconfig mstbr0 down
brctl delbr mstbr0
brctl addbr mstbr0
brctl setfd mstbr0 0
ifconfig mstbr0 11.0.0.1cat > ifup << EOF
#!/bin/sh -x/sbin/ifconfigmsttap0 0.0.0.0 up
brctl addif mstbr0 msttap0
EOFqemu-kvm -enable-kvm -m 1G -cdrom fc13.iso -drive file=disk.qcow2
-net nic,model=virtio,netdev=foo -netdev
tap,id=foo,ifname=msttap0,script=/home/mst/ifup,downscript=no,vhost=onafter you set up the guest, log into it and
ifconfig eth0 11.0.0.2You should now be able to ping guest to host and back. Use something like netperf to stress the connection. Close qemu with kill -9 and unload module to test flushing code.
Quoted 4 lines
Index:
work/drivers/vhost/vhost.c
===================================================================drivers/vhost/vhost.c +0
-0...
Quoted 22 lines
drivers/vhost/vhost.c +18
-0@@ -125,10 +139,50 @@ static void vhost_vq_reset(struct vhost_ vq->log_ctx =NULL ; } +static int vhost_poller (void *data) +{ +struct vhost_dev *dev = data; +struct vhost_poll *poll; + +repeat: +set_current_state (TASK_INTERRUPTIBLE );/* mb paired w/ kthread_stop */ + +if (kthread_should_stop ()) { +__set_current_state (TASK_RUNNING ); +return 0 ; + } + + poll =NULL ; +spin_lock (&dev->poller_lock); +if (!list_empty (&dev->poll_list)) { + poll =list_first_entry (&dev->poll_list, + struct vhost_poll, node);drivers/vhost/vhost.c static void vhost_vq_reset(struct vhost_ · line 169
Quoted 10 lines
drivers/vhost/vhost.c +10
-0+list_del_init (&poll->node); + } +spin_unlock (&dev->poller_lock); + +if (poll) { +__set_current_state (TASK_RUNNING ); + poll->fn (poll); +smp_wmb ();/* paired with rmb in vhost_poll_flush() */ + poll->done_seq = poll->queue_seq; +wake_up_all (&poll->done);This seems to add wakeups on data path, which uses spinlocks etc. OTOH
workqueue.cadds a special barrier
entry which only does a wakeup when needed.
Right?Quoted 54 lines
drivers/vhost/vhost.c +18
-4+ }else +schedule (); + + goto repeat; +} +long vhost_dev_init (struct vhost_dev *dev,struct vhost_virtqueue *vqs,int nvqs) { +struct task_struct *poller;int i; + + poller =kthread_create (vhost_poller, dev,"vhost-%d" , current->pid); +if (IS_ERR (poller)) +return PTR_ERR (poller); + dev->vqs = vqs; dev->nvqs = nvqs;mutex_init (&dev->mutex);@@ -136,6 +190,9 @@ long vhost_dev_init(struct vhost_dev *de dev->log_file =NULL ; dev->memory =NULL ; dev->mm =NULL ; +spin_lock_init (&dev->poller_lock); +INIT_LIST_HEAD (&dev->poll_list); + dev->poller = poller;for (i =0 ; i < dev->nvqs; ++i) { dev->vqs[i].dev = dev;@@ -143,8 +200,7 @@ long vhost_dev_init(struct vhost_dev *devhost_vq_reset (dev, dev->vqs + i);if (dev->vqs[i].handle_kick)vhost_poll_init (&dev->vqs[i].poll, - dev->vqs[i].handle_kick, -POLLIN ); + dev->vqs[i].handle_kick,POLLIN , dev); }return 0 ; }@@ -217,6 +273,8 @@ void vhost_dev_cleanup(struct vhost_devif (dev->mm)mmput (dev->mm); dev->mm =NULL ; + +kthread_stop (dev->poller); }static int log_access_ok (void __user *log_base,u64 addr,unsigned long sz)@@ -1113,16 +1171,3 @@ void vhost_disable_notify(struct vhost_vvq_err (vq,"Failed to enable notification at %p: %d\n" , &vq->used->flags, r); } - -int vhost_init (void )drivers/vhost/vhost.c void vhost_disable_notify(struct vhost_v · line 1127
Quoted 10 lines
drivers/vhost/vhost.c +0
-10-{ - vhost_workqueue =create_singlethread_workqueue ("vhost" ); -if (!vhost_workqueue) -return -ENOMEM ; -return 0 ; -} - -void vhost_cleanup (void ) -{ -destroy_workqueue (vhost_workqueue);I note that
destroy_workqueuedoes a flush,kthread_stopdoesn't. Right? Sure we don't need to check nothing is in one of the lists? Maybe add aBUG_ON?Quoted 80 lines
drivers/vhost/vhost.c +0
-1-}Index:
work/drivers/vhost/vhost.h
===================================================================drivers/vhost/vhost.h +19
-10@@ -5,7 +5,6 @@#include <linux/vhost.h> #include <linux/mm.h> #include <linux/mutex.h> -#include <linux/workqueue.h> #include <linux/poll.h> #include <linux/file.h> #include <linux/skbuff.h> @@ -20,19 +19,26 @@ enum { VHOST_NET_MAX_SG = MAX_SKB_FRAGS + 2, }; +struct vhost_poll ; +typedef void (*vhost_poll_fn_t )(struct vhost_poll *poll); +/* Poll a file (eventfd or socket) */ /* Note: there's nothing vhost specific about this structure. */ struct vhost_poll { +vhost_poll_fn_t fn;poll_table table;wait_queue_head_t *wqh;wait_queue_t wait; -/* struct which will handle all actual work. */ -struct work_struct work; +struct list_head node; +wait_queue_head_t done;unsigned long mask; +struct vhost_dev *dev; +int queue_seq; +int done_seq; }; -void vhost_poll_init (struct vhost_poll *poll,work_func_t func, -unsigned long mask); +void vhost_poll_init (struct vhost_poll *poll,vhost_poll_fn_t fn, +unsigned long mask,struct vhost_dev *dev);void vhost_poll_start (struct vhost_poll *poll,struct file *file);void vhost_poll_stop (struct vhost_poll *poll);void vhost_poll_flush (struct vhost_poll *poll);@@ -63,7 +69,7 @@ struct vhost_virtqueue {struct vhost_poll poll;/* The routine to call when the Guest pings us, or timeout. */ -work_func_t handle_kick; +vhost_poll_fn_t handle_kick;/* Last available index we saw. */ u16 last_avail_idx;@@ -86,11 +92,11 @@ struct vhost_virtqueue {struct iovec hdr[VHOST_NET_MAX_SG ];size_t hdr_size;/* We use a kind of RCU to access private pointer. -* All readers access it from workqueue, which makes it possible to -* flush the workqueue instead of synchronize_rcu. Therefore readers do +* All readers access it from poller, which makes it possible to +* flush the vhost_poll instead of synchronize_rcu. Therefore readers do * not need to call rcu_read_lock/rcu_read_unlock: the beginning of -* work item execution acts instead of rcu_read_lock() and the end of -* work item execution acts instead of rcu_read_lock(). +* vhost_poll execution acts instead of rcu_read_lock() and the end of +* vhost_poll execution acts instead of rcu_read_lock(). * Writers use virtqueue mutex. */ void *private_data;/* Log write descriptors */ @@ -110,6 +116,9 @@ struct vhost_dev {int nvqs;struct file *log_file;struct eventfd_ctx *log_ctx; +spinlock_t poller_lock; +struct list_head poll_list; +struct task_struct *poller; };long vhost_dev_init (struct vhost_dev *,struct vhost_virtqueue *vqs,int nvqs);drivers/vhost/vhost.h bool vhost_enable_notify(struct vhost_vi · line 150
Quoted 9 lines
drivers/vhost/vhost.h +0
-3@@ -136,9 +145,6 @@ bool vhost_enable_notify(struct vhost_viint vhost_log_write (struct vhost_virtqueue *vq,struct vhost_log *log,unsigned int log_num,u64 len); -int vhost_init (void ); -void vhost_cleanup (void ); -#define vq_err (vq, fmt, ...) do { \ pr_debug(pr_fmt(fmt), ##__VA_ARGS__); \ if ((vq)->error_ctx) \--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Tejun Heo
maintainerPATCH 2/3 cgroups: Add an API to attach a task to current task's cgroup
In reply to Michael S. TsirkinFrom: Sridhar Samudrala <[email protected]>
Add a new kernel API to attach a task to current task's cgroup in all the active hierarchies.
--- include/linux/cgroup.h | 1 + kernel/cgroup.c | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+)
include/linux/cgroup.h +1 -0
@@ -570,6 +570,7 @@ struct task_struct *cgroup_iter_next(strvoid cgroup_iter_end (struct cgroup *cgrp,struct cgroup_iter *it);int cgroup_scan_tasks (struct cgroup_scanner *scan);int cgroup_attach_task (struct cgroup *,struct task_struct *); +int cgroup_attach_task_current_cg (struct task_struct *);/* * CSS ID is ID for cgroup_subsys_state structs under subsys. This only works
kernel/cgroup.c +23 -0
@@ -1788,6 +1788,29 @@ out:return retval; } +/** +* cgroup_attach_task_current_cg - attach task 'tsk' to current task's cgroup +* @tsk: the task to be attached +*/ +int cgroup_attach_task_current_cg (struct task_struct *tsk) +{ +struct cgroupfs_root *root; +struct cgroup *cur_cg; +int retval =0 ; + +cgroup_lock (); +for_each_active_root (root) { + cur_cg =task_cgroup_from_root (current, root); + retval =cgroup_attach_task (cur_cg, tsk); +if (retval) +break ; + } +cgroup_unlock (); + +return retval; +} +EXPORT_SYMBOL_GPL (cgroup_attach_task_current_cg); +/* * Attach task with pid 'pid' to cgroup 'cgrp'. Call with cgroup_mutex * held. May take task_lock of task
Re: [PATCH 2/3] cgroups: Add an API to attach a task to current task's cgroup
Quoted 6 lines · Tejun Heo
From: Sridhar Samudrala <[email protected]>
Add a new kernel API to attach a task to current task's cgroup in all the active hierarchies.
Signed-off-by Sridhar Samudrala <[email protected]>
btw: you lost the reviewed-by tag given by Paul Menage.
Tejun Heo
maintainerRe: [PATCH 2/3] cgroups: Add an API to attach a task to current task's cgroup
Quoted 11 lines · Li Zefan
Quoted 6 lines · Tejun Heo
From: Sridhar Samudrala <[email protected]>
Add a new kernel API to attach a task to current task's cgroup in all the active hierarchies.
Signed-off-by Sridhar Samudrala <[email protected]>Acked-by Li Zefan <[email protected]>btw: you lost the reviewed-by tag given by Paul Menage.
I only got bounced the original posting. Michael, can you please add it if/when you commit these?
Thank you.
Signature · 1 line
tejun
Tejun Heo
maintainerPATCH 3/3 vhost: apply cpumask and cgroup to vhost pollers
In reply to Michael S. TsirkinApply the cpumask and cgroup of the initializing task to the created vhost poller.
Based on Sridhar Samudrala's patch.
--- drivers/vhost/vhost.c | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-)
drivers/vhost/vhost.c +31 -5
@@ -23,6 +23,7 @@#include <linux/highmem.h> #include <linux/slab.h> #include <linux/kthread.h> +#include <linux/cgroup.h> #include <linux/net.h> #include <linux/if_packet.h>
@@ -176,12 +177,30 @@ repeat:long vhost_dev_init (struct vhost_dev *dev,struct vhost_virtqueue *vqs,int nvqs) { -struct task_struct *poller; -int i; +struct task_struct *poller =NULL ; +cpumask_var_t mask; +int i, ret = -ENOMEM ; + +if (!alloc_cpumask_var (&mask,GFP_KERNEL )) + goto out; poller =kthread_create (vhost_poller, dev,"vhost-%d" , current->pid); -if (IS_ERR (poller)) -return PTR_ERR (poller); +if (IS_ERR (poller)) { + ret =PTR_ERR (poller); + goto out; + } + + ret =sched_getaffinity (current->pid, mask); +if (ret) + goto out; + + ret =sched_setaffinity (poller->pid, mask); +if (ret) + goto out; + + ret =cgroup_attach_task_current_cg (poller); +if (ret) + goto out; dev->vqs = vqs; dev->nvqs = nvqs;
@@ -202,7 +221,14 @@ long vhost_dev_init(struct vhost_dev *devhost_poll_init (&dev->vqs[i].poll, dev->vqs[i].handle_kick,POLLIN , dev); } -return 0 ; + +wake_up_process (poller);/* avoid contributing to loadavg */ + ret =0 ; +out: +if (ret) +kthread_stop (poller); +free_cpumask_var (mask); +return ret; }/* Caller should have device mutex */
Re: [PATCH 3/3] vhost: apply cpumask and cgroup to vhost pollers
Quoted 26 lines · Tejun Heo
Apply the cpumask and cgroup of the initializing task to the created vhost poller.
Based on Sridhar Samudrala's patch.
Cc Michael S. Tsirkin <[email protected]>Cc Sridhar Samudrala <[email protected]>--- drivers/vhost/vhost.c | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-)Index:
work/drivers/vhost/vhost.c
===================================================================drivers/vhost/vhost.c +1
-0@@ -23,6 +23,7 @@#include <linux/highmem.h> #include <linux/slab.h> #include <linux/kthread.h> +#include <linux/cgroup.h> #include <linux/net.h> #include <linux/if_packet.h> @@ -176,12 +177,30 @@ repeat:long vhost_dev_init (struct vhost_dev *dev,struct vhost_virtqueue *vqs,int nvqs)
drivers/vhost/vhost.c repeat: · line 186
Quoted 10 lines
drivers/vhost/vhost.c +6
-2{ -struct task_struct *poller; -int i; +struct task_struct *poller =NULL ; +cpumask_var_t mask; +int i, ret = -ENOMEM ; + +if (!alloc_cpumask_var (&mask,GFP_KERNEL )) + goto out;
If we "goto out", we will end up calling kthread_stop(poller), but seems kthread_stop() requires the task_struct pointer != NULL.
Quoted 38 lines
drivers/vhost/vhost.c +24
-3poller =kthread_create (vhost_poller, dev,"vhost-%d" , current->pid); -if (IS_ERR (poller)) -return PTR_ERR (poller); +if (IS_ERR (poller)) { + ret =PTR_ERR (poller); + goto out; + } + + ret =sched_getaffinity (current->pid, mask); +if (ret) + goto out; + + ret =sched_setaffinity (poller->pid, mask); +if (ret) + goto out; + + ret =cgroup_attach_task_current_cg (poller); +if (ret) + goto out; dev->vqs = vqs; dev->nvqs = nvqs;@@ -202,7 +221,14 @@ long vhost_dev_init(struct vhost_dev *devhost_poll_init (&dev->vqs[i].poll, dev->vqs[i].handle_kick,POLLIN , dev); } -return 0 ; + +wake_up_process (poller);/* avoid contributing to loadavg */ + ret =0 ; +out: +if (ret) +kthread_stop (poller); +free_cpumask_var (mask); +return ret; }/* Caller should have device mutex */
Tejun Heo
maintainerPATCH 3/3 vhost: apply cpumask and cgroup to vhost pollers
Apply the cpumask and cgroup of the initializing task to the created vhost poller.
Based on Sridhar Samudrala's patch. Li Zefan spotted a bug in error path, fixed.
---
Updated accordingly. Thanks.
drivers/vhost/vhost.c | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-)
drivers/vhost/vhost.c +31 -5
@@ -23,6 +23,7 @@#include <linux/highmem.h> #include <linux/slab.h> #include <linux/kthread.h> +#include <linux/cgroup.h> #include <linux/net.h> #include <linux/if_packet.h>
@@ -176,12 +177,30 @@ repeat:long vhost_dev_init (struct vhost_dev *dev,struct vhost_virtqueue *vqs,int nvqs) { -struct task_struct *poller; -int i; +struct task_struct *poller =NULL ; +cpumask_var_t mask; +int i, ret = -ENOMEM ; + +if (!alloc_cpumask_var (&mask,GFP_KERNEL )) + goto out; poller =kthread_create (vhost_poller, dev,"vhost-%d" , current->pid); -if (IS_ERR (poller)) -return PTR_ERR (poller); +if (IS_ERR (poller)) { + ret =PTR_ERR (poller); + goto out; + } + + ret =sched_getaffinity (current->pid, mask); +if (ret) + goto out; + + ret =sched_setaffinity (poller->pid, mask); +if (ret) + goto out; + + ret =cgroup_attach_task_current_cg (poller); +if (ret) + goto out; dev->vqs = vqs; dev->nvqs = nvqs;
@@ -202,7 +221,14 @@ long vhost_dev_init(struct vhost_dev *devhost_poll_init (&dev->vqs[i].poll, dev->vqs[i].handle_kick,POLLIN , dev); } -return 0 ; + +wake_up_process (poller);/* avoid contributing to loadavg */ + ret =0 ; +out: +if (ret && poller) +kthread_stop (poller); +free_cpumask_var (mask); +return ret; }/* Caller should have device mutex */
Re: [PATCH UPDATED 3/3] vhost: apply cpumask and cgroup to vhost pollers
drivers/vhost/vhost.c repeat: · line 190
Quoted 6 lines
drivers/vhost/vhost.c +3
-2poller =kthread_create (vhost_poller, dev,"vhost-%d" , current->pid); -if (IS_ERR (poller)) -return PTR_ERR (poller); +if (IS_ERR (poller)) { + ret =PTR_ERR (poller); + goto out;
here...
Quoted 17 lines
drivers/vhost/vhost.c +13
-0+ } + + ret =sched_getaffinity (current->pid, mask); +if (ret) + goto out; + + ret =sched_setaffinity (poller->pid, mask); +if (ret) + goto out; + + ret =cgroup_attach_task_current_cg (poller); +if (ret) + goto out; dev->vqs = vqs; dev->nvqs = nvqs;
drivers/vhost/vhost.c long vhost_dev_init(struct vhost_dev *de · line 228
Quoted 9 lines
drivers/vhost/vhost.c +5
-1@@ -202,7 +221,14 @@ long vhost_dev_init(struct vhost_dev *devhost_poll_init (&dev->vqs[i].poll, dev->vqs[i].handle_kick,POLLIN , dev); } -return 0 ; + +wake_up_process (poller);/* avoid contributing to loadavg */ + ret =0 ; +out: +if (ret && poller)
It's still buggy..
Quoted 8 lines
drivers/vhost/vhost.c +3
-0+kthread_stop (poller); +free_cpumask_var (mask); +return ret; }/* Caller should have device mutex */
Tejun Heo
maintainerRe: [PATCH UPDATED2 3/3] vhost: apply cpumask and cgroup to vhost pollers
Apply the cpumask and cgroup of the initializing task to the created vhost poller.
Based on Sridhar Samudrala's patch. Li Zefan spotted a bug in error path (twice), fixed (twice).
---
Heh... that's embarrassing. Let's see if I can get it right the third time.
Thank you.
drivers/vhost/vhost.c | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-)
drivers/vhost/vhost.c +32 -4
@@ -23,6 +23,7 @@#include <linux/highmem.h> #include <linux/slab.h> #include <linux/kthread.h> +#include <linux/cgroup.h> #include <linux/net.h> #include <linux/if_packet.h>
@@ -177,11 +178,29 @@ long vhost_dev_init(struct vhost_dev *de struct vhost_virtqueue *vqs, int nvqs) {struct task_struct *poller; -int i; +cpumask_var_t mask; +int i, ret = -ENOMEM ; + +if (!alloc_cpumask_var (&mask,GFP_KERNEL )) + goto out_free_mask; poller =kthread_create (vhost_poller, dev,"vhost-%d" , current->pid); -if (IS_ERR (poller)) -return PTR_ERR (poller); +if (IS_ERR (poller)) { + ret =PTR_ERR (poller); + goto out_free_mask; + } + + ret =sched_getaffinity (current->pid, mask); +if (ret) + goto out_stop_poller; + + ret =sched_setaffinity (poller->pid, mask); +if (ret) + goto out_stop_poller; + + ret =cgroup_attach_task_current_cg (poller); +if (ret) + goto out_stop_poller; dev->vqs = vqs; dev->nvqs = nvqs;
@@ -202,7 +221,16 @@ long vhost_dev_init(struct vhost_dev *devhost_poll_init (&dev->vqs[i].poll, dev->vqs[i].handle_kick,POLLIN , dev); } -return 0 ; + +wake_up_process (poller);/* avoid contributing to loadavg */ + ret =0 ; + goto out_free_mask; + +out_stop_poller: +kthread_stop (poller); +out_free_mask: +free_cpumask_var (mask); +return ret; }/* Caller should have device mutex */
Michael S. Tsirkin
maintainerRe: [PATCH 3/3] vhost: apply cpumask and cgroup to vhost pollers
In reply to Tejun HeoQuoted 7 lines · Tejun Heo
Apply the cpumask and cgroup of the initializing task to the created vhost poller.
Based on Sridhar Samudrala's patch.
Cc Michael S. Tsirkin <[email protected]>Cc Sridhar Samudrala <[email protected]>
I wanted to apply this, but modpost fails:
ERROR: "sched_setaffinity" [drivers/vhost/vhost_net.ko] undefined!
ERROR: "sched_getaffinity" [drivers/vhost/vhost_net.ko] undefined!
Did you try building as a module?
Quoted 67 lines
--- drivers/vhost/vhost.c | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-)Index:
work/drivers/vhost/vhost.c
===================================================================drivers/vhost/vhost.c +31
-5@@ -23,6 +23,7 @@#include <linux/highmem.h> #include <linux/slab.h> #include <linux/kthread.h> +#include <linux/cgroup.h> #include <linux/net.h> #include <linux/if_packet.h> @@ -176,12 +177,30 @@ repeat:long vhost_dev_init (struct vhost_dev *dev,struct vhost_virtqueue *vqs,int nvqs) { -struct task_struct *poller; -int i; +struct task_struct *poller =NULL ; +cpumask_var_t mask; +int i, ret = -ENOMEM ; + +if (!alloc_cpumask_var (&mask,GFP_KERNEL )) + goto out; poller =kthread_create (vhost_poller, dev,"vhost-%d" , current->pid); -if (IS_ERR (poller)) -return PTR_ERR (poller); +if (IS_ERR (poller)) { + ret =PTR_ERR (poller); + goto out; + } + + ret =sched_getaffinity (current->pid, mask); +if (ret) + goto out; + + ret =sched_setaffinity (poller->pid, mask); +if (ret) + goto out; + + ret =cgroup_attach_task_current_cg (poller); +if (ret) + goto out; dev->vqs = vqs; dev->nvqs = nvqs;@@ -202,7 +221,14 @@ long vhost_dev_init(struct vhost_dev *devhost_poll_init (&dev->vqs[i].poll, dev->vqs[i].handle_kick,POLLIN , dev); } -return 0 ; + +wake_up_process (poller);/* avoid contributing to loadavg */ + ret =0 ; +out: +if (ret) +kthread_stop (poller); +free_cpumask_var (mask); +return ret; }/* Caller should have device mutex */
Re: [PATCH 3/3] vhost: apply cpumask and cgroup to vhost pollers
Quoted 10 lines · Michael S. Tsirkin
Quoted 7 lines · Tejun Heo
Apply the cpumask and cgroup of the initializing task to the created vhost poller.
Based on Sridhar Samudrala's patch.
Cc Michael S. Tsirkin <[email protected]>Cc Sridhar Samudrala <[email protected]>
Quoted 5 lines
I wanted to apply this, but modpost fails:
ERROR: "sched_setaffinity" [drivers/vhost/vhost_net.ko] undefined!
ERROR: "sched_getaffinity" [drivers/vhost/vhost_net.ko] undefined!Did you try building as a module?
In my original implementation, i had these calls in workqueue.c. Now that these are moved to vhost.c which can be built as a module, these symbols need to be exported.
The following patch fixes the build issue with vhost as a module.
kernel/sched.c +1 -0
@@ -4837,6 +4837,7 @@ out_put_task:put_online_cpus ();return retval; } +EXPORT_SYMBOL_GPL (sched_setaffinity); static int get_user_cpu_mask(unsigned long __user *user_mask_ptr,
unsigned len,
struct cpumask *new_mask)
@@ -4900,6 +4901,7 @@ out_unlock:
return retval;
}
+EXPORT_SYMBOL_GPL(sched_getaffinity);
/**
* sys_sched_getaffinity - get the cpu affinity of a process
Quoted 71 lines
Quoted 57 lines
--- drivers/vhost/vhost.c | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-)Index:
work/drivers/vhost/vhost.c
===================================================================drivers/vhost/vhost.c +24
-5@@ -23,6 +23,7 @@#include <linux/highmem.h> #include <linux/slab.h> #include <linux/kthread.h> +#include <linux/cgroup.h> #include <linux/net.h> #include <linux/if_packet.h> @@ -176,12 +177,30 @@ repeat:long vhost_dev_init (struct vhost_dev *dev,struct vhost_virtqueue *vqs,int nvqs) { -struct task_struct *poller; -int i; +struct task_struct *poller =NULL ; +cpumask_var_t mask; +int i, ret = -ENOMEM ; + +if (!alloc_cpumask_var (&mask,GFP_KERNEL )) + goto out; poller =kthread_create (vhost_poller, dev,"vhost-%d" , current->pid); -if (IS_ERR (poller)) -return PTR_ERR (poller); +if (IS_ERR (poller)) { + ret =PTR_ERR (poller); + goto out; + } + + ret =sched_getaffinity (current->pid, mask); +if (ret) + goto out; + + ret =sched_setaffinity (poller->pid, mask); +if (ret) + goto out; + + ret =cgroup_attach_task_current_cg (poller); +if (ret) + goto out; dev->vqs = vqs; dev->nvqs = nvqs;@@ -202,7 +221,14 @@ long vhost_dev_init(struct vhost_dev *devhost_poll_init (&dev->vqs[i].poll, dev->vqs[i].handle_kick,POLLIN , dev); } -return 0 ; +drivers/vhost/vhost.c long vhost_dev_init(struct vhost_dev *de · line 234
Quoted 10 lines
drivers/vhost/vhost.c +7
-0+wake_up_process (poller);/* avoid contributing to loadavg */ + ret =0 ; +out: +if (ret) +kthread_stop (poller); +free_cpumask_var (mask); +return ret; }/* Caller should have device mutex */ --
To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Michael S. Tsirkin
maintainerPATCH 1/1 sched: export sched_set/getaffinity (was Re: [PATCH 3/3] vhost: apply cpumask and cgroup to vhost pollers)
Quoted 15 lines · Sridhar Samudrala
Quoted 14 lines · Michael S. Tsirkin
Quoted 7 lines · Tejun Heo
Apply the cpumask and cgroup of the initializing task to the created vhost poller.
Based on Sridhar Samudrala's patch.
Cc Michael S. Tsirkin <[email protected]>Cc Sridhar Samudrala <[email protected]>I wanted to apply this, but modpost fails:
ERROR: "sched_setaffinity" [drivers/vhost/vhost_net.ko] undefined!
ERROR: "sched_getaffinity" [drivers/vhost/vhost_net.ko] undefined!
Quoted 8 lines
Quoted 1 line
Did you try building as a module?
In my original implementation, i had these calls in
workqueue.c. Now that these are moved tovhost.cwhich can be built as a module, these symbols need to be exported.
The following patch fixes the build issue with vhost as a module.Signed-off-by Sridhar Samudrala <[email protected]>
Works for me. To simplify dependencies, I'd like to queue this together with the chost patches through net-next. Ack to this?
Quoted 94 lines
kernel/sched.c +1
-0@@ -4837,6 +4837,7 @@ out_put_task:put_online_cpus ();return retval; } +EXPORT_SYMBOL_GPL (sched_setaffinity); static int get_user_cpu_mask(unsigned long __user *user_mask_ptr, unsigned len,kernel/sched.c +1
-0struct cpumask *new_mask)@@ -4900,6 +4901,7 @@ out_unlock:return retval; } +EXPORT_SYMBOL_GPL (sched_getaffinity);/** * sys_sched_getaffinity - get the cpu affinity of a process Quoted 73 lines
Quoted 59 lines
--- drivers/vhost/vhost.c | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-)Index:
work/drivers/vhost/vhost.c
===================================================================drivers/vhost/vhost.c +24
-5@@ -23,6 +23,7 @@#include <linux/highmem.h> #include <linux/slab.h> #include <linux/kthread.h> +#include <linux/cgroup.h> #include <linux/net.h> #include <linux/if_packet.h> @@ -176,12 +177,30 @@ repeat:long vhost_dev_init (struct vhost_dev *dev,struct vhost_virtqueue *vqs,int nvqs) { -struct task_struct *poller; -int i; +struct task_struct *poller =NULL ; +cpumask_var_t mask; +int i, ret = -ENOMEM ; + +if (!alloc_cpumask_var (&mask,GFP_KERNEL )) + goto out; poller =kthread_create (vhost_poller, dev,"vhost-%d" , current->pid); -if (IS_ERR (poller)) -return PTR_ERR (poller); +if (IS_ERR (poller)) { + ret =PTR_ERR (poller); + goto out; + } + + ret =sched_getaffinity (current->pid, mask); +if (ret) + goto out; + + ret =sched_setaffinity (poller->pid, mask); +if (ret) + goto out; + + ret =cgroup_attach_task_current_cg (poller); +if (ret) + goto out; dev->vqs = vqs; dev->nvqs = nvqs;@@ -202,7 +221,14 @@ long vhost_dev_init(struct vhost_dev *devhost_poll_init (&dev->vqs[i].poll, dev->vqs[i].handle_kick,POLLIN , dev); } -return 0 ; +drivers/vhost/vhost.c long vhost_dev_init(struct vhost_dev *de · line 234
Quoted 10 lines
drivers/vhost/vhost.c +7
-0+wake_up_process (poller);/* avoid contributing to loadavg */ + ret =0 ; +out: +if (ret) +kthread_stop (poller); +free_cpumask_var (mask); +return ret; }/* Caller should have device mutex */ --
To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Michael S. Tsirkin
maintainerPATCH 1/1 sched: export sched_set/getaffinity to modules
Author: Sridhar Samudrala <[email protected]>
sched: export sched_set/getaffinity to modules
vhost-net driver wants to copy the affinity from the owner thread to thread it creates. Export sched_set/get affinity to modules to make this possible when vhost is built as a module.
---
I'm not sure the previous time made it clear what exactly is the proposed change, so reposting. Info, Peter, could you ack merging the following through the net-next tree please?
kernel/sched.c +2 -0
@@ -4744,6 +4744,7 @@ out_put_task:put_online_cpus ();return retval; } +EXPORT_SYMBOL_GPL (sched_setaffinity);static int get_user_cpu_mask (unsigned long __user *user_mask_ptr,unsigned len,struct cpumask *new_mask)
@@ -4807,6 +4808,7 @@ out_unlock:return retval; } +EXPORT_SYMBOL_GPL (sched_getaffinity);/** * sys_sched_getaffinity - get the cpu affinity of a process
Re: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 31 lines · Michael S. Tsirkin
Author: Sridhar Samudrala <[email protected]>
sched: export sched_set/getaffinity to modules
vhost-net driver wants to copy the affinity from the owner thread to thread it creates. Export sched_set/get affinity to modules to make this possible when vhost is built as a module.
Signed-off-by Sridhar Samudrala <[email protected]>Signed-off-by Michael S. Tsirkin <[email protected]>---
I'm not sure the previous time made it clear what exactly is the proposed change, so reposting. Info, Peter, could you ack merging the following through the net-next tree please?
kernel/sched.c +1
-0@@ -4744,6 +4744,7 @@ out_put_task:put_online_cpus ();return retval; } +EXPORT_SYMBOL_GPL (sched_setaffinity);static int get_user_cpu_mask (unsigned long __user *user_mask_ptr,unsigned len,struct cpumask *new_mask)
kernel/sched.c out_unlock: · line 4814
Quoted 7 lines
kernel/sched.c +1
-0@@ -4807,6 +4808,7 @@ out_unlock:return retval; } +EXPORT_SYMBOL_GPL (sched_getaffinity);/** * sys_sched_getaffinity - get the cpu affinity of a process
Urgh,.. so why again is that a good idea?
Re: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 11 lines · Peter Zijlstra
Quoted 8 lines · Michael S. Tsirkin
Author: Sridhar Samudrala <[email protected]>
sched: export sched_set/getaffinity to modules
vhost-net driver wants to copy the affinity from the owner thread to thread it creates. Export sched_set/get affinity to modules to make this possible when vhost is built as a module.
Urgh,.. so why again is that a good idea?
In particular:
- who sets the affinity of the task?
- why can't it set the kernel thread's affinity too?
- what happens if someone changes the tasks' affinity?
So no, I don't think this is a sensible thing to do at all.
Michael S. Tsirkin
maintainerRe: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 11 lines · Peter Zijlstra
Quoted 9 lines · Peter Zijlstra
Quoted 8 lines · Michael S. Tsirkin
Author: Sridhar Samudrala <[email protected]>
sched: export sched_set/getaffinity to modules
vhost-net driver wants to copy the affinity from the owner thread to thread it creates. Export sched_set/get affinity to modules to make this possible when vhost is built as a module.
Quoted 4 lines
Quoted 1 line
Urgh,.. so why again is that a good idea?
In particular:
- who sets the affinity of the task?
management tools do this when they start qemu.
Quoted 1 line
- why can't it set the kernel thread's affinity too?
It can. However: the threads are started internally by the driver when qemu does an ioctl. What we want to do is give it a sensible default affinity. management tool can later tweak it if it wants to.
Quoted 1 line
- what happens if someone changes the tasks' affinity?
We would normally create a cgroup including all internal tasks, making it easy to find and change affinity for them all if necessary.
Quoted 1 line
So no, I don't think this is a sensible thing to do at all.
Michael S. Tsirkin
maintainerRe: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 25 lines · Michael S. Tsirkin
Quoted 11 lines · Peter Zijlstra
Quoted 9 lines · Peter Zijlstra
Quoted 8 lines · Michael S. Tsirkin
Author: Sridhar Samudrala <[email protected]>
sched: export sched_set/getaffinity to modules
vhost-net driver wants to copy the affinity from the owner thread to thread it creates. Export sched_set/get affinity to modules to make this possible when vhost is built as a module.
Quoted 4 lines
Quoted 1 line
Urgh,.. so why again is that a good idea?
In particular:
- who sets the affinity of the task?management tools do this when they start qemu.
Quoted 1 line
- why can't it set the kernel thread's affinity too?
It can. However: the threads are started internally by the driver when qemu does an ioctl. What we want to do is give it a sensible default affinity. management tool can later tweak it if it wants to.
Quoted 7 lines
Quoted 1 line
- what happens if someone changes the tasks' affinity?
We would normally create a cgroup including all internal tasks, making it easy to find and change affinity for them all if necessary.
Quoted 1 line
So no, I don't think this is a sensible thing to do at all.
The patch using this is here:
http://www.mail-archive.com/[email protected]/msg35411.html
It simply copies the affinity from the parent when thread is created.
Signature · 1 line
MST
Re: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 5 lines · Michael S. Tsirkin
The patch using this is here:
http://www.mail-archive.com/[email protected]/msg35411.htmlIt simply copies the affinity from the parent when thread is created.
Sounds like policy, not something the kernel should do..
Re: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 8 lines · Peter Zijlstra
Quoted 5 lines · Michael S. Tsirkin
The patch using this is here:
http://www.mail-archive.com/[email protected]/msg35411.htmlIt simply copies the affinity from the parent when thread is created.
Sounds like policy, not something the kernel should do..
The alternative would be using clone() instead of thread_create() and inherit everything from the creating task. Inheriting from kthreadd and then undoing some aspects just sounds like daft policy that really ought to be in userspace.
Michael S. Tsirkin
maintainerRe: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 6 lines · Peter Zijlstra
Quoted 5 lines · Peter Zijlstra
Quoted 4 lines · Michael S. Tsirkin
The patch using this is here:
http://www.mail-archive.com/[email protected]/msg35411.html
Quoted 8 lines
Quoted 3 lines
Quoted 1 line
It simply copies the affinity from the parent when thread is created.
Sounds like policy, not something the kernel should do..
The alternative would be using
clone()instead ofthread_create()and inherit everything from the creating task.
Inheriting from kthreadd and then undoing some aspects just sounds like daft policy that really ought to be in userspace.
Yes, that's basically what this patchset is trying to do:
create a workqueue inheriting everything from the creating task. Sridhar started with an API to do exactly this:
http://linux.derkeiler.com/Mailing-Lists/Kernel/2010-05/msg07478.html
Then we switched to raw kthread to avoid stepping on cwq toes. Maybe it makes sense to add kthread_clone (in addition to kthread_create) that would do what you suggest?
If yes, any hints on an implementation?
Signature · 1 line
MST
Re: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 21 lines · Michael S. Tsirkin
Quoted 6 lines · Peter Zijlstra
Quoted 5 lines · Peter Zijlstra
Quoted 4 lines · Michael S. Tsirkin
The patch using this is here:
http://www.mail-archive.com/[email protected]/msg35411.htmlQuoted 8 lines
Quoted 3 lines
Quoted 1 line
It simply copies the affinity from the parent when thread is created.
Sounds like policy, not something the kernel should do..
The alternative would be using
clone()instead ofthread_create()and inherit everything from the creating task.
Inheriting from kthreadd and then undoing some aspects just sounds like daft policy that really ought to be in userspace.Yes, that's basically what this patchset is trying to do:
create a workqueue inheriting everything from the creating task. Sridhar started with an API to do exactly this:
http://linux.derkeiler.com/Mailing-Lists/Kernel/2010-05/msg07478.html
Quoted 4 lines
Then we switched to raw kthread to avoid stepping on cwq toes. Maybe it makes sense to add
kthread_clone(in addition tokthread_create) that would do what you suggest?
If yes, any hints on an implementation?
I think that's called kernel_thread() see
kernel/kthread.c:create_kthread().
Doing the whole kthreadd dance and then copying bits and pieces back sounds very fragile, so yeah, something like that should work.
The other issue to consider is the thread group status of these things, I think it would be best if these threads were still considered part of the process that spawned them so that they would die nicely when the process gets whacked.
At which point one could wonder if the kthread interface makes any sense, why not let userspace fork tasks and let them call into the kernel to perform work...
Michael S. Tsirkin
maintainerRe: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 27 lines · Peter Zijlstra
Quoted 21 lines · Michael S. Tsirkin
Quoted 6 lines · Peter Zijlstra
Quoted 5 lines · Peter Zijlstra
Quoted 4 lines · Michael S. Tsirkin
The patch using this is here:
http://www.mail-archive.com/[email protected]/msg35411.htmlQuoted 8 lines
Quoted 3 lines
Quoted 1 line
It simply copies the affinity from the parent when thread is created.
Sounds like policy, not something the kernel should do..
The alternative would be using
clone()instead ofthread_create()and inherit everything from the creating task.
Inheriting from kthreadd and then undoing some aspects just sounds like daft policy that really ought to be in userspace.Yes, that's basically what this patchset is trying to do:
create a workqueue inheriting everything from the creating task. Sridhar started with an API to do exactly this:
http://linux.derkeiler.com/Mailing-Lists/Kernel/2010-05/msg07478.htmlQuoted 4 lines
Then we switched to raw kthread to avoid stepping on cwq toes. Maybe it makes sense to add
kthread_clone(in addition tokthread_create) that would do what you suggest?
If yes, any hints on an implementation?
Quoted 5 lines
I think that's called
kernel_thread()see
kernel/kthread.c:create_kthread().Doing the whole kthreadd dance and then copying bits and pieces back sounds very fragile, so yeah, something like that should work.
Thanks!
Sridhar, Tejun, have the time to look into this approach?
Quoted 4 lines
The other issue to consider is the thread group status of these things, I think it would be best if these threads were still considered part of the process that spawned them so that they would die nicely when the process gets whacked.
The proposed patch kills the thread when the fd is closed, so I think this already works without making it part of the process.
Quoted 3 lines
At which point one could wonder if the kthread interface makes any sense, why not let userspace fork tasks and let them call into the kernel to perform work...
One thing I wanted to avoid is letting userspace know just how many threads are there. We are using a single one now, but we used to have threads per-cpu, and we might switch to a thread per virtqueue in the future. IMO all this should ideally be transparent to userspace.
Signature · 1 line
MST
Re: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 4 lines · Michael S. Tsirkin
The proposed patch kills the thread when the fd is closed, so I think this already works without making it part of the process.
OK, fd bound resources are fine.
Tejun Heo
maintainerRe: [PATCH repost] sched: export sched_set/getaffinity to modules
In reply to Michael S. TsirkinHello,
Quoted 8 lines · Michael S. Tsirkin
Quoted 5 lines
I think that's called
kernel_thread()see
kernel/kthread.c:create_kthread().Doing the whole kthreadd dance and then copying bits and pieces back sounds very fragile, so yeah, something like that should work.
Thanks!
Sridhar, Tejun, have the time to look into this approach?
All that's necessary is shortcutting indirection through kthreadd. ie. An exported function which looks like the following,
struct kthread_clone_or_whatever(int (*threadfn).....)
{
struct kthread_create_info create;
int pid;
INIT create;
pid = kernel_thread(kthread, &create, CLONE_FS...);
if (pid < 0)
return ERROR;
wait_for_completion(&create.done);
if (!IS_ERR(create.result))
SET NAME;
return create.result;}
It might be a good idea to make the function take extra clone flags but anyways once created cloned task can be treated the same way as other kthreads, so nothing else needs to be changed.
Thanks.
Signature · 1 line
tejun
Re: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 19 lines · Tejun Heo
All that's necessary is shortcutting indirection through kthreadd. ie. An exported function which looks like the following,
struct
kthread_clone_or_whatever(int (*threadfn).....){ struct kthread_create_info create; int pid; INIT create; pid = kernel_thread(kthread, &create, CLONE_FS...); if (pid < 0) return ERROR; wait_for_completion(&create.done); if (!IS_ERR(create.result)) SET NAME; return create.result;
Quoted 5 lines
}
It might be a good idea to make the function take extra clone flags but anyways once created cloned task can be treated the same way as other kthreads, so nothing else needs to be changed.
This makes kthread_stop() work. Otherwise the new thread is just the CLONE_VM child of the caller, and the caller is the user-mode task doing ioctl() ?
Oleg.
Tejun Heo
maintainerRe: [PATCH repost] sched: export sched_set/getaffinity to modules
Hello,
Quoted 7 lines · Oleg Nesterov
Quoted 3 lines
It might be a good idea to make the function take extra clone flags but anyways once created cloned task can be treated the same way as other kthreads, so nothing else needs to be changed.
This makes
kthread_stop()work. Otherwise the new thread is just theCLONE_VMchild of the caller, and the caller is the user-mode task doingioctl()?
Hmmm, indeed. It makes the attribute inheritance work but circumvents the whole reason there is kthreadd.
Thanks.
Signature · 1 line
tejun
Re: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 7 lines · Tejun Heo
Hello,
Quoted 4 lines · Oleg Nesterov
Quoted 3 lines
It might be a good idea to make the function take extra clone flags but anyways once created cloned task can be treated the same way as other kthreads, so nothing else needs to be changed.
Quoted 6 lines
Quoted 3 lines
This makes
kthread_stop()work. Otherwise the new thread is just theCLONE_VMchild of the caller, and the caller is the user-mode task doingioctl()?Hmmm, indeed. It makes the attribute inheritance work but circumvents the whole reason there is kthreadd.
I thought the whole reason there was threadd was to avoid the inheritance? So avoiding the avoiding of inheritance seems like the goal here, no?
Re: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 14 lines · Peter Zijlstra
Quoted 12 lines · Tejun Heo
Hello,
Quoted 8 lines · Oleg Nesterov
Quoted 4 lines
It might be a good idea to make the function take extra clone flags but anyways once created cloned task can be treated the same way as other kthreads, so nothing else needs to be changed.
This makes
kthread_stop()work. Otherwise the new thread is just theCLONE_VMchild of the caller, and the caller is the user-mode task doingioctl()?
Quoted 7 lines
Quoted 3 lines
Hmmm, indeed. It makes the attribute inheritance work but circumvents the whole reason there is kthreadd.
I thought the whole reason there was threadd was to avoid the inheritance? So avoiding the avoiding of inheritance seems like the goal here, no?
I think so. Does it (Tejun's kthread_clone() patch) also inherit the cgroup of the caller? or do we still need the explicit call to attach the thread to the current task's cgroup?
I am on vacation next week and cannot look into this until Jul 12. Hope this will be resoved by then. If not, i will look into after i am back.
Thanks
Sridhar
Re: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 3 lines · Sridhar Samudrala
Does it (Tejun's
kthread_clone()patch) also inherit the
cgroup of the caller?
Of course, its a simple do_fork() which inherits everything just as you would expect from a similar sys_clone()/sys_fork() call.
Re: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 8 lines · Peter Zijlstra
Quoted 3 lines · Sridhar Samudrala
Does it (Tejun's
kthread_clone()patch) also inherit the
cgroup of the caller?Of course, its a simple
do_fork()which inherits everything just as you would expect from a similar sys_clone()/sys_fork() call.
Yes. And I'm afraid it can inherit more than we want. IIUC, this is called from ioctl(), right?
Then the new thread becomes the natural child of the caller, and it shares ->mm with the parent. And files, dup_fd() without CLONE_FS.
Signals. Say, if you send SIGKILL to this new thread, it can't sleep in TASK_INTERRUPTIBLE or KILLABLE after that. And this SIGKILL can be sent just because the parent gets SIGQUIT or abother coredumpable signal. Or the new thread can recieve SIGSTOP via ^Z.
Perhaps this is OK, I do not know. Just to remind that kernel_thread() is merely clone(CLONE_VM).
Oleg.
Michael S. Tsirkin
maintainerRe: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 21 lines · Oleg Nesterov
Quoted 8 lines · Peter Zijlstra
Quoted 3 lines · Sridhar Samudrala
Does it (Tejun's
kthread_clone()patch) also inherit the
cgroup of the caller?Of course, its a simple
do_fork()which inherits everything just as you would expect from a similar sys_clone()/sys_fork() call.Yes. And I'm afraid it can inherit more than we want. IIUC, this is called from
ioctl(), right?Then the new thread becomes the natural child of the caller, and it shares ->mm with the parent. And files,
dup_fd()withoutCLONE_FS.Signals. Say, if you send SIGKILL to this new thread, it can't sleep in
TASK_INTERRUPTIBLEor KILLABLE after that. And this SIGKILL can be sent just because the parent gets SIGQUIT or abother coredumpable signal. Or the new thread can recieve SIGSTOP via ^Z.
Quoted 4 lines
Perhaps this is OK, I do not know. Just to remind that
kernel_thread()is merelyclone(CLONE_VM).Oleg.
Right. Doing this might break things like flush. The signal and exit behaviour needs to be examined carefully. I am also unsure whether using such threads might be more expensive than inheriting kthreadd.
Signature · 1 line
MST
Re: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 26 lines · Michael S. Tsirkin
Quoted 24 lines · Oleg Nesterov
Quoted 8 lines · Peter Zijlstra
Quoted 3 lines · Sridhar Samudrala
Does it (Tejun's
kthread_clone()patch) also inherit the
cgroup of the caller?Of course, its a simple
do_fork()which inherits everything just as you would expect from a similar sys_clone()/sys_fork() call.Yes. And I'm afraid it can inherit more than we want. IIUC, this is called from
ioctl(), right?Then the new thread becomes the natural child of the caller, and it shares ->mm with the parent. And files,
dup_fd()withoutCLONE_FS.Signals. Say, if you send SIGKILL to this new thread, it can't sleep in
TASK_INTERRUPTIBLEor KILLABLE after that. And this SIGKILL can be sent just because the parent gets SIGQUIT or abother coredumpable signal. Or the new thread can recieve SIGSTOP via ^Z.Perhaps this is OK, I do not know. Just to remind that
kernel_thread()is merelyclone(CLONE_VM).
Quoted 8 lines
Quoted 2 lines
Oleg.
Right. Doing this might break things like flush. The signal and exit behaviour needs to be examined carefully. I am also unsure whether using such threads might be more expensive than inheriting kthreadd.
Should we just leave it to the userspace to set the cgroup/cpumask after qemu starts the guest and the vhost threads?
Thanks
Sridhar
Michael S. Tsirkin
maintainerRe: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 28 lines · Sridhar Samudrala
Quoted 18 lines · Michael S. Tsirkin
Quoted 17 lines · Oleg Nesterov
Quoted 5 lines · Peter Zijlstra
Quoted 2 lines · Sridhar Samudrala
Does it (Tejun's
kthread_clone()patch) also inherit the
cgroup of the caller?Of course, its a simple
do_fork()which inherits everything just as you would expect from a similar sys_clone()/sys_fork() call.Yes. And I'm afraid it can inherit more than we want. IIUC, this is called from
ioctl(), right?Then the new thread becomes the natural child of the caller, and it shares ->mm with the parent. And files,
dup_fd()withoutCLONE_FS.Signals. Say, if you send SIGKILL to this new thread, it can't sleep in
TASK_INTERRUPTIBLEor KILLABLE after that. And this SIGKILL can be sent just because the parent gets SIGQUIT or abother coredumpable signal. Or the new thread can recieve SIGSTOP via ^Z.Quoted 9 lines
Quoted 4 lines
Perhaps this is OK, I do not know. Just to remind that
kernel_thread()is merelyclone(CLONE_VM).Oleg.
Right. Doing this might break things like flush. The signal and exit behaviour needs to be examined carefully. I am also unsure whether using such threads might be more expensive than inheriting kthreadd.
Quoted 6 lines
Should we just leave it to the userspace to set the cgroup/cpumask after qemu starts the guest and
the vhost threads?Thanks
Sridhar
Yes but we can't trust userspace to do this. It's important to do it on thread creation: if we don't, malicious userspace can create large amount of work exceeding the cgroup limits.
And the same applies so the affinity: if the qemu process is limited to a set of CPUs, it's important to make the kernel thread that does work our behalf limited to the same set of CPUs.
This is not unique to vhost, it's just that virt scenarious are affected by this more: people seem to run untrusted applications and expect the damage to be contained.
Signature · 1 line
MST
Re: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 40 lines · Michael S. Tsirkin
Quoted 28 lines · Sridhar Samudrala
Quoted 18 lines · Michael S. Tsirkin
Quoted 17 lines · Oleg Nesterov
Quoted 5 lines · Peter Zijlstra
Quoted 2 lines · Sridhar Samudrala
Does it (Tejun's
kthread_clone()patch) also inherit the
cgroup of the caller?Of course, its a simple
do_fork()which inherits everything just as you would expect from a similar sys_clone()/sys_fork() call.Yes. And I'm afraid it can inherit more than we want. IIUC, this is called from
ioctl(), right?Then the new thread becomes the natural child of the caller, and it shares ->mm with the parent. And files,
dup_fd()withoutCLONE_FS.Signals. Say, if you send SIGKILL to this new thread, it can't sleep in
TASK_INTERRUPTIBLEor KILLABLE after that. And this SIGKILL can be sent just because the parent gets SIGQUIT or abother coredumpable signal. Or the new thread can recieve SIGSTOP via ^Z.Quoted 9 lines
Quoted 4 lines
Perhaps this is OK, I do not know. Just to remind that
kernel_thread()is merelyclone(CLONE_VM).Oleg.
Right. Doing this might break things like flush. The signal and exit behaviour needs to be examined carefully. I am also unsure whether using such threads might be more expensive than inheriting kthreadd.
Quoted 6 lines
Should we just leave it to the userspace to set the cgroup/cpumask after qemu starts the guest and
the vhost threads?Thanks
SridharYes but we can't trust userspace to do this. It's important to do it on thread creation: if we don't, malicious userspace can create large amount of work exceeding the cgroup limits.
Quoted 8 lines
And the same applies so the affinity: if the qemu process is limited to a set of CPUs, it's important to make the kernel thread that does work our behalf limited to the same set of CPUs.
This is not unique to vhost, it's just that virt scenarious are affected by this more: people seem to run untrusted applications and expect the damage to be contained.
OK. So we want to create a thread that is a child of kthreadd, but inherits the cgroup/cpumask from the caller. How about an exported kthread function kthread_create_in_current_cg() that does this?
include/linux/kthread.h +3 -0
@@ -9,6 +9,9 @@ struct task_struct *kthread_create(int (*threadfn)(void *data), const char namefmt[], ...) __attribute__((format(printf, 3, 4))); +struct task_struct *kthread_create_in_current_cg (int (*threadfn)(void *data), +void *data,char *name); +/** * kthread_run - create and wake a thread. * @threadfn: the function to run until signal_pending(current).
kernel/kthread.c +37 -0
@@ -15,6 +15,7 @@#include <linux/module.h> #include <linux/mutex.h> #include <trace/events/sched.h> +#include <linux/cgroup.h> static DEFINE_SPINLOCK (kthread_create_lock);static LIST_HEAD (kthread_create_list);
@@ -149,6 +150,42 @@ struct task_struct *kthread_create(int (*threadfn)(void *data), }EXPORT_SYMBOL (kthread_create); +struct task_struct *kthread_create_in_current_cg (int (*threadfn)(void *data), +void *data,char *name) +{ +struct task_struct *worker; +cpumask_var_t mask; +int ret = -ENOMEM ; + +if (!alloc_cpumask_var (&mask,GFP_KERNEL )) + goto out_free_mask; + + worker =kthread_create (threadfn, data,"%s-%d" , name, current->pid); +if (IS_ERR (worker)) + goto out_free_mask; + + ret =sched_getaffinity (current->pid, mask); +if (ret) + goto out_stop_worker; + + ret =sched_setaffinity (worker->pid, mask); +if (ret) + goto out_stop_worker; + + ret =cgroup_attach_task_current_cg (worker); +if (ret) + goto out_stop_worker; + +return worker; + +out_stop_worker: +kthread_stop (worker); +out_free_mask: +free_cpumask_var (mask); +return ERR_PTR (ret); +} +EXPORT_SYMBOL (kthread_create_in_current_cg); +/** * kthread_bind - bind a just-created kthread to a cpu. * @p: thread created by kthread_create().
Thanks
Sridhar
Re: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 4 lines · Sridhar Samudrala
OK. So we want to create a thread that is a child of kthreadd, but inherits the cgroup/cpumask from the caller. How about an exported kthread function
kthread_create_in_current_cg()that does this?
Well. I must admit, this looks a bit strange to me ;)
Instead of exporting sched_xxxaffinity() we export the new function which calls them. And I don't think this new helper is very useful in general. May be I am wrong...
Oleg.
Re: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 6 lines · Oleg Nesterov
Quoted 4 lines · Sridhar Samudrala
OK. So we want to create a thread that is a child of kthreadd, but inherits the cgroup/cpumask from the caller. How about an exported kthread function
kthread_create_in_current_cg()that does this?
Quoted 6 lines
Well. I must admit, this looks a bit strange to me ;)
Instead of exporting
sched_xxxaffinity()we export the new function which calls them. And I don't think this new helper is very useful in general. May be I am wrong...
If we agree on exporting sched_xxxaffinity() functions, we don't need this new kthread function and we can do the same in vhost as the original patch did.
Thanks
Sridhar
Michael S. Tsirkin
maintainerRe: [PATCH repost] sched: export sched_set/getaffinity to modules
In reply to Oleg NesterovQuoted 21 lines · Oleg Nesterov
Quoted 8 lines · Peter Zijlstra
Quoted 3 lines · Sridhar Samudrala
Does it (Tejun's
kthread_clone()patch) also inherit the
cgroup of the caller?Of course, its a simple
do_fork()which inherits everything just as you would expect from a similar sys_clone()/sys_fork() call.Yes. And I'm afraid it can inherit more than we want. IIUC, this is called from
ioctl(), right?Then the new thread becomes the natural child of the caller, and it shares ->mm with the parent. And files,
dup_fd()withoutCLONE_FS.Signals. Say, if you send SIGKILL to this new thread, it can't sleep in
TASK_INTERRUPTIBLEor KILLABLE after that. And this SIGKILL can be sent just because the parent gets SIGQUIT or abother coredumpable signal. Or the new thread can recieve SIGSTOP via ^Z.
Quoted 4 lines
Perhaps this is OK, I do not know. Just to remind that
kernel_thread()is merelyclone(CLONE_VM).Oleg.
With some machinery to stop it later, yes.
Oleg, how does the below look to you?
Here I explicitly drop the fds so we don't share them. CLONE_VM takes care of sharing the mm I think.
About signals - for the vhost-net use this is OK as we use uninterruptible sleep anyway (like the new kthread_worker does).
This code seems to work fine for me so far - any comments?
---
include/linux/kthread.h +5 -0
@@ -9,6 +9,11 @@ struct task_struct *kthread_create(int (*threadfn)(void *data), const char namefmt[], ...) __attribute__((format(printf, 3, 4))); +struct task_struct *kthread_create_inherit (int (*threadfn)(void *data), +void *data, +const char namefmt[], ...) + __attribute__((format (printf,3 ,4 ))); +/** * kthread_run - create and wake a thread. * @threadfn: the function to run until signal_pending(current).
kernel/kthread.c +32 -0
@@ -149,6 +149,38 @@ struct task_struct *kthread_create(int (*threadfn)(void *data), }EXPORT_SYMBOL (kthread_create); +/* Same as kthread_create, but inherit attributes (cgroups, priority, CPU mask) +* from current. */ +struct task_struct *kthread_create_inherit (int (*threadfn)(void *data), +void *data, +const char namefmt[], + ...) +{ +struct kthread_create_info create; + + create.threadfn = threadfn; + create.data = data; +init_completion (&create.done); + +create_kthread (&create); +wait_for_completion (&create.done); + +if (!IS_ERR (create.result)) { +va_list args; + +/* Don't share files with parent as drivers use release for +* close on exit, etc. */ +exit_files (create.result); + +va_start (args, namefmt); +vsnprintf (create.result->comm,sizeof (create.result->comm), + namefmt, args); +va_end (args); + } +return create.result; +} +EXPORT_SYMBOL (kthread_create_inherit); +/** * kthread_bind - bind a just-created kthread to a cpu. * @p: thread created by kthread_create().
Re: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 88 lines · Michael S. Tsirkin
Quoted 21 lines · Oleg Nesterov
Quoted 8 lines · Peter Zijlstra
Quoted 3 lines · Sridhar Samudrala
Does it (Tejun's
kthread_clone()patch) also inherit the
cgroup of the caller?Of course, its a simple
do_fork()which inherits everything just as you would expect from a similar sys_clone()/sys_fork() call.Yes. And I'm afraid it can inherit more than we want. IIUC, this is called from
ioctl(), right?Then the new thread becomes the natural child of the caller, and it shares ->mm with the parent. And files,
dup_fd()withoutCLONE_FS.Signals. Say, if you send SIGKILL to this new thread, it can't sleep in
TASK_INTERRUPTIBLEor KILLABLE after that. And this SIGKILL can be sent just because the parent gets SIGQUIT or abother coredumpable signal. Or the new thread can recieve SIGSTOP via ^Z.Quoted 4 lines
Perhaps this is OK, I do not know. Just to remind that
kernel_thread()is merelyclone(CLONE_VM).Oleg.
With some machinery to stop it later, yes.
Oleg, how does the below look to you?Here I explicitly drop the fds so we don't share them.
CLONE_VMtakes care of sharing the mm I think.
About signals - for the vhost-net use this is OK as we use uninterruptible sleep anyway (like the newkthread_workerdoes).This code seems to work fine for me so far - any comments?
---
include/linux/kthread.h +5
-0@@ -9,6 +9,11 @@ struct task_struct *kthread_create(int (*threadfn)(void *data), const char namefmt[], ...) __attribute__((format(printf, 3, 4))); +struct task_struct *kthread_create_inherit (int (*threadfn)(void *data), +void *data, +const char namefmt[], ...) + __attribute__((format (printf,3 ,4 ))); +/** * kthread_run - create and wake a thread. * @threadfn: the function to run until signal_pending(current). kernel/kthread.c +25
-0@@ -149,6 +149,38 @@ struct task_struct *kthread_create(int (*threadfn)(void *data), }EXPORT_SYMBOL (kthread_create); +/* Same as kthread_create, but inherit attributes (cgroups, priority, CPU mask) +* from current. */ +struct task_struct *kthread_create_inherit (int (*threadfn)(void *data), +void *data, +const char namefmt[], + ...) +{ +struct kthread_create_info create; + + create.threadfn = threadfn; + create.data = data; +init_completion (&create.done); + +create_kthread (&create); +wait_for_completion (&create.done); + +if (!IS_ERR (create.result)) { +va_list args; + +/* Don't share files with parent as drivers use release for +* close on exit, etc. */ +exit_files (create.result); + +va_start (args, namefmt); + vsnprintf(create.result->comm, sizeof(create.result->comm),
kernel/kthread.c struct task_struct *kthread_create(int (*threadfn)(void *data), · line 186
Quoted 10 lines
kernel/kthread.c +7
-0+ namefmt, args); +va_end (args); + } +return create.result; +} +EXPORT_SYMBOL (kthread_create_inherit); +/** * kthread_bind - bind a just-created kthread to a cpu. * @p: thread created by kthread_create().
I have been testing out a similar patch that uses kernel_thread() without CLONE_FILES flag rather than create_kthread() and then closing the files.
Either version should be fine.
include/linux/kthread.h +5 -0
@@ -9,6 +9,11 @@ struct task_struct *kthread_create(int (*threadfn)(void *data), const char namefmt[], ...) __attribute__((format(printf, 3, 4))); +struct task_struct *kthread_clone (int (*threadfn)(void *data), +void *data, +const char namefmt[], ...) + __attribute__((format (printf,3 ,4 ))); +/** * kthread_run - create and wake a thread. * @threadfn: the function to run until signal_pending(current).
kernel/kthread.c +32 -0
@@ -149,6 +149,38 @@ struct task_struct *kthread_create(int (*threadfn)(void *data), }EXPORT_SYMBOL (kthread_create); +struct task_struct *kthread_clone (int (*threadfn)(void *data), +void *data, +const char namefmt[], + ...) +{ +struct kthread_create_info create; +int pid; + + create.threadfn = threadfn; + create.data = data; +init_completion (&create.done); +INIT_LIST_HEAD (&create.list); + + pid =kernel_thread (kthread, &create,CLONE_FS ); +if (pid <0 ) { + create.result =ERR_PTR (pid); +complete (&create.done); + } +wait_for_completion (&create.done); + +if (!IS_ERR (create.result)) { +va_list args; +va_start (args, namefmt); +vsnprintf (create.result->comm,sizeof (create.result->comm), + namefmt, args); +va_end (args); + } + +return create.result; +} +EXPORT_SYMBOL (kthread_clone); +/** * kthread_bind - bind a just-created kthread to a cpu. * @p: thread created by kthread_create().
Re: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 3 lines · Sridhar Samudrala
I have been testing out a similar patch that uses
kernel_thread()withoutCLONE_FILESflag rather thancreate_kthread()and then closing the files.
!CLONE_FILES can't help. copy_files() does dup_fd() in this case. The child still inherits the files.
Quoted 1 line
Either version should be fine.
I think neither version is fine ;)
exit_files() is not enough too. How about the signals, reparenting?
I already forgot all details, probably I missed somethig. But it seems to me that it is better to just export get/set affinity and forget about all complications.
Oleg.
Michael S. Tsirkin
maintainerRe: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 5 lines · Oleg Nesterov
Quoted 3 lines · Sridhar Samudrala
I have been testing out a similar patch that uses
kernel_thread()withoutCLONE_FILESflag rather thancreate_kthread()and then closing the files.
Quoted 8 lines
!CLONE_FILES can't help.
copy_files()doesdup_fd()in this case. The child still inherits the files.Quoted 1 line
Either version should be fine.
I think neither version is fine ;)
exit_files()is not enough too. How about the signals,
As I said, signals are unimportant as we are using this thread to base a worker on - it sleeps uninterruptibly.
Quoted 1 line
reparenting?
That's actually a feature: it lets us find out which process owns the device using the thread by looking at the parent.
Quoted 6 lines
I already forgot all details, probably I missed somethig. But it seems to me that it is better to just export get/set affinity and forget about all complications.
Oleg.
Michael S. Tsirkin
maintainerRe: [PATCH repost] sched: export sched_set/getaffinity to modules
In reply to Oleg NesterovQuoted 12 lines · Oleg Nesterov
Quoted 3 lines · Sridhar Samudrala
I have been testing out a similar patch that uses
kernel_thread()withoutCLONE_FILESflag rather thancreate_kthread()and then closing the files.!CLONE_FILES can't help.
copy_files()doesdup_fd()in this case. The child still inherits the files.Quoted 1 line
Either version should be fine.
I think neither version is fine ;)
Quoted 8 lines
exit_files()is not enough too. How about the signals, reparenting?I already forgot all details, probably I missed somethig. But it seems to me that it is better to just export get/set affinity and forget about all complications.
Oleg.
Almost forgot, we need the same for priority.
Signature · 1 line
MST
Michael S. Tsirkin
maintainerRe: [PATCH repost] sched: export sched_set/getaffinity to modules
In reply to Oleg NesterovQuoted 12 lines · Oleg Nesterov
Quoted 3 lines · Sridhar Samudrala
I have been testing out a similar patch that uses
kernel_thread()withoutCLONE_FILESflag rather thancreate_kthread()and then closing the files.!CLONE_FILES can't help.
copy_files()doesdup_fd()in this case. The child still inherits the files.Quoted 1 line
Either version should be fine.
I think neither version is fine ;)
Quoted 8 lines
exit_files()is not enough too. How about the signals, reparenting?I already forgot all details, probably I missed somethig. But it seems to me that it is better to just export get/set affinity and forget about all complications.
Oleg.
Peter, could you please indicate whether you think this is the way to go, too?
Signature · 1 line
MST
Re: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 2 lines · Michael S. Tsirkin
Peter, could you please indicate whether you think this is the way to go, too?
I really dislike it, as you indicated, you now want priority too..
It seems the problem is that we normally don't consider work done by kernel threads for user processes part of that process.
I'm not sure what work you're doing, but I'm pretty sure there's similar things already in the kernel -- think about the work done by encryption threads for encrypted sockets and stuff.
If you want proper containment of work caused by a process, I'd suggest you start by looking at curing the general problem, instead of special casing this one case.
Michael S. Tsirkin
maintainerRe: [PATCH repost] sched: export sched_set/getaffinity to modules
In reply to Oleg NesterovQuoted 12 lines · Oleg Nesterov
Quoted 3 lines · Sridhar Samudrala
I have been testing out a similar patch that uses
kernel_thread()withoutCLONE_FILESflag rather thancreate_kthread()and then closing the files.!CLONE_FILES can't help.
copy_files()doesdup_fd()in this case. The child still inherits the files.Quoted 1 line
Either version should be fine.
I think neither version is fine ;)
Quoted 8 lines
exit_files()is not enough too. How about the signals, reparenting?I already forgot all details, probably I missed somethig. But it seems to me that it is better to just export get/set affinity and forget about all complications.
Oleg.
Oleg, so can I attach your Ack to the patch in question, and merge it all through net-next?
Signature · 1 line
MST
Re: [PATCH repost] sched: export sched_set/getaffinity to modules
Sorry for the delay, I can't be responsive these days...
Quoted 17 lines · Michael S. Tsirkin
Quoted 15 lines · Oleg Nesterov
Quoted 3 lines · Sridhar Samudrala
I have been testing out a similar patch that uses
kernel_thread()withoutCLONE_FILESflag rather thancreate_kthread()and then closing the files.!CLONE_FILES can't help.
copy_files()doesdup_fd()in this case. The child still inherits the files.Quoted 1 line
Either version should be fine.
I think neither version is fine ;)
exit_files()is not enough too. How about the signals, reparenting?
Quoted 8 lines
Quoted 5 lines
I already forgot all details, probably I missed somethig. But it seems to me that it is better to just export get/set affinity and forget about all complications.
Oleg.
Oleg, so can I attach your Ack to the patch in question, and merge it all through net-next?
Well, I do not think you need my ack ;)
But I must admit, I personally dislike this idea. A kernel thread which is the child of the user-space process, and in fact it is not the "real" kernel thread. I think this is against the common case. If you do not care the signals/reparenting, why can't you fork the user-space process which does all the work via ioctl's ? OK, I do not understand the problem domain, probably this can't work.
Anyway, the patch looks buggy to me. Starting from
create_kthread(&create); wait_for_completion(&create.done);
At least you should check create_kthread() suceeds, otherwise wait_for_completion() will hang forever. OTOH, if it suceeds then wait_for_completion() is not needed. But this is minor.
create_kthread()->kernel_thread() uses CLONE_VM, this means that the child will share ->mm. And this means that if the parent recieves the coredumping signal it will hang forever in kernel space waiting until this child exits.
This is just the immediate surprise I can see with this approach, I am afraid there is something else.
And once again. We are doing this hacks only because we lack a couples of exports (iiuk). This is, well, a bit strange ;)
Oleg.
Tejun Heo
maintainerRe: [PATCH repost] sched: export sched_set/getaffinity to modules
Hello,
Quoted 6 lines · Oleg Nesterov
But I must admit, I personally dislike this idea. A kernel thread which is the child of the user-space process, and in fact it is not the "real" kernel thread. I think this is against the common case. If you do not care the signals/reparenting, why can't you fork the user-space process which does all the work via ioctl's ? OK, I do not understand the problem domain, probably this can't work.
Having kernel threads which are children of user process is plain scary considering the many things parent/children relationship implies and various bugs and security vulnerabilities in the area. I can't pinpoint any problem but I think we really shouldn't be adding something like this for this specific use case. If we can get away with exporting a few symbols, let's go that way.
Thanks.
Signature · 1 line
tejun
Michael S. Tsirkin
maintainerRe: [PATCH repost] sched: export sched_set/getaffinity to modules
In reply to Oleg NesterovQuoted 53 lines · Oleg Nesterov
Sorry for the delay, I can't be responsive these days...
Quoted 17 lines · Michael S. Tsirkin
Quoted 15 lines · Oleg Nesterov
Quoted 3 lines · Sridhar Samudrala
I have been testing out a similar patch that uses
kernel_thread()withoutCLONE_FILESflag rather thancreate_kthread()and then closing the files.!CLONE_FILES can't help.
copy_files()doesdup_fd()in this case. The child still inherits the files.Quoted 1 line
Either version should be fine.
I think neither version is fine ;)
exit_files()is not enough too. How about the signals, reparenting?Quoted 8 lines
Quoted 5 lines
I already forgot all details, probably I missed somethig. But it seems to me that it is better to just export get/set affinity and forget about all complications.
Oleg.
Oleg, so can I attach your Ack to the patch in question, and merge it all through net-next?
Well, I do not think you need my ack ;)
But I must admit, I personally dislike this idea. A kernel thread which is the child of the user-space process, and in fact it is not the "real" kernel thread. I think this is against the common case. If you do not care the signals/reparenting, why can't you fork the user-space process which does all the work via ioctl's ? OK, I do not understand the problem domain, probably this can't work.
Anyway, the patch looks buggy to me. Starting from
create_kthread(&create); wait_for_completion(&create.done);At least you should check
create_kthread()suceeds, otherwisewait_for_completion()will hang forever. OTOH, if it suceeds thenwait_for_completion()is not needed. But this is minor.create_kthread()->kernel_thread() uses
CLONE_VM, this means that the child will share ->mm. And this means that if the parent recieves the coredumping signal it will hang forever in kernel space waiting until this child exits.
Quoted 7 lines
This is just the immediate surprise I can see with this approach, I am afraid there is something else.
And once again. We are doing this hacks only because we lack a couples of exports (iiuk). This is, well, a bit strange ;)
Oleg.
Oleg, I mean Ack the exporting of get/set affinity.
Thanks!
Signature · 1 line
MST
Re: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 2 lines · Michael S. Tsirkin
Oleg, I mean Ack the exporting of get/set affinity.
Ah, I misunderstood.
Yes, I believe the exporting is the lesser evil. Please feel free to add my ack.
Oleg.
Re: [PATCH repost] sched: export sched_set/getaffinity to modules
In reply to Peter ZijlstraQuoted 8 lines · Peter Zijlstra
Quoted 3 lines · Michael S. Tsirkin
Maybe it makes sense to add
kthread_clone(in addition tokthread_create) that would do what you suggest? If yes, any hints on an implementation?I think that's called
kernel_thread()see
kernel/kthread.c:create_kthread().
Well, strictly speaking kernel_thread() doesn't create the kernel thread. Unless the caller is the kernel thread. And daemonize() is deprecated. kernel_thread() just forks the CLONE_VM + flags child.
Oleg.
Re: [PATCH repost] sched: export sched_set/getaffinity to modules
In reply to Michael S. TsirkinQuoted 5 lines · Michael S. Tsirkin
Quoted 1 line
- why can't it set the kernel thread's affinity too?
It can. However: the threads are started internally by the driver when qemu does an ioctl. What we want to do is give it a sensible default affinity. management tool can later tweak it if it wants to.
So have that ioctl return the tid of that new fancy thread and then set its affinity, stuff it in cgroup, whatever you fancy.
Quoted 5 lines
Quoted 1 line
- what happens if someone changes the tasks' affinity?
We would normally create a cgroup including all internal tasks, making it easy to find and change affinity for them all if necessary.
And to stuff them in a cgroup you also need the tid, at which point it might as well set the affinity from userspace, right?
Michael S. Tsirkin
maintainerRe: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 11 lines · Peter Zijlstra
Quoted 5 lines · Michael S. Tsirkin
Quoted 1 line
- why can't it set the kernel thread's affinity too?
It can. However: the threads are started internally by the driver when qemu does an ioctl. What we want to do is give it a sensible default affinity. management tool can later tweak it if it wants to.
So have that ioctl return the tid of that new fancy thread and then set its affinity, stuff it in cgroup, whatever you fancy.
Quoted 8 lines
Quoted 5 lines
Quoted 1 line
- what happens if someone changes the tasks' affinity?
We would normally create a cgroup including all internal tasks, making it easy to find and change affinity for them all if necessary.
And to stuff them in a cgroup you also need the tid, at which point it might as well set the affinity from userspace, right?
We also put it in a cgroup transparently. I think that it's actually important to do it on thread creation: if we don't, malicious userspace can create large amount of work exceeding the cgroup limits.
And the same applies so the affinity, right? If the qemu process is limited to a set of CPUs, isn't it important to make the kernel thread that does work our behalf limited to the same set of CPUs?
Signature · 1 line
MST
Re: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 21 lines · Michael S. Tsirkin
Quoted 11 lines · Peter Zijlstra
Quoted 5 lines · Michael S. Tsirkin
Quoted 1 line
- why can't it set the kernel thread's affinity too?
It can. However: the threads are started internally by the driver when qemu does an ioctl. What we want to do is give it a sensible default affinity. management tool can later tweak it if it wants to.
So have that ioctl return the tid of that new fancy thread and then set its affinity, stuff it in cgroup, whatever you fancy.
Quoted 8 lines
Quoted 5 lines
Quoted 1 line
- what happens if someone changes the tasks' affinity?
We would normally create a cgroup including all internal tasks, making it easy to find and change affinity for them all if necessary.
And to stuff them in a cgroup you also need the tid, at which point it might as well set the affinity from userspace, right?
Quoted 8 lines
We also put it in a cgroup transparently. I think that it's actually important to do it on thread creation: if we don't, malicious userspace can create large amount of work exceeding the cgroup limits.
And the same applies so the affinity, right? If the qemu process is limited to a set of CPUs, isn't it important to make the kernel thread that does work our behalf limited to the same set of CPUs?
I'm not sure we have anything like this, but I wouldn't think so, if a driver creates a kthread and manages to inject tons of work its not typically limited to whatever limits apply to the task that supplied the work.
Take the encryption threads for example, those don't run in the context of whoever provides the data to be encrypted (file,net whatever) and thus the task responsible could consume heaps more resources than when it would have to do the encryption itself.
That's how kthreads work.
Michael S. Tsirkin
maintainerRe: [PATCH repost] sched: export sched_set/getaffinity to modules
Quoted 36 lines · Peter Zijlstra
Quoted 21 lines · Michael S. Tsirkin
Quoted 11 lines · Peter Zijlstra
Quoted 5 lines · Michael S. Tsirkin
Quoted 1 line
- why can't it set the kernel thread's affinity too?
It can. However: the threads are started internally by the driver when qemu does an ioctl. What we want to do is give it a sensible default affinity. management tool can later tweak it if it wants to.
So have that ioctl return the tid of that new fancy thread and then set its affinity, stuff it in cgroup, whatever you fancy.
Quoted 8 lines
Quoted 5 lines
Quoted 1 line
- what happens if someone changes the tasks' affinity?
We would normally create a cgroup including all internal tasks, making it easy to find and change affinity for them all if necessary.
And to stuff them in a cgroup you also need the tid, at which point it might as well set the affinity from userspace, right?
Quoted 8 lines
We also put it in a cgroup transparently. I think that it's actually important to do it on thread creation: if we don't, malicious userspace can create large amount of work exceeding the cgroup limits.
And the same applies so the affinity, right? If the qemu process is limited to a set of CPUs, isn't it important to make the kernel thread that does work our behalf limited to the same set of CPUs?
I'm not sure we have anything like this, but I wouldn't think so, if a driver creates a kthread and manages to inject tons of work its not typically limited to whatever limits apply to the task that supplied the work.
Quoted 6 lines
Take the encryption threads for example, those don't run in the context of whoever provides the data to be encrypted (file,net whatever) and thus the task responsible could consume heaps more resources than when it would have to do the encryption itself.
That's how kthreads work.
Right. And IMHO ideally all such work would run on the appropriate CPU and be accounted to. It's just that with virt people seem to run untrusted applications and expect the damage to be contained. So we came up with a simple approach that seems to do the just just for us.
Signature · 1 line
MST
Quoted 63 lines · Oleg Nesterov
Quoted 7 lines · Michael S. Tsirkin
Quoted 4 lines · Sridhar Samudrala
Add a new kernel API to create a singlethread workqueue and attach it's task to current task's cgroup and cpumask.
Signed-off-by Sridhar Samudrala <[email protected]>Quoted 7 lines
Could someone familiar with workqueue code please comment on whether this patch is suitable for 2.6.35?
It is needed to fix the case where vhost user might cause a kernel thread to consume more CPU than allowed by the cgroup. Should I merge it through the vhost tree?
Ack for this?I don't understand the reasons for this patch, but this doesn't matter.
I don't really see any need to change
workqueue.c,Quoted 4 lines
kernel/workqueue.c static const struct cpumask *cpu_singlethread_map __read_mostly; · line 200
Quoted 4 lines
kernel/workqueue.c +4
-0+static struct task_struct *get_singlethread_wq_task (struct workqueue_struct *wq) +{ +return (per_cpu_ptr (wq->cpu_wq, singlethread_cpu))->thread; +}(Not sure this trivial static helper with the single caller makes sense, but
see below)Quoted 24 lines
Quoted 24 lines
kernel/workqueue.c +24
-0+/* Create a singlethread workqueue and attach it's task to the current task's +* cgroup and set it's cpumask to the current task's cpumask. +*/ +struct workqueue_struct *create_singlethread_workqueue_in_current_cg (char *name) +{ +struct workqueue_struct *wq; +struct task_struct *task; +cpumask_var_t mask; + + wq =create_singlethread_workqueue (name); +if (!wq) + goto out; + +if (!alloc_cpumask_var (&mask,GFP_KERNEL )) + goto err; + +if (sched_getaffinity (current->pid, mask)) + goto err; + + task =get_singlethread_wq_task (wq); +if (sched_setaffinity (task->pid, mask)) + goto err; + +if (cgroup_attach_task_current_cg (task))Quoted 8 lines
kernel/workqueue.c static const struct cpumask *cpu_singlethread_map __read_mostly; · line 233
Quoted 8 lines
kernel/workqueue.c +8
-0+ goto err; +out: +return wq; +err: +destroy_workqueue (wq); + wq =NULL ; + goto out; +}Instead,
cgroup.c(or whoever needs this) can do
Quoted 10 lines
struct move_struct {
struct work_struct work;
int ret;
};
static void move_func(struct work_struct *work)
{
struct move_struct *move = container_of(...);
if (cgroup_attach_task_current_cg(current))We are trying to attach the task associated with workqueue to the current task's cgroup. So what we need is
cgroup_attach_task_current_cg(wq->task);
However there is no interface currently that exports the task associated with a workqueue. It is hidden in cpu_workqueue_struct and is only accessible within workqueue.c.
Quoted 25 lines
ret = -EANY;
}
static struct workqueue_struct *create_singlethread_workqueue_in_current_cg(char *name)
{
struct workqueue_struct *wq;
struct move_struct move = {
.work = __WORK_INITIALIZER(move_func);
};
wq = create_singlethread_workqueue(name);
if (!wq)
return NULL;
queue_work(&move.work);
flush_work(&move.work);
if (move.ret) {
destroy_workqueue(wq);
wq = NULL;
}
return wq;
}Quoted 2 lines
Or. Just export
wq_per_cpu()fromworkqueue.c(probably with a better name) and use it like the patch does.
This requires that struct cpu_workqueue_struct and struct workqueue_struct are made externally visible by moving them to kernel/workqueue.h.
Instead what about adding the simple helper get_singlethread_wq_task() in workqueue.c and exporting it.
I can add create_singlethread_workqueue_in_current_cg() to cgroup.c using this helper routine.
Thanks
Sridhar
Michael S. Tsirkin
maintainerQuoted 111 lines · Sridhar Samudrala
Quoted 63 lines · Oleg Nesterov
Quoted 7 lines · Michael S. Tsirkin
Quoted 4 lines · Sridhar Samudrala
Add a new kernel API to create a singlethread workqueue and attach it's task to current task's cgroup and cpumask.
Signed-off-by Sridhar Samudrala <[email protected]>Quoted 7 lines
Could someone familiar with workqueue code please comment on whether this patch is suitable for 2.6.35?
It is needed to fix the case where vhost user might cause a kernel thread to consume more CPU than allowed by the cgroup. Should I merge it through the vhost tree?
Ack for this?I don't understand the reasons for this patch, but this doesn't matter.
I don't really see any need to change
workqueue.c,Quoted 4 lines
kernel/workqueue.c static const struct cpumask *cpu_singlethread_map __read_mostly; · line 200
Quoted 4 lines
kernel/workqueue.c +4
-0+static struct task_struct *get_singlethread_wq_task (struct workqueue_struct *wq) +{ +return (per_cpu_ptr (wq->cpu_wq, singlethread_cpu))->thread; +}(Not sure this trivial static helper with the single caller makes sense, but
see below)Quoted 24 lines
Quoted 24 lines
kernel/workqueue.c +24
-0+/* Create a singlethread workqueue and attach it's task to the current task's +* cgroup and set it's cpumask to the current task's cpumask. +*/ +struct workqueue_struct *create_singlethread_workqueue_in_current_cg (char *name) +{ +struct workqueue_struct *wq; +struct task_struct *task; +cpumask_var_t mask; + + wq =create_singlethread_workqueue (name); +if (!wq) + goto out; + +if (!alloc_cpumask_var (&mask,GFP_KERNEL )) + goto err; + +if (sched_getaffinity (current->pid, mask)) + goto err; + + task =get_singlethread_wq_task (wq); +if (sched_setaffinity (task->pid, mask)) + goto err; + +if (cgroup_attach_task_current_cg (task))Quoted 8 lines
kernel/workqueue.c static const struct cpumask *cpu_singlethread_map __read_mostly; · line 233
Quoted 8 lines
kernel/workqueue.c +8
-0+ goto err; +out: +return wq; +err: +destroy_workqueue (wq); + wq =NULL ; + goto out; +}Instead,
cgroup.c(or whoever needs this) can doQuoted 10 lines
struct move_struct { struct work_struct work; int ret; }; static void move_func(struct work_struct *work) { struct move_struct *move = container_of(...); if (cgroup_attach_task_current_cg(current))We are trying to attach the task associated with workqueue to the current task's cgroup. So what we need is
cgroup_attach_task_current_cg(wq->task);However there is no interface currently that exports the task associated with a workqueue. It is hidden in
cpu_workqueue_structand is only accessible withinworkqueue.c.Quoted 25 lines
ret = -EANY; } static struct workqueue_struct *create_singlethread_workqueue_in_current_cg(char *name) { struct workqueue_struct *wq; struct move_struct move = { .work = __WORK_INITIALIZER(move_func); }; wq = create_singlethread_workqueue(name); if (!wq) return NULL; queue_work(&move.work); flush_work(&move.work); if (move.ret) { destroy_workqueue(wq); wq = NULL; } return wq; }Quoted 2 lines
Or. Just export
wq_per_cpu()fromworkqueue.c(probably with a better name) and use it like the patch does.
Quoted 8 lines
This requires that struct
cpu_workqueue_structand structworkqueue_structare made externally visible by moving them tokernel/workqueue.h.Instead what about adding the simple helper
get_singlethread_wq_task()inworkqueue.cand exporting it.
I can addcreate_singlethread_workqueue_in_current_cg()tocgroup.cusing this helper routine.
Or to our driver, if that's more palatable.
Quoted 3 lines
Thanks
Sridhar
What I am actually worried about is Tejun's rework, I am not sure cmwq has the "this thread services that wq" property...
Quoted 14 lines · Sridhar Samudrala
Quoted 12 lines · Oleg Nesterov
Instead,
cgroup.c(or whoever needs this) can dostruct move_struct { struct work_struct work; int ret; }; static void move_func(struct work_struct *work) { struct move_struct *move = container_of(...);
Quoted 5 lines
Quoted 1 line
if (
cgroup_attach_task_current_cg(current))We are trying to attach the task associated with workqueue to the current task's cgroup. So what we need is
cgroup_attach_task_current_cg(wq->task);
I do not see cgroup_attach_task_current_cg() in Linus's tree and thus I do not now what exactly it does, and of course the code above is only template.
But I think this is easy. Just add "struct cgroup *cgrp" into move_struct and then move_func() can do cgroup_attach_task(move->cgrp, current) ?
Quoted 5 lines
Quoted 2 lines
Or. Just export
wq_per_cpu()fromworkqueue.c(probably with a better name) and use it like the patch does.This requires that struct
cpu_workqueue_structand structworkqueue_structare made externally visible by moving them tokernel/workqueue.h.
no, no,
Quoted 2 lines
Instead what about adding the simple helper
get_singlethread_wq_task()inworkqueue.cand exporting it.
Indeed, this is what I meant.
But. I disagree with get_singlethread_wq_task(). If we add this helper, it should work with the multi-threaded wq's too, and needs the "int cpu" parameter, ignored when is_wq_single_threaded().
So. Either rename wq_per_cpu() and export it (once again, I do not mean we should move the body to workqueue.h!), or create the new helper which just calls wq_per_cpu().
Quoted 2 lines
I can add
create_singlethread_workqueue_in_current_cg()tocgroup.cusing this helper routine.
Imho, this is better.
But please note that it is possible to do without any changes in workqueue.[ch] afaics, see above.
Oleg.