| Pavel Emelyanov | ae5e1b2 | 2008-02-08 04:18:22 -0800 | [diff] [blame] | 1 | /* | 
|  | 2 | * linux/ipc/namespace.c | 
|  | 3 | * Copyright (C) 2006 Pavel Emelyanov <xemul@openvz.org> OpenVZ, SWsoft Inc. | 
|  | 4 | */ | 
|  | 5 |  | 
|  | 6 | #include <linux/ipc.h> | 
|  | 7 | #include <linux/msg.h> | 
|  | 8 | #include <linux/ipc_namespace.h> | 
|  | 9 | #include <linux/rcupdate.h> | 
|  | 10 | #include <linux/nsproxy.h> | 
|  | 11 | #include <linux/slab.h> | 
|  | 12 |  | 
|  | 13 | #include "util.h" | 
|  | 14 |  | 
|  | 15 | static struct ipc_namespace *clone_ipc_ns(struct ipc_namespace *old_ns) | 
|  | 16 | { | 
| Pavel Emelyanov | ae5e1b2 | 2008-02-08 04:18:22 -0800 | [diff] [blame] | 17 | struct ipc_namespace *ns; | 
|  | 18 |  | 
| Pavel Emelyanov | ae5e1b2 | 2008-02-08 04:18:22 -0800 | [diff] [blame] | 19 | ns = kmalloc(sizeof(struct ipc_namespace), GFP_KERNEL); | 
|  | 20 | if (ns == NULL) | 
| Pierre Peiffer | ed2ddbf | 2008-02-08 04:18:57 -0800 | [diff] [blame] | 21 | return ERR_PTR(-ENOMEM); | 
| Pavel Emelyanov | ae5e1b2 | 2008-02-08 04:18:22 -0800 | [diff] [blame] | 22 |  | 
| Nadia Derbey | 4d89dc6 | 2008-04-29 01:00:40 -0700 | [diff] [blame] | 23 | atomic_inc(&nr_ipc_ns); | 
|  | 24 |  | 
| Pierre Peiffer | ed2ddbf | 2008-02-08 04:18:57 -0800 | [diff] [blame] | 25 | sem_init_ns(ns); | 
|  | 26 | msg_init_ns(ns); | 
|  | 27 | shm_init_ns(ns); | 
| Pavel Emelyanov | ae5e1b2 | 2008-02-08 04:18:22 -0800 | [diff] [blame] | 28 |  | 
| Nadia Derbey | e2c284d | 2008-04-29 01:00:44 -0700 | [diff] [blame] | 29 | /* | 
|  | 30 | * msgmni has already been computed for the new ipc ns. | 
|  | 31 | * Thus, do the ipcns creation notification before registering that | 
|  | 32 | * new ipcns in the chain. | 
|  | 33 | */ | 
|  | 34 | ipcns_notify(IPCNS_CREATED); | 
| Nadia Derbey | b6b337a | 2008-04-29 01:00:42 -0700 | [diff] [blame] | 35 | register_ipcns_notifier(ns); | 
|  | 36 |  | 
| Pavel Emelyanov | ae5e1b2 | 2008-02-08 04:18:22 -0800 | [diff] [blame] | 37 | kref_init(&ns->kref); | 
|  | 38 | return ns; | 
| Pavel Emelyanov | ae5e1b2 | 2008-02-08 04:18:22 -0800 | [diff] [blame] | 39 | } | 
|  | 40 |  | 
|  | 41 | struct ipc_namespace *copy_ipcs(unsigned long flags, struct ipc_namespace *ns) | 
|  | 42 | { | 
|  | 43 | struct ipc_namespace *new_ns; | 
|  | 44 |  | 
|  | 45 | BUG_ON(!ns); | 
|  | 46 | get_ipc_ns(ns); | 
|  | 47 |  | 
|  | 48 | if (!(flags & CLONE_NEWIPC)) | 
|  | 49 | return ns; | 
|  | 50 |  | 
|  | 51 | new_ns = clone_ipc_ns(ns); | 
|  | 52 |  | 
|  | 53 | put_ipc_ns(ns); | 
|  | 54 | return new_ns; | 
|  | 55 | } | 
|  | 56 |  | 
| Pierre Peiffer | 01b8b07 | 2008-02-08 04:18:57 -0800 | [diff] [blame] | 57 | /* | 
|  | 58 | * free_ipcs - free all ipcs of one type | 
|  | 59 | * @ns:   the namespace to remove the ipcs from | 
|  | 60 | * @ids:  the table of ipcs to free | 
|  | 61 | * @free: the function called to free each individual ipc | 
|  | 62 | * | 
|  | 63 | * Called for each kind of ipc when an ipc_namespace exits. | 
|  | 64 | */ | 
|  | 65 | void free_ipcs(struct ipc_namespace *ns, struct ipc_ids *ids, | 
|  | 66 | void (*free)(struct ipc_namespace *, struct kern_ipc_perm *)) | 
|  | 67 | { | 
|  | 68 | struct kern_ipc_perm *perm; | 
|  | 69 | int next_id; | 
|  | 70 | int total, in_use; | 
|  | 71 |  | 
|  | 72 | down_write(&ids->rw_mutex); | 
|  | 73 |  | 
|  | 74 | in_use = ids->in_use; | 
|  | 75 |  | 
|  | 76 | for (total = 0, next_id = 0; total < in_use; next_id++) { | 
|  | 77 | perm = idr_find(&ids->ipcs_idr, next_id); | 
|  | 78 | if (perm == NULL) | 
|  | 79 | continue; | 
|  | 80 | ipc_lock_by_ptr(perm); | 
|  | 81 | free(ns, perm); | 
|  | 82 | total++; | 
|  | 83 | } | 
|  | 84 | up_write(&ids->rw_mutex); | 
|  | 85 | } | 
|  | 86 |  | 
| Pavel Emelyanov | ae5e1b2 | 2008-02-08 04:18:22 -0800 | [diff] [blame] | 87 | void free_ipc_ns(struct kref *kref) | 
|  | 88 | { | 
|  | 89 | struct ipc_namespace *ns; | 
|  | 90 |  | 
|  | 91 | ns = container_of(kref, struct ipc_namespace, kref); | 
| Nadia Derbey | b6b337a | 2008-04-29 01:00:42 -0700 | [diff] [blame] | 92 | /* | 
|  | 93 | * Unregistering the hotplug notifier at the beginning guarantees | 
|  | 94 | * that the ipc namespace won't be freed while we are inside the | 
|  | 95 | * callback routine. Since the blocking_notifier_chain_XXX routines | 
|  | 96 | * hold a rw lock on the notifier list, unregister_ipcns_notifier() | 
|  | 97 | * won't take the rw lock before blocking_notifier_call_chain() has | 
|  | 98 | * released the rd lock. | 
|  | 99 | */ | 
|  | 100 | unregister_ipcns_notifier(ns); | 
| Pavel Emelyanov | ae5e1b2 | 2008-02-08 04:18:22 -0800 | [diff] [blame] | 101 | sem_exit_ns(ns); | 
|  | 102 | msg_exit_ns(ns); | 
|  | 103 | shm_exit_ns(ns); | 
|  | 104 | kfree(ns); | 
| Nadia Derbey | 4d89dc6 | 2008-04-29 01:00:40 -0700 | [diff] [blame] | 105 | atomic_dec(&nr_ipc_ns); | 
| Nadia Derbey | e2c284d | 2008-04-29 01:00:44 -0700 | [diff] [blame] | 106 |  | 
|  | 107 | /* | 
|  | 108 | * Do the ipcns removal notification after decrementing nr_ipc_ns in | 
|  | 109 | * order to have a correct value when recomputing msgmni. | 
|  | 110 | */ | 
|  | 111 | ipcns_notify(IPCNS_REMOVED); | 
| Pavel Emelyanov | ae5e1b2 | 2008-02-08 04:18:22 -0800 | [diff] [blame] | 112 | } |