diff options
Diffstat (limited to 'ipc')
-rw-r--r-- | ipc/shm.c | 75 |
1 files changed, 39 insertions, 36 deletions
@@ -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,37 +269,6 @@ 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 | { | ||
273 | struct ipc_namespace *ns = data; | ||
274 | struct kern_ipc_perm *ipcp = p; | ||
275 | struct shmid_kernel *shp = container_of(ipcp, struct shmid_kernel, shm_perm); | ||
276 | |||
277 | if (shp->shm_creator != current) | ||
278 | return 0; | ||
279 | |||
280 | /* | ||
281 | * Mark it as orphaned to destroy the segment when | ||
282 | * kernel.shm_rmid_forced is changed. | ||
283 | * It is noop if the following shm_may_destroy() returns true. | ||
284 | */ | ||
285 | shp->shm_creator = NULL; | ||
286 | |||
287 | /* | ||
288 | * Don't even try to destroy it. If shm_rmid_forced=0 and IPC_RMID | ||
289 | * is not set, it shouldn't be deleted here. | ||
290 | */ | ||
291 | if (!ns->shm_rmid_forced) | ||
292 | return 0; | ||
293 | |||
294 | if (shm_may_destroy(ns, shp)) { | ||
295 | shm_lock_by_ptr(shp); | ||
296 | shm_destroy(ns, shp); | ||
297 | } | ||
298 | return 0; | ||
299 | } | ||
300 | |||
301 | /* Called with ns->shm_ids(ns).rwsem locked */ | ||
302 | static int shm_try_destroy_orphaned(int id, void *p, void *data) | 272 | static int shm_try_destroy_orphaned(int id, void *p, void *data) |
303 | { | 273 | { |
304 | struct ipc_namespace *ns = data; | 274 | struct ipc_namespace *ns = data; |
@@ -329,18 +299,50 @@ void shm_destroy_orphaned(struct ipc_namespace *ns) | |||
329 | up_write(&shm_ids(ns).rwsem); | 299 | up_write(&shm_ids(ns).rwsem); |
330 | } | 300 | } |
331 | 301 | ||
332 | 302 | /* Locking assumes this will only be called with task == current */ | |
333 | void exit_shm(struct task_struct *task) | 303 | void exit_shm(struct task_struct *task) |
334 | { | 304 | { |
335 | struct ipc_namespace *ns = task->nsproxy->ipc_ns; | 305 | struct ipc_namespace *ns = task->nsproxy->ipc_ns; |
306 | struct shmid_kernel *shp, *n; | ||
336 | 307 | ||
337 | if (shm_ids(ns).in_use == 0) | 308 | if (list_empty(&task->sysvshm.shm_clist)) |
338 | return; | 309 | return; |
339 | 310 | ||
340 | /* Destroy all already created segments, but not mapped yet */ | 311 | /* |
312 | * If kernel.shm_rmid_forced is not set then only keep track of | ||
313 | * which shmids are orphaned, so that a later set of the sysctl | ||
314 | * can clean them up. | ||
315 | */ | ||
316 | if (!ns->shm_rmid_forced) { | ||
317 | down_read(&shm_ids(ns).rwsem); | ||
318 | list_for_each_entry(shp, &task->sysvshm.shm_clist, shm_clist) | ||
319 | shp->shm_creator = NULL; | ||
320 | /* | ||
321 | * Only under read lock but we are only called on current | ||
322 | * so no entry on the list will be shared. | ||
323 | */ | ||
324 | list_del(&task->sysvshm.shm_clist); | ||
325 | up_read(&shm_ids(ns).rwsem); | ||
326 | return; | ||
327 | } | ||
328 | |||
329 | /* | ||
330 | * Destroy all already created segments, that were not yet mapped, | ||
331 | * and mark any mapped as orphan to cover the sysctl toggling. | ||
332 | * Destroy is skipped if shm_may_destroy() returns false. | ||
333 | */ | ||
341 | down_write(&shm_ids(ns).rwsem); | 334 | down_write(&shm_ids(ns).rwsem); |
342 | if (shm_ids(ns).in_use) | 335 | 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); | 336 | shp->shm_creator = NULL; |
337 | |||
338 | if (shm_may_destroy(ns, shp)) { | ||
339 | shm_lock_by_ptr(shp); | ||
340 | shm_destroy(ns, shp); | ||
341 | } | ||
342 | } | ||
343 | |||
344 | /* Remove the list head from any segments still attached. */ | ||
345 | list_del(&task->sysvshm.shm_clist); | ||
344 | up_write(&shm_ids(ns).rwsem); | 346 | up_write(&shm_ids(ns).rwsem); |
345 | } | 347 | } |
346 | 348 | ||
@@ -561,6 +563,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params) | |||
561 | shp->shm_nattch = 0; | 563 | shp->shm_nattch = 0; |
562 | shp->shm_file = file; | 564 | shp->shm_file = file; |
563 | shp->shm_creator = current; | 565 | shp->shm_creator = current; |
566 | list_add(&shp->shm_clist, ¤t->sysvshm.shm_clist); | ||
564 | 567 | ||
565 | /* | 568 | /* |
566 | * shmid gets reported as "inode#" in /proc/pid/maps. | 569 | * shmid gets reported as "inode#" in /proc/pid/maps. |