fs, proc: Introduce the /proc/<pid>/children entry v2
Subsystems (2)
PATCH 1/1 fs, proc: Introduce the /proc/<pid>/children entry v2
There is no easy way to make a reverse parent->children chain from the task status, in turn children->parent provided with "PPid" field.
So instead of walking over all pids in system to figure out what children the task have -- we add explicit /proc/<pid>/children entry, since kernel already knows this kind of information but it was not yet exported.
v2:
- Kame suggested to use a separated /proc/<pid>/children entry instead of poking /proc/<pid>/status
- Andew suggested to use rcu facility instead of locking tasklist_lock
- Tejun pointed that non-seekable seq file might not be enough for tasks with large number of children
---
Please review. I hope I didn't miss anything here.
fs/proc/array.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/proc/base.c | 1 fs/proc/internal.h | 1 3 files changed, 68 insertions(+)
fs/proc/array.c +66 -0
@@ -547,3 +547,69 @@ int proc_pid_statm(struct seq_file *m, sreturn 0 ; } + +static int children_seq_show (struct seq_file *seq,void *v) +{ + struct task_struct *task = container_of(v, struct task_struct, sibling); +return seq_printf (seq," %d" ,pid_vnr (task_pid (task))); +} + +static void *children_seq_start (struct seq_file *seq,loff_t *pos) +{ +struct task_struct *task; + +rcu_read_lock (); + task = seq->private; +if (task) +return seq_list_start (&task->children, *pos); + +return NULL ; +} + +static void *children_seq_next (struct seq_file *seq,void *v,loff_t *pos) +{ +struct task_struct *task = seq->private; +if (task) +return seq_list_next (v, &task->children, pos); +return NULL ; +} + +static void children_seq_stop (struct seq_file *seq,void *v) +{ +rcu_read_unlock (); +seq_printf (seq,"\n" ); +} + +static const struct seq_operations children_seq_ops = { + .start = children_seq_start, + .next = children_seq_next, + .stop = children_seq_stop, + .show = children_seq_show, +}; + +static int children_seq_open (struct inode *inode,struct file *file) +{ +int ret =seq_open (file, &children_seq_ops); +if (!ret) { +struct seq_file *m = file->private_data; + m->private = (void *)get_proc_task (inode); + } + +return ret; +} +int children_seq_release (struct inode *inode,struct file *file) +{ +struct seq_file *m = file->private_data; +if (m->private) +put_task_struct (m->private); + +seq_release (inode, file); +return 0 ; +} + +const struct file_operations proc_pid_children_operations = { + .open = children_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = children_seq_release, +};
fs/proc/base.c +1 -0
@@ -3204,6 +3204,7 @@ static const struct pid_entry tgid_base_INF ("cmdline" ,S_IRUGO , proc_pid_cmdline),ONE ("stat" ,S_IRUGO , proc_tgid_stat),ONE ("statm" ,S_IRUGO , proc_pid_statm), +REG ("children" ,S_IRUGO , proc_pid_children_operations),REG ("maps" ,S_IRUGO , proc_maps_operations), #ifdef CONFIG_NUMA REG("numa_maps", S_IRUGO, proc_numa_maps_operations),
fs/proc/internal.h +1 -0
@@ -53,6 +53,7 @@ extern int proc_pid_statm(struct seq_fil struct pid *pid, struct task_struct *task);extern loff_t mem_lseek (struct file *file,loff_t offset,int orig); +extern const struct file_operations proc_pid_children_operations;extern const struct file_operations proc_maps_operations;extern const struct file_operations proc_numa_maps_operations;extern const struct file_operations proc_smaps_operations;
Quoted 12 lines · On Tue, 6 Dec 2011 22:10:26 +0400 Cyrill Gorcunov <[email protected]> wrote:
There is no easy way to make a reverse
parent->childrenchain from the task status, in turnchildren->parentprovided with "PPid" field.So instead of walking over all pids in system to figure out what children the task have -- we add explicit /proc/<pid>/children entry, since kernel already knows this kind of information but it was not yet exported.
...
fs/proc/array.c int proc_pid_statm(struct seq_file *m, s · line 555
Quoted 5 lines
fs/proc/array.c +5
-0+static int children_seq_show (struct seq_file *seq,void *v) +{ + struct task_struct *task = container_of(v, struct task_struct, sibling); +return seq_printf (seq," %d" ,pid_vnr (task_pid (task))); +}
Printing a pid_t with %d is a bit risky. At present all architectures appear to use int for pid_t so it's not broken (yet!). I suppose we can leave this code as-is for now - if some 32-bit arch turns up with a 64-bit pid_t, we'll want that warning to come out.
Quoted 15 lines
fs/proc/array.c +15
-0+static void *children_seq_start (struct seq_file *seq,loff_t *pos) +{ +struct task_struct *task; + +rcu_read_lock (); + task = seq->private; +if (task) +return seq_list_start (&task->children, *pos); + +return NULL ; +} + +static void *children_seq_next (struct seq_file *seq,void *v,loff_t *pos) +{ +struct task_struct *task = seq->private;
fs/proc/array.c int proc_pid_statm(struct seq_file *m, s · line 581
Quoted 10 lines
fs/proc/array.c +10
-0+if (task) +return seq_list_next (v, &task->children, pos); +return NULL ; +} + +static void children_seq_stop (struct seq_file *seq,void *v) +{ +rcu_read_unlock (); +seq_printf (seq,"\n" ); +}
So as I understand it, they way this works is that when the seqfile buffer fills up we will drop the rcu lock, will flush the buffer and then we reacquire the lock and the seq_file core will iterate over the children list until we end up the same number of items from the head, and populating the output buffer will resume.
This means that if the children list changes during the flush, the data which userspace reads will be wrong. Of course, inaccuracy is unavoidable when returning kernel-internal data such as this to userspace.
I suggest that the changelog should explain these races and should also explain how you propose that the client software will be robust against such races.
It's also worth adding a warning about this into the documentation for this procfs file. Documentation which this patch forgot to provide, btw!
On Tue, Dec 06, 2011 at 02:33:13PM -0800, Andrew Morton wrote:
...
Quoted 6 lines
It's also worth adding a warning about this into the documentation for this procfs file. Documentation which this patch forgot to provide, btw!
Yes, thanks for comments Andrew, I already got the feeling that I've missed documentation update from the start_brk /proc patch ;)
I'll address all issues, thanks!
Cyrill
Quoted 9 lines · Cyrill Gorcunov
On Tue, Dec 06, 2011 at 02:33:13PM -0800, Andrew Morton wrote:
...Quoted 6 lines
It's also worth adding a warning about this into the documentation for this procfs file. Documentation which this patch forgot to provide, btw!
Quoted 5 lines
Yes, thanks for comments Andrew, I already got the feeling that I've missed documentation update from the
start_brk/proc patch ;)I'll address all issues, thanks!
I hope this one addresses all nits.
Cyrill
---
From: Cyrill Gorcunov <[email protected]>
Subject: [PATCH] fs, proc: Introduce the /proc/<pid>/children entry v3
There is no easy way to make a reverse parent->children chain from a task status, in turn children->parent provided with "PPid" field.
So instead of walking over all pids in system to figure out what children a task have -- we add explicit /proc/<pid>/children entry, since kernel already has this kind of information but it is not yet exported.
Note that filling user-space buffer is done in iterative manner where each iteration is protected via RCU read lock, which means if between iterations a new task is added it might be missed in output and to have a consistent image of which children are really present in system the task should be either frozen or stopped together with all children.
v2:
- Kame suggested to use a separated /proc/<pid>/children entry instead of poking /proc/<pid>/status
- Andew suggested to use rcu facility instead of locking tasklist_lock
- Tejun pointed that non-seekable seq file might not be enough for tasks with large number of children
v3:
- To be on a safe side use %lu format for pid_t printing
- Update changelog and documentation
--- Documentation/filesystems/proc.txt | 15 ++++++++ fs/proc/array.c | 66 +++++++++++++++++++++++++++++++++++++ fs/proc/base.c | 1 fs/proc/internal.h | 1 4 files changed, 83 insertions(+)
Documentation/filesystems/proc.txt +15 -0
@@ -40,6 +40,7 @@ Table of Contents 3.4 /proc/<pid>/coredump_filter - Core dump filtering settings 3.5 /proc/<pid>/mountinfo - Information about mounts 3.6 /proc/<pid>/comm & /proc/<pid>/task/<tid>/comm + 3.7 /proc/<pid>/children - Information about task children ------------------------------------------------------------------------------
@@ -1545,3 +1546,17 @@ a task to set its own or one of its thre is limited in size compared to the cmdline value, so writing anything longer then the kernel's TASK_COMM_LEN (currently 16 chars) will result in a truncated comm value. + +3.7 /proc/<pid>/children - Information about task children +-------------------------------------------------------------- +This file provides a fast way to retrieve children pids of the +task pointed by <pid>. The format is a stream of pids separated +by space with a new line at the end. If a task has no children at +all -- only a new line returned. + +Note that filling user-space buffer is done in iterative manner where +each iteration is protected via RCU read lock, which means if between +iterations a new task is added it might be missed in output and +to have a consistent image of which children are really present in +system the task should be either frozen or stopped together with +all children.
fs/proc/array.c +66 -0
69 lines not on this page · raw message
fs/proc/base.c +1 -0
@@ -3204,6 +3204,7 @@ static const struct pid_entry tgid_base_INF ("cmdline" ,S_IRUGO , proc_pid_cmdline),ONE ("stat" ,S_IRUGO , proc_tgid_stat),ONE ("statm" ,S_IRUGO , proc_pid_statm), +REG ("children" ,S_IRUGO , proc_pid_children_operations),REG ("maps" ,S_IRUGO , proc_maps_operations), #ifdef CONFIG_NUMA REG("numa_maps", S_IRUGO, proc_numa_maps_operations),
fs/proc/internal.h +1 -0
@@ -53,6 +53,7 @@ extern int proc_pid_statm(struct seq_fil struct pid *pid, struct task_struct *task);extern loff_t mem_lseek (struct file *file,loff_t offset,int orig); +extern const struct file_operations proc_pid_children_operations;extern const struct file_operations proc_maps_operations;extern const struct file_operations proc_numa_maps_operations;extern const struct file_operations proc_smaps_operations;
Hello,
fs/proc/array.c int proc_pid_statm(struct seq_file *m, s · line 581
Quoted 5 lines · Cyrill Gorcunov
fs/proc/array.c +5
-0+static void children_seq_stop (struct seq_file *seq,void *v) +{ +rcu_read_unlock (); +seq_printf (seq,"\n" ); +}
Wouldn't this add newline on page boundary? Newline should be printed at the end of the whole sequence but seq_stop() is called at buffer boundaries.
Thanks.
Signature · 1 line
tejun
Quoted 13 lines · Tejun Heo
Hello,
fs/proc/array.c int proc_pid_statm(struct seq_file *m, s · line 581
Quoted 5 lines · Cyrill Gorcunov
fs/proc/array.c +5
-0+static void children_seq_stop (struct seq_file *seq,void *v) +{ +rcu_read_unlock (); +seq_printf (seq,"\n" ); +}Wouldn't this add newline on page boundary? Newline should be printed at the end of the whole sequence but
seq_stop()is called at buffer boundaries.
Yeah, good point. I'll update! Thanks Tejun (somehow missed this call in traverse()).
Cyrill
Hi Cyrill,
Sorry, I didn't read this patch yet, but
fs/proc/array.c int proc_pid_statm(struct seq_file *m, s · line 564
Quoted 9 lines · Cyrill Gorcunov
fs/proc/array.c +8
-0+static void *children_seq_start (struct seq_file *seq,loff_t *pos) +{ +struct task_struct *task; + +rcu_read_lock (); + task = seq->private; +if (task) +return seq_list_start (&task->children, *pos);
This looks "obviously wrong".
We can not trust ->children->next after rcu_read_unlock(). Another rcu_read_lock() can't help.
Once again, I can be easily wrong, need to read the patch first.
Oleg.
Quoted 17 lines · Oleg Nesterov
Hi Cyrill,
Sorry, I didn't read this patch yet, but
fs/proc/array.c int proc_pid_statm(struct seq_file *m, s · line 564
Quoted 9 lines · Cyrill Gorcunov
fs/proc/array.c +8
-0+static void *children_seq_start (struct seq_file *seq,loff_t *pos) +{ +struct task_struct *task; + +rcu_read_lock (); + task = seq->private; +if (task) +return seq_list_start (&task->children, *pos);This looks "obviously wrong".
Quoted 5 lines
We can not trust ->children->next after
rcu_read_unlock(). Anotherrcu_read_lock()can't help.Once again, I can be easily wrong, need to read the patch first.
Wait, Oleg, I might be wrong as well, but it's now as
children_seq_open
get_proc_task (so ref to task increased)
the children_seq_start/children_seq_stop works in iteration and every new iteration seq_list_next walks over the whole children list from the list head under rcu lock, so even if task is removed or added the link should exsist until rcu is unlocked and sync'ed no?
Cyrill
Quoted 29 lines · Cyrill Gorcunov
Quoted 17 lines · Oleg Nesterov
Hi Cyrill,
Sorry, I didn't read this patch yet, but
fs/proc/array.c int proc_pid_statm(struct seq_file *m, s · line 564
Quoted 9 lines · Cyrill Gorcunov
fs/proc/array.c +8
-0+static void *children_seq_start (struct seq_file *seq,loff_t *pos) +{ +struct task_struct *task; + +rcu_read_lock (); + task = seq->private; +if (task) +return seq_list_start (&task->children, *pos);This looks "obviously wrong".
Quoted 5 lines
We can not trust ->children->next after
rcu_read_unlock(). Anotherrcu_read_lock()can't help.Once again, I can be easily wrong, need to read the patch first.
Wait, Oleg, I might be wrong as well, but it's now as
children_seq_open
get_proc_task(so ref to task increased)
Quoted 7 lines
the children_seq_start/children_seq_stop works in iteration and every new iteration
seq_list_nextwalks over the whole children list from the list head under rcu lock, so even if task is removed or added the link should exsist until rcu is unlocked and sync'ed no?
On the other hands some if (task) tests are redundant and might be dropped since we have a reference to a task until seq-file is not released. I'll update it and shrink a patch some more.
Cyrill
On Wed, Dec 07, 2011 at 11:19:16PM +0400, Cyrill Gorcunov wrote:
...
Quoted 22 lines
Quoted 14 lines
Quoted 8 lines
This looks "obviously wrong".
We can not trust ->children->next after
rcu_read_unlock(). Anotherrcu_read_lock()can't help.Once again, I can be easily wrong, need to read the patch first.
Wait, Oleg, I might be wrong as well, but it's now as
children_seq_open
get_proc_task(so ref to task increased)Quoted 7 lines
the children_seq_start/children_seq_stop works in iteration and every new iteration
seq_list_nextwalks over the whole children list from the list head under rcu lock, so even if task is removed or added the link should exsist until rcu is unlocked and sync'ed no?
Quoted 5 lines
On the other hands some if (task) tests are redundant and might be dropped since we have a reference to a task until seq-file is not released. I'll update it and shrink a patch some more.
So while adding new task to a list is not a problem (the reader will simply not notice it) removing task from a list is a bit tricky, but as far as I see switch_task_namespaces (from exit_notify) uses synchronize_rcu as well as release_task calls for call_rcu for put_task_struct) so I think we will not get any wrong dereference in
static int children_seq_show(struct seq_file *seq, void *v)
{
struct task_struct *task = container_of(v, struct task_struct, sibling);
return seq_printf(seq, " %lu", (unsigned long)pid_vnr(task_pid(task)));
}while we're in rcu reader section. I might be wrong of course, so please verify this claim.
Cyrill
On Thu, Dec 08, 2011 at 12:34:50AM +0400, Cyrill Gorcunov wrote:
...
Quoted 8 lines
So while adding new task to a list is not a problem (the reader will simply not notice it) removing task from a list is a bit tricky, but as far as I see
switch_task_namespaces(fromexit_notify) usessynchronize_rcuas well asrelease_taskcalls forcall_rcuforput_task_struct) so I think we will not get any wrong dereference in
Quoted 9 lines
static int
children_seq_show(struct seq_file *seq, void *v){ struct task_struct *task = container_of(v, struct task_struct, sibling); return seq_printf(seq, " %lu", (unsigned long)pid_vnr(task_pid(task))); }while we're in rcu reader section. I might be wrong of course, so please verify this claim.
An updated version is below (I've updated changelog and docs as well). Please review if you get a chance. Complains are welcome of course.
Cyrill
---
From: Cyrill Gorcunov <[email protected]>
Subject: [PATCH] fs, proc: Introduce the /proc/<pid>/children entry v4
There is no easy way to make a reverse parent->children chain from arbitrary <pid> (while parent pid is provided in "PPid" field of /proc/<pid>/status).
So instead of walking over all pids in the system to figure out which children a task have -- we add explicit /proc/<pid>/children entry, because kernel already has this kind of information but it is not yet exported. This is a first level children, not the whole process tree, neither the process threads are identified with this interface.
Note that filling user-space buffer is done in iterative manner where each iteration is protected via RCU read lock, which means if between iterations a new task is added it might be missed in output and to have a consistent image of which children are really present in system the task should be either frozen or stopped together with all children.
v2:
- Kame suggested to use a separated /proc/<pid>/children entry instead of poking /proc/<pid>/status
- Andew suggested to use rcu facility instead of locking tasklist_lock
- Tejun pointed that non-seekable seq file might not be enough for tasks with large number of children
v3:
- To be on a safe side use %lu format for pid_t printing
v4:
- New line get printed when sequence ends not at seq->stop, nit pointed by Tejun
- documentation update
--- Documentation/filesystems/proc.txt | 19 +++++++++ fs/proc/array.c | 72 +++++++++++++++++++++++++++++++++++++ fs/proc/base.c | 1 fs/proc/internal.h | 1 4 files changed, 93 insertions(+)
Documentation/filesystems/proc.txt +19 -0
@@ -40,6 +40,7 @@ Table of Contents 3.4 /proc/<pid>/coredump_filter - Core dump filtering settings 3.5 /proc/<pid>/mountinfo - Information about mounts 3.6 /proc/<pid>/comm & /proc/<pid>/task/<tid>/comm + 3.7 /proc/<pid>/children - Information about task children ------------------------------------------------------------------------------
@@ -1545,3 +1546,21 @@ a task to set its own or one of its thre is limited in size compared to the cmdline value, so writing anything longer then the kernel's TASK_COMM_LEN (currently 16 chars) will result in a truncated comm value. + +3.7 /proc/<pid>/children - Information about task children +-------------------------------------------------------------- +This file provides a fast way to retrieve first level children pids +of the task pointed by <pid>. The format is a stream of pids separated +by space with a new line at the end. If a task has no children at +all -- only a new line returned. Note the "first level" here -- if +a task child has own children they will not be printed there, one +need to read /proc/<children-pid>/children to obtain such pids. +The same applies to threads -- they are not counted via this interface, +one need to read /proc/<pid>/status to find all threads the task created. + +Note that filling user-space buffer is done in iterative manner where +each iteration is protected via RCU read lock, which means if between +iterations a new task is added it might be missed in output and +to have a consistent image of which children are really present in +system the task should be either frozen or stopped together with +all children.
fs/proc/array.c +72 -0
75 lines not on this page · raw message
fs/proc/base.c +1 -0
@@ -3204,6 +3204,7 @@ static const struct pid_entry tgid_base_INF ("cmdline" ,S_IRUGO , proc_pid_cmdline),ONE ("stat" ,S_IRUGO , proc_tgid_stat),ONE ("statm" ,S_IRUGO , proc_pid_statm), +REG ("children" ,S_IRUGO , proc_pid_children_operations),REG ("maps" ,S_IRUGO , proc_maps_operations), #ifdef CONFIG_NUMA REG("numa_maps", S_IRUGO, proc_numa_maps_operations),
fs/proc/internal.h +1 -0
@@ -53,6 +53,7 @@ extern int proc_pid_statm(struct seq_fil struct pid *pid, struct task_struct *task);extern loff_t mem_lseek (struct file *file,loff_t offset,int orig); +extern const struct file_operations proc_pid_children_operations;extern const struct file_operations proc_maps_operations;extern const struct file_operations proc_numa_maps_operations;extern const struct file_operations proc_smaps_operations;
Quoted 22 lines · Cyrill Gorcunov
Quoted 20 lines · Oleg Nesterov
Hi Cyrill,
Sorry, I didn't read this patch yet, but
fs/proc/array.c int proc_pid_statm(struct seq_file *m, s · line 564
Quoted 9 lines · Cyrill Gorcunov
fs/proc/array.c +8
-0+static void *children_seq_start (struct seq_file *seq,loff_t *pos) +{ +struct task_struct *task; + +rcu_read_lock (); + task = seq->private; +if (task) +return seq_list_start (&task->children, *pos);This looks "obviously wrong".
We can not trust ->children->next after
rcu_read_unlock(). Anotherrcu_read_lock()can't help.
Quoted 7 lines
Quoted 2 lines
Once again, I can be easily wrong, need to read the patch first.
Wait, Oleg, I might be wrong as well, but it's now a
children_seq_open
get_proc_task(so ref to task increased)
Yes. task_struct itself can't go away.
Quoted 4 lines
the children_seq_start/children_seq_stop work in iteration and every new iteration
seq_list_nextwalks over the whole children list from the list head under rcu lock,
Yep, I misread this code, I though it does _next.
However, ->children list is not rcu-safe, this means that even list_for_each() itself is not safe. Either you need tasklist or we can probably make it rcu-safe...
As for /proc/pid/children, personally I think it is very useful. But note that it obviously reports the children per-thread, while in general this is the per-process thing. Not sure this really makes sense, but perhaps /proc/pid/children and /proc/pid/task/tid/children should act differently. Like, say, proc_tid_stat/proc_tgid_stat. I won't insist.
Oleg.
Quoted 13 lines · Oleg Nesterov
Quoted 5 lines
Wait, Oleg, I might be wrong as well, but it's now a
children_seq_open
get_proc_task(so ref to task increased)Yes.
task_structitself can't go away.Quoted 4 lines
the children_seq_start/children_seq_stop work in iteration and every new iteration
seq_list_nextwalks over the whole children list from the list head under rcu lock,
Quoted 6 lines
Yep, I misread this code, I though it does
_next.However, ->children list is not rcu-safe, this means that even
list_for_each()itself is not safe. Either you need tasklist or we can probably make it rcu-safe...
Seems I'll have to use tasklist_lock read-lock (atually it was there in previous versions of patch but patch was not implementing start/stop concept so I've been advised to use rcu read locks instead).
Quoted 3 lines
As for
/proc/pid/children, personally I think it is very useful. But note that it obviously reports the children per-thread, while in general this is the per-process thing. Not sure this really
Yeah, Kosaki pointed me that I missed children from another threads.
Quoted 4 lines
makes sense, but perhaps
/proc/pid/childrenand/proc/pid/task/tid/childrenshould act differently. Like, say, proc_tid_stat/proc_tgid_stat. I won't insist.
At moment I thought only about top level here, ie /proc/pid/children, but I think once I finish we can extend the patch ;)
Thanks for comments!
Cyrill
On Thu, Dec 08, 2011 at 05:35:35PM +0100, Oleg Nesterov wrote:
...
Quoted 5 lines
However, ->children list is not rcu-safe, this means that even
list_for_each()itself is not safe. Either you need tasklist or we can probably make it rcu-safe...
Andrew, Oleg, does the below one look more less fine? Note the tasklist_lock is back and it worries me a bit since I imagine one could be endlessly reading some /proc/<pid>/children file increasing contention over this lock on the whole system (regardless the fact that it's take for read only).
Cyrill
---
From: Cyrill Gorcunov <[email protected]>
Subject: [PATCH] fs, proc: Introduce the /proc/<pid>/children entry v4
There is no easy way to make a reverse parent->children chain from arbitrary <pid> (while parent pid is provided in "PPid" field of /proc/<pid>/status).
So instead of walking over all pids in the system to figure out which children a task have -- we add explicit /proc/<pid>/children entry, because kernel already has this kind of information but it is not yet exported. This is a first level children, not the whole process tree, neither the process threads are identified with this interface.
v2:
- Kame suggested to use a separated /proc/<pid>/children entry instead of poking /proc/<pid>/status
- Andew suggested to use rcu facility instead of locking tasklist_lock
- Tejun pointed that non-seekable seq file might not be enough for tasks with large number of children
v3:
- To be on a safe side use %lu format for pid_t printing
v4:
- New line get printed when sequence ends not at seq->stop, a nit pointed by Tejun
- Documentation update
- tasklist_lock is back, Oleg pointed that ->children list is actually not rcu-safe
--- Documentation/filesystems/proc.txt | 11 +++ fs/proc/array.c | 127 +++++++++++++++++++++++++++++++++++++ fs/proc/base.c | 1 fs/proc/internal.h | 6 + 4 files changed, 145 insertions(+)
Documentation/filesystems/proc.txt +11 -0
@@ -40,6 +40,7 @@ Table of Contents 3.4 /proc/<pid>/coredump_filter - Core dump filtering settings 3.5 /proc/<pid>/mountinfo - Information about mounts 3.6 /proc/<pid>/comm & /proc/<pid>/task/<tid>/comm + 3.7 /proc/<pid>/children - Information about task children ------------------------------------------------------------------------------
@@ -1545,3 +1546,13 @@ a task to set its own or one of its thre is limited in size compared to the cmdline value, so writing anything longer then the kernel's TASK_COMM_LEN (currently 16 chars) will result in a truncated comm value. + +3.7 /proc/<pid>/children - Information about task children +-------------------------------------------------------------- +This file provides a fast way to retrieve first level children pids +of a task pointed by <pid>. The format is a stream of pids separated +by space with a new line at the end. If a task has no children at +all -- only a new line returned. Note the "first level" here -- if +a task child has own children they will not be printed there, one +need to read /proc/<children-pid>/children to obtain such pids. +The same applies to threads -- they are not counted here.
fs/proc/array.c +127 -0
130 lines not on this page · raw message
fs/proc/base.c +1 -0
@@ -3204,6 +3204,7 @@ static const struct pid_entry tgid_base_INF ("cmdline" ,S_IRUGO , proc_pid_cmdline),ONE ("stat" ,S_IRUGO , proc_tgid_stat),ONE ("statm" ,S_IRUGO , proc_pid_statm), +REG ("children" ,S_IRUGO , proc_pid_children_operations),REG ("maps" ,S_IRUGO , proc_maps_operations), #ifdef CONFIG_NUMA REG("numa_maps", S_IRUGO, proc_numa_maps_operations),
fs/proc/internal.h +6 -0
@@ -53,6 +53,12 @@ extern int proc_pid_statm(struct seq_fil struct pid *pid, struct task_struct *task);extern loff_t mem_lseek (struct file *file,loff_t offset,int orig); +struct proc_pid_children_iter { +struct task_struct *leader; +struct task_struct *last; +}; + +extern const struct file_operations proc_pid_children_operations;extern const struct file_operations proc_maps_operations;extern const struct file_operations proc_numa_maps_operations;extern const struct file_operations proc_smaps_operations;
Quoted 8 lines · On Fri, 9 Dec 2011 01:28:53 +0400 Cyrill Gorcunov <[email protected]> wrote:
On Thu, Dec 08, 2011 at 05:35:35PM +0100, Oleg Nesterov wrote:
...Quoted 5 lines
However, ->children list is not rcu-safe, this means that even
list_for_each()itself is not safe. Either you need tasklist or we can probably make it rcu-safe...
Quoted 5 lines
Andrew, Oleg, does the below one look more less fine? Note the
tasklist_lockis back and it worries me a bit since I imagine one could be endlessly reading some /proc/<pid>/children file increasing contention over this lock on the whole system (regardless the fact that it's take for read only).
It is a potential problem, from the lock-hold point of view and also it can cause large scheduling latencies. What's involved in making ->children an rcu-protected list?
Quoted 8 lines
---
From: Cyrill Gorcunov <[email protected]>
Subject: [PATCH] fs, proc: Introduce the /proc/<pid>/children entry v4There is no easy way to make a reverse
parent->childrenchain from arbitrary <pid> (while parent pid is provided in "PPid" field of /proc/<pid>/status).
Quoted 5 lines
So instead of walking over all pids in the system to figure out which children a task have -- we add explicit /proc/<pid>/children entry, because kernel already has this kind of information but it is not yet exported. This is a first level children, not the whole process tree, neither the process threads are identified with this interface.
The changelog doesn't explain why we want the patch, so there's no reason to merge it! Something to do with c/r, yes?
If so, I guess the feature could/should be configurable. Probably with a CONFIG_PROC_CHILDREN which is selected by CONFIG_CHECKPOINT_RESTORE. Which is all getting a bit over the top, but I suppose we must do it.
Also, neither the changelog not the documentation mention the loss-of-data problem which might occur if/when the lock is dropped.
The code now appears to be kinda-duplicating functionality which the seq_file library provides. Shouldn't this have been changelogged/commented? If it was, I wouldn't need to ask the next question.
Why is it kinda-duplicating seq_file functionality? Can we strengthen the seq_file code so this is unnecessary?
Quoted 17 lines · Andrew Morton
Quoted 8 lines · On Fri, 9 Dec 2011 01:28:53 +0400 Cyrill Gorcunov <[email protected]> wrote:
On Thu, Dec 08, 2011 at 05:35:35PM +0100, Oleg Nesterov wrote:
...Quoted 5 lines
However, ->children list is not rcu-safe, this means that even
list_for_each()itself is not safe. Either you need tasklist or we can probably make it rcu-safe...Quoted 5 lines
Andrew, Oleg, does the below one look more less fine? Note the
tasklist_lockis back and it worries me a bit since I imagine one could be endlessly reading some /proc/<pid>/children file increasing contention over this lock on the whole system (regardless the fact that it's take for read only).
Quoted 4 lines
It is a potential problem, from the lock-hold point of view and also it can cause large scheduling latencies. What's involved in making ->children an rcu-protected list?
I think better to poke Oleg about it ;) Oleg?
Quoted 8 lines
Quoted 8 lines
---
From: Cyrill Gorcunov <[email protected]>
Subject: [PATCH] fs, proc: Introduce the /proc/<pid>/children entry v4There is no easy way to make a reverse
parent->childrenchain from arbitrary <pid> (while parent pid is provided in "PPid" field of /proc/<pid>/status).
Quoted 8 lines
Quoted 5 lines
So instead of walking over all pids in the system to figure out which children a task have -- we add explicit /proc/<pid>/children entry, because kernel already has this kind of information but it is not yet exported. This is a first level children, not the whole process tree, neither the process threads are identified with this interface.
The changelog doesn't explain why we want the patch, so there's no reason to merge it! Something to do with c/r, yes?
I though that "there is no easy way to make a reverse parent->children chain" explains the problem -- ie when we do checkpoint of some task we need to dump all its children and there is no easy and fast way to find such topology. Parent of child can always be found from /proc/pid/status but not the reverse.
Quoted 5 lines
If so, I guess the feature could/should be configurable. Probably with a
CONFIG_PROC_CHILDRENwhich is selected byCONFIG_CHECKPOINT_RESTORE. Which is all getting a bit over the top, but I suppose we must do it.
Yes, I can put it under CONFIG_CHECKPOINT_RESTORE, I thought to introduce this config variable in prctl patch. But I think all might end up in mess, so I guess better to make some patch series instead
- Introduction of CONFIG_CHECKPOINT_RESTORE variable
- /proc/pid/child patch
- prctl patch
- finally make map_files/ patch (which is in -mm already) being CONFIG_CHECKPOINT_RESTORE dependant as well
Sounds good in general?
Btw, any hint which Kconfig is a better place for CONFIG_CHECKPOINT_RESTORE, definitely not Kconfig.cpu, nor the mm/Kconfig. Not sure which one should be taken. I guess it should be somewhere on top level and at moment x86 dependant as well.
Quoted 3 lines
Also, neither the changelog not the documentation mention the loss-of-data problem which might occur if/when the lock is dropped.
Indeed. The number set is only valid while read, nothing else is guaranteed after that (I dropped those part from doc which mentioned that task should be ither frozen or stopped to get more-less consistent results). I'll bring it back, sorry!
Quoted 8 lines
The code now appears to be kinda-duplicating functionality which the
seq_filelibrary provides. Shouldn't this have been changelogged/commented? If it was, I wouldn't need to ask the next question.Why is it kinda-duplicating
seq_filefunctionality? Can we strengthen theseq_filecode so this is unnecessary?
If no threads were involved then seq_list_start/next would be enough but I need next_thread() helper here which doesn't allow me to use the former seq engine helpers. Andrew, I fear changing seq code bring even more complexity. At moment all helpers we use are grouped in a couple of code lines, i think it's easier to read.
Actually I had some crazy idea to be able to pass a "hint" to seq_file.c about how many memory we need to keep all pids printed, so seq_file would kmalloc/vmalloc memory for us at once and we simply fill it all in one pass but finally I got impression that it will bring more complexity and except us noone in kernel would need this functionality.
Cyrill
Quoted 18 lines · Andrew Morton
Quoted 8 lines · On Fri, 9 Dec 2011 01:28:53 +0400 Cyrill Gorcunov <[email protected]> wrote:
On Thu, Dec 08, 2011 at 05:35:35PM +0100, Oleg Nesterov wrote:
...Quoted 5 lines
However, ->children list is not rcu-safe, this means that even
list_for_each()itself is not safe. Either you need tasklist or we can probably make it rcu-safe...Quoted 5 lines
Andrew, Oleg, does the below one look more less fine? Note the
tasklist_lockis back and it worries me a bit since I imagine one could be endlessly reading some /proc/<pid>/children file increasing contention over this lock on the whole system (regardless the fact that it's take for read only).
Quoted 3 lines
It is a potential problem, from the lock-hold point of view and also it can cause large scheduling latencies. What's involved in making ->children an rcu-protected list?
At first glance, this doesn't look trivial... forget_original_parent() abuses ->sibling.
But yes, it is not really nice to hold tasklist_lock here. May be we can change this code so that every iteration records the reported task_struct and then tries to continue. This means we should verify that ->real_parent is still the same under tasklist, but at least this way we do not hold it throughout.
Quoted 13 lines
Quoted 7 lines
From: Cyrill Gorcunov <[email protected]>
Subject: [PATCH] fs, proc: Introduce the /proc/<pid>/children entry v4There is no easy way to make a reverse
parent->childrenchain from arbitrary <pid> (while parent pid is provided in "PPid" field of /proc/<pid>/status).Quoted 5 lines
So instead of walking over all pids in the system to figure out which children a task have -- we add explicit /proc/<pid>/children entry, because kernel already has this kind of information but it is not yet exported. This is a first level children, not the whole process tree, neither the process threads are identified with this interface.
Quoted 6 lines
The changelog doesn't explain why we want the patch, so there's no reason to merge it! Something to do with c/r, yes?
If so, I guess the feature could/should be configurable. Probably with a
CONFIG_PROC_CHILDRENwhich is selected byCONFIG_CHECKPOINT_RESTORE. Which is all getting a bit over the top, but I suppose we must do it.
Heh. This is the rare case when I personally like the new feature ;) I mean, it looks "obviously useful" to me. If nothing else, it can help to debug the problems. Probably the tools like pstree can use it.
Personally I'd even prefer /proc/pid/children/ directory (like /proc/pid/task), but I guess this needs much more complications.
Oleg.
On Fri, Dec 09, 2011 at 04:30:09PM +0100, Oleg Nesterov wrote:
...
Quoted 8 lines
Quoted 4 lines
It is a potential problem, from the lock-hold point of view and also it can cause large scheduling latencies. What's involved in making ->children an rcu-protected list?
At first glance, this doesn't look trivial...
forget_original_parent()abuses ->sibling.
But wait, forget_original_parent may move task out of the original list and put it in dead_children list, right? Then release_task may call delayed_put_task_struct under as call_rcu which should wait until we finish reading in our rcu section, isn't it?
Even if we get inconsistent picture of children (ie we get pid of shildren which just getting dead) I think it's fine.
Quoted 6 lines
But yes, it is not really nice to hold
tasklist_lockhere. May be we can change this code so that every iteration records the reportedtask_structand then tries to continue. This means we should verify that ->real_parent is still the same under tasklist, but at least this way we do not hold it throughout.
And if real_parent is changed, I should simply skip such task and continue, right?
Quoted 8 lines
Quoted 3 lines
If so, I guess the feature could/should be configurable. Probably with a
CONFIG_PROC_CHILDRENwhich is selected byCONFIG_CHECKPOINT_RESTORE. Which is all getting a bit over the top, but I suppose we must do it.Heh. This is the rare case when I personally like the new feature ;) I mean, it looks "obviously useful" to me. If nothing else, it can help to debug the problems. Probably the tools like pstree can use it.
Well, I'm personally fine with either approach. We could make it CONFIG_CHECKPOINT_RESTORE dependant and make CONFIG_CHECKPOINT_RESTORE being Y on default, then if someone not needed this facility he simply turn it off, no?
Quoted 3 lines
Personally I'd even prefer
/proc/pid/children/directory (like/proc/pid/task), but I guess this needs much more complications.
I fear so. Oleg, if it possible I would like to bring in as minimum code as I can ;)
Cyrill
Quoted 12 lines · Cyrill Gorcunov
On Fri, Dec 09, 2011 at 04:30:09PM +0100, Oleg Nesterov wrote:
...Quoted 8 lines
Quoted 4 lines
It is a potential problem, from the lock-hold point of view and also it can cause large scheduling latencies. What's involved in making ->children an rcu-protected list?
At first glance, this doesn't look trivial...
forget_original_parent()abuses ->sibling.
Quoted 7 lines
But wait,
forget_original_parentmay move task out of the original list and put it indead_childrenlist, right? Thenrelease_taskmay calldelayed_put_task_structunder ascall_rcuwhich should wait until we finish reading in our rcu section, isn't it?Even if we get inconsistent picture of children (ie we get pid of shildren which just getting dead) I think it's fine.
The problen is (one of the problems, in fact), list_move() changes ->next.
To simplify, let's talk about children_seq_start() your patch adds. Suppose that we make ->children/sibling "rcu-safe" (we replace __unhash_process()->list_del_init() with list_del_rcu, and so on).
Now, children_seq_start() does:
rcu_read_lock(); list_for_each(child, task->children) ...;
Suppose that this task exits and reparents the child we are looking at. Once reparent_leader() moves it into another list, list_for_each() can never stop.
In short. forget_original_parent() changes the _head_ of the list, in some sense.
Quoted 8 lines
Quoted 6 lines
But yes, it is not really nice to hold
tasklist_lockhere. May be we can change this code so that every iteration records the reportedtask_structand then tries to continue. This means we should verify that ->real_parent is still the same under tasklist, but at least this way we do not hold it throughout.And if
real_parentis changed,
... or if list_empty(->sibling) == T
Quoted 2 lines
I should simply skip such task and
continue, right?
I think you should restart in this (hopefully unlikely) case. Yes, this means the potentional data loss, but I guess we can't avoid this in any case. For example, with the current patch children_seq_start() can miss the _live_ thread if the already reported process was reaped.
Quoted 6 lines
Quoted 3 lines
Personally I'd even prefer
/proc/pid/children/directory (like/proc/pid/task), but I guess this needs much more complications.I fear so. Oleg, if it possible I would like to bring in as minimum code as I can ;)
Yes, yes, I agree.
Oleg.
On Fri, Dec 09, 2011 at 05:55:36PM +0100, Oleg Nesterov wrote:
...
Quoted 12 lines
To simplify, let's talk about
children_seq_start()your patch adds. Suppose that we make ->children/sibling "rcu-safe" (we replace __unhash_process()->list_del_init() withlist_del_rcu, and so on).Now,
children_seq_start()does:rcu_read_lock(); list_for_each(child, task->children) ...;
Quoted 7 lines
Suppose that this task exits and reparents the child we are looking at. Once
reparent_leader()moves it into another list,list_for_each()can never stop.In short.
forget_original_parent()changes the _head_ of the list, in some sense.
Yeah, I missed this, my bad! Thanks for explanation, Oleg. I'll post updated version once I finish it.
Cyrill