aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/fork.c
diff options
context:
space:
mode:
authorJack Miller <millerjo@us.ibm.com>2014-08-08 17:23:19 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-08-08 18:57:26 -0400
commitab602f799159393143d567e5c04b936fec79d6bd (patch)
treeae2ebcc0d372cf0efb91cf2b95fca33f304a69da /kernel/fork.c
parent9687fd9101afaa1c4b1de7ffd2f9d7e53f45b29f (diff)
shm: make exit_shm work proportional to task activity
This is small set of patches our team has had kicking around for a few versions internally that fixes tasks getting hung on shm_exit when there are many threads hammering it at once. Anton wrote a simple test to cause the issue: http://ozlabs.org/~anton/junkcode/bust_shm_exit.c Before applying this patchset, this test code will cause either hanging tracebacks or pthread out of memory errors. After this patchset, it will still produce output like: root@somehost:~# ./bust_shm_exit 1024 160 ... INFO: rcu_sched detected stalls on CPUs/tasks: {} (detected by 116, t=2111 jiffies, g=241, c=240, q=7113) INFO: Stall ended before state dump start ... But the task will continue to run along happily, so we consider this an improvement over hanging, even if it's a bit noisy. This patch (of 3): exit_shm obtains the ipc_ns shm rwsem for write and holds it while it walks every shared memory segment in the namespace. Thus the amount of work is related to the number of shm segments in the namespace not the number of segments that might need to be cleaned. In addition, this occurs after the task has been notified the thread has exited, so the number of tasks waiting for the ns shm rwsem can grow without bound until memory is exausted. Add a list to the task struct of all shmids allocated by this task. Init the list head in copy_process. Use the ns->rwsem for locking. Add segments after id is added, remove before removing from id. On unshare of NEW_IPCNS orphan any ids as if the task had exited, similar to handling of semaphore undo. I chose a define for the init sequence since its a simple list init, otherwise it would require a function call to avoid include loops between the semaphore code and the task struct. Converting the list_del to list_del_init for the unshare cases would remove the exit followed by init, but I left it blow up if not inited. Signed-off-by: Milton Miller <miltonm@bga.com> Signed-off-by: Jack Miller <millerjo@us.ibm.com> Cc: Davidlohr Bueso <davidlohr@hp.com> Cc: Manfred Spraul <manfred@colorfullife.com> Cc: Anton Blanchard <anton@samba.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/fork.c')
-rw-r--r--kernel/fork.c6
1 files changed, 6 insertions, 0 deletions
diff --git a/kernel/fork.c b/kernel/fork.c
index 86da59e165ad..fa9124322cd4 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1362,6 +1362,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
1362 if (retval) 1362 if (retval)
1363 goto bad_fork_cleanup_policy; 1363 goto bad_fork_cleanup_policy;
1364 /* copy all the process information */ 1364 /* copy all the process information */
1365 shm_init_task(p);
1365 retval = copy_semundo(clone_flags, p); 1366 retval = copy_semundo(clone_flags, p);
1366 if (retval) 1367 if (retval)
1367 goto bad_fork_cleanup_audit; 1368 goto bad_fork_cleanup_audit;
@@ -1913,6 +1914,11 @@ SYSCALL_DEFINE1(unshare, unsigned long, unshare_flags)
1913 */ 1914 */
1914 exit_sem(current); 1915 exit_sem(current);
1915 } 1916 }
1917 if (unshare_flags & CLONE_NEWIPC) {
1918 /* Orphan segments in old ns (see sem above). */
1919 exit_shm(current);
1920 shm_init_task(current);
1921 }
1916 1922
1917 if (new_nsproxy) 1923 if (new_nsproxy)
1918 switch_task_namespaces(current, new_nsproxy); 1924 switch_task_namespaces(current, new_nsproxy);