diff options
-rw-r--r-- | include/linux/sched.h | 2 | ||||
-rw-r--r-- | include/linux/shm.h | 16 | ||||
-rw-r--r-- | ipc/shm.c | 22 | ||||
-rw-r--r-- | kernel/fork.c | 6 |
4 files changed, 34 insertions, 12 deletions
diff --git a/include/linux/sched.h b/include/linux/sched.h index b21e9218c0fd..db2f6474e95e 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h | |||
@@ -33,6 +33,7 @@ struct sched_param { | |||
33 | 33 | ||
34 | #include <linux/smp.h> | 34 | #include <linux/smp.h> |
35 | #include <linux/sem.h> | 35 | #include <linux/sem.h> |
36 | #include <linux/shm.h> | ||
36 | #include <linux/signal.h> | 37 | #include <linux/signal.h> |
37 | #include <linux/compiler.h> | 38 | #include <linux/compiler.h> |
38 | #include <linux/completion.h> | 39 | #include <linux/completion.h> |
@@ -1385,6 +1386,7 @@ struct task_struct { | |||
1385 | #ifdef CONFIG_SYSVIPC | 1386 | #ifdef CONFIG_SYSVIPC |
1386 | /* ipc stuff */ | 1387 | /* ipc stuff */ |
1387 | struct sysv_sem sysvsem; | 1388 | struct sysv_sem sysvsem; |
1389 | struct sysv_shm sysvshm; | ||
1388 | #endif | 1390 | #endif |
1389 | #ifdef CONFIG_DETECT_HUNG_TASK | 1391 | #ifdef CONFIG_DETECT_HUNG_TASK |
1390 | /* hung task detection */ | 1392 | /* hung task detection */ |
diff --git a/include/linux/shm.h b/include/linux/shm.h index 57d77709fbe2..fd206387048a 100644 --- a/include/linux/shm.h +++ b/include/linux/shm.h | |||
@@ -1,6 +1,7 @@ | |||
1 | #ifndef _LINUX_SHM_H_ | 1 | #ifndef _LINUX_SHM_H_ |
2 | #define _LINUX_SHM_H_ | 2 | #define _LINUX_SHM_H_ |
3 | 3 | ||
4 | #include <linux/list.h> | ||
4 | #include <asm/page.h> | 5 | #include <asm/page.h> |
5 | #include <uapi/linux/shm.h> | 6 | #include <uapi/linux/shm.h> |
6 | #include <asm/shmparam.h> | 7 | #include <asm/shmparam.h> |
@@ -20,6 +21,7 @@ struct shmid_kernel /* private to the kernel */ | |||
20 | 21 | ||
21 | /* The task created the shm object. NULL if the task is dead. */ | 22 | /* The task created the shm object. NULL if the task is dead. */ |
22 | struct task_struct *shm_creator; | 23 | struct task_struct *shm_creator; |
24 | struct list_head shm_clist; /* list by creator */ | ||
23 | }; | 25 | }; |
24 | 26 | ||
25 | /* shm_mode upper byte flags */ | 27 | /* shm_mode upper byte flags */ |
@@ -44,11 +46,20 @@ struct shmid_kernel /* private to the kernel */ | |||
44 | #define SHM_HUGE_1GB (30 << SHM_HUGE_SHIFT) | 46 | #define SHM_HUGE_1GB (30 << SHM_HUGE_SHIFT) |
45 | 47 | ||
46 | #ifdef CONFIG_SYSVIPC | 48 | #ifdef CONFIG_SYSVIPC |
49 | struct sysv_shm { | ||
50 | struct list_head shm_clist; | ||
51 | }; | ||
52 | |||
47 | long do_shmat(int shmid, char __user *shmaddr, int shmflg, unsigned long *addr, | 53 | long do_shmat(int shmid, char __user *shmaddr, int shmflg, unsigned long *addr, |
48 | unsigned long shmlba); | 54 | unsigned long shmlba); |
49 | extern int is_file_shm_hugepages(struct file *file); | 55 | extern int is_file_shm_hugepages(struct file *file); |
50 | extern void exit_shm(struct task_struct *task); | 56 | void exit_shm(struct task_struct *task); |
57 | #define shm_init_task(task) INIT_LIST_HEAD(&(task)->sysvshm.shm_clist) | ||
51 | #else | 58 | #else |
59 | struct sysv_shm { | ||
60 | /* empty */ | ||
61 | }; | ||
62 | |||
52 | static inline long do_shmat(int shmid, char __user *shmaddr, | 63 | static inline long do_shmat(int shmid, char __user *shmaddr, |
53 | int shmflg, unsigned long *addr, | 64 | int shmflg, unsigned long *addr, |
54 | unsigned long shmlba) | 65 | unsigned long shmlba) |
@@ -62,6 +73,9 @@ static inline int is_file_shm_hugepages(struct file *file) | |||
62 | static inline void exit_shm(struct task_struct *task) | 73 | static inline void exit_shm(struct task_struct *task) |
63 | { | 74 | { |
64 | } | 75 | } |
76 | static inline void shm_init_task(struct task_struct *task) | ||
77 | { | ||
78 | } | ||
65 | #endif | 79 | #endif |
66 | 80 | ||
67 | #endif /* _LINUX_SHM_H_ */ | 81 | #endif /* _LINUX_SHM_H_ */ |
@@ -178,6 +178,7 @@ static void shm_rcu_free(struct rcu_head *head) | |||
178 | 178 | ||
179 | static inline void shm_rmid(struct ipc_namespace *ns, struct shmid_kernel *s) | 179 | static inline void shm_rmid(struct ipc_namespace *ns, struct shmid_kernel *s) |
180 | { | 180 | { |
181 | list_del(&s->shm_clist); | ||
181 | ipc_rmid(&shm_ids(ns), &s->shm_perm); | 182 | ipc_rmid(&shm_ids(ns), &s->shm_perm); |
182 | } | 183 | } |
183 | 184 | ||
@@ -268,14 +269,10 @@ static void shm_close(struct vm_area_struct *vma) | |||
268 | } | 269 | } |
269 | 270 | ||
270 | /* Called with ns->shm_ids(ns).rwsem locked */ | 271 | /* Called with ns->shm_ids(ns).rwsem locked */ |
271 | static int shm_try_destroy_current(int id, void *p, void *data) | 272 | static void shm_mark_orphan(struct shmid_kernel *shp, struct ipc_namespace *ns) |
272 | { | 273 | { |
273 | struct ipc_namespace *ns = data; | 274 | if (WARN_ON(shp->shm_creator != current)) /* Remove me when it works */ |
274 | struct kern_ipc_perm *ipcp = p; | 275 | return; |
275 | struct shmid_kernel *shp = container_of(ipcp, struct shmid_kernel, shm_perm); | ||
276 | |||
277 | if (shp->shm_creator != current) | ||
278 | return 0; | ||
279 | 276 | ||
280 | /* | 277 | /* |
281 | * Mark it as orphaned to destroy the segment when | 278 | * Mark it as orphaned to destroy the segment when |
@@ -289,13 +286,12 @@ static int shm_try_destroy_current(int id, void *p, void *data) | |||
289 | * is not set, it shouldn't be deleted here. | 286 | * is not set, it shouldn't be deleted here. |
290 | */ | 287 | */ |
291 | if (!ns->shm_rmid_forced) | 288 | if (!ns->shm_rmid_forced) |
292 | return 0; | 289 | return; |
293 | 290 | ||
294 | if (shm_may_destroy(ns, shp)) { | 291 | if (shm_may_destroy(ns, shp)) { |
295 | shm_lock_by_ptr(shp); | 292 | shm_lock_by_ptr(shp); |
296 | shm_destroy(ns, shp); | 293 | shm_destroy(ns, shp); |
297 | } | 294 | } |
298 | return 0; | ||
299 | } | 295 | } |
300 | 296 | ||
301 | /* Called with ns->shm_ids(ns).rwsem locked */ | 297 | /* Called with ns->shm_ids(ns).rwsem locked */ |
@@ -333,14 +329,17 @@ void shm_destroy_orphaned(struct ipc_namespace *ns) | |||
333 | void exit_shm(struct task_struct *task) | 329 | void exit_shm(struct task_struct *task) |
334 | { | 330 | { |
335 | struct ipc_namespace *ns = task->nsproxy->ipc_ns; | 331 | struct ipc_namespace *ns = task->nsproxy->ipc_ns; |
332 | struct shmid_kernel *shp, *n; | ||
336 | 333 | ||
337 | if (shm_ids(ns).in_use == 0) | 334 | if (shm_ids(ns).in_use == 0) |
338 | return; | 335 | return; |
339 | 336 | ||
340 | /* Destroy all already created segments, but not mapped yet */ | 337 | /* Destroy all already created segments, but not mapped yet */ |
341 | down_write(&shm_ids(ns).rwsem); | 338 | down_write(&shm_ids(ns).rwsem); |
342 | if (shm_ids(ns).in_use) | 339 | list_for_each_entry_safe(shp, n, &task->sysvshm.shm_clist, shm_clist) |
343 | idr_for_each(&shm_ids(ns).ipcs_idr, &shm_try_destroy_current, ns); | 340 | shm_mark_orphan(shp, ns); |
341 | /* remove the list head from any segments still attached */ | ||
342 | list_del(&task->sysvshm.shm_clist); | ||
344 | up_write(&shm_ids(ns).rwsem); | 343 | up_write(&shm_ids(ns).rwsem); |
345 | } | 344 | } |
346 | 345 | ||
@@ -561,6 +560,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params) | |||
561 | shp->shm_nattch = 0; | 560 | shp->shm_nattch = 0; |
562 | shp->shm_file = file; | 561 | shp->shm_file = file; |
563 | shp->shm_creator = current; | 562 | shp->shm_creator = current; |
563 | list_add(&shp->shm_clist, ¤t->sysvshm.shm_clist); | ||
564 | 564 | ||
565 | /* | 565 | /* |
566 | * shmid gets reported as "inode#" in /proc/pid/maps. | 566 | * shmid gets reported as "inode#" in /proc/pid/maps. |
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); |