aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ipc/shm.c67
1 files changed, 35 insertions, 32 deletions
diff --git a/ipc/shm.c b/ipc/shm.c
index 1fc3a61b443b..7fc9f9f3a26b 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -269,32 +269,6 @@ static void shm_close(struct vm_area_struct *vma)
269} 269}
270 270
271/* Called with ns->shm_ids(ns).rwsem locked */ 271/* Called with ns->shm_ids(ns).rwsem locked */
272static void shm_mark_orphan(struct shmid_kernel *shp, struct ipc_namespace *ns)
273{
274 if (WARN_ON(shp->shm_creator != current)) /* Remove me when it works */
275 return;
276
277 /*
278 * Mark it as orphaned to destroy the segment when
279 * kernel.shm_rmid_forced is changed.
280 * It is noop if the following shm_may_destroy() returns true.
281 */
282 shp->shm_creator = NULL;
283
284 /*
285 * Don't even try to destroy it. If shm_rmid_forced=0 and IPC_RMID
286 * is not set, it shouldn't be deleted here.
287 */
288 if (!ns->shm_rmid_forced)
289 return;
290
291 if (shm_may_destroy(ns, shp)) {
292 shm_lock_by_ptr(shp);
293 shm_destroy(ns, shp);
294 }
295}
296
297/* Called with ns->shm_ids(ns).rwsem locked */
298static int shm_try_destroy_orphaned(int id, void *p, void *data) 272static int shm_try_destroy_orphaned(int id, void *p, void *data)
299{ 273{
300 struct ipc_namespace *ns = data; 274 struct ipc_namespace *ns = data;
@@ -325,20 +299,49 @@ void shm_destroy_orphaned(struct ipc_namespace *ns)
325 up_write(&shm_ids(ns).rwsem); 299 up_write(&shm_ids(ns).rwsem);
326} 300}
327 301
328 302/* Locking assumes this will only be called with task == current */
329void exit_shm(struct task_struct *task) 303void exit_shm(struct task_struct *task)
330{ 304{
331 struct ipc_namespace *ns = task->nsproxy->ipc_ns; 305 struct ipc_namespace *ns = task->nsproxy->ipc_ns;
332 struct shmid_kernel *shp, *n; 306 struct shmid_kernel *shp, *n;
333 307
334 if (shm_ids(ns).in_use == 0) 308 if (list_empty(&task->sysvshm.shm_clist))
309 return;
310
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);
335 return; 326 return;
327 }
336 328
337 /* Destroy all already created segments, but not mapped yet */ 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 */
338 down_write(&shm_ids(ns).rwsem); 334 down_write(&shm_ids(ns).rwsem);
339 list_for_each_entry_safe(shp, n, &task->sysvshm.shm_clist, shm_clist) 335 list_for_each_entry_safe(shp, n, &task->sysvshm.shm_clist, shm_clist) {
340 shm_mark_orphan(shp, ns); 336 shp->shm_creator = NULL;
341 /* remove the list head from any segments still attached */ 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. */
342 list_del(&task->sysvshm.shm_clist); 345 list_del(&task->sysvshm.shm_clist);
343 up_write(&shm_ids(ns).rwsem); 346 up_write(&shm_ids(ns).rwsem);
344} 347}