diff options
author | Jiri Kosina <jkosina@suse.cz> | 2011-09-15 09:08:05 -0400 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2011-09-15 09:08:18 -0400 |
commit | e060c38434b2caa78efe7cedaff4191040b65a15 (patch) | |
tree | 407361230bf6733f63d8e788e4b5e6566ee04818 /ipc | |
parent | 10e4ac572eeffe5317019bd7330b6058a400dfc2 (diff) | |
parent | cc39c6a9bbdebfcf1a7dee64d83bf302bc38d941 (diff) |
Merge branch 'master' into for-next
Fast-forward merge with Linus to be able to merge patches
based on more recent version of the tree.
Diffstat (limited to 'ipc')
-rw-r--r-- | ipc/ipc_sysctl.c | 36 | ||||
-rw-r--r-- | ipc/mqueue.c | 125 | ||||
-rw-r--r-- | ipc/shm.c | 117 |
3 files changed, 212 insertions, 66 deletions
diff --git a/ipc/ipc_sysctl.c b/ipc/ipc_sysctl.c index 56410faa455..00fba2bab87 100644 --- a/ipc/ipc_sysctl.c +++ b/ipc/ipc_sysctl.c | |||
@@ -31,12 +31,37 @@ static int proc_ipc_dointvec(ctl_table *table, int write, | |||
31 | void __user *buffer, size_t *lenp, loff_t *ppos) | 31 | void __user *buffer, size_t *lenp, loff_t *ppos) |
32 | { | 32 | { |
33 | struct ctl_table ipc_table; | 33 | struct ctl_table ipc_table; |
34 | |||
34 | memcpy(&ipc_table, table, sizeof(ipc_table)); | 35 | memcpy(&ipc_table, table, sizeof(ipc_table)); |
35 | ipc_table.data = get_ipc(table); | 36 | ipc_table.data = get_ipc(table); |
36 | 37 | ||
37 | return proc_dointvec(&ipc_table, write, buffer, lenp, ppos); | 38 | return proc_dointvec(&ipc_table, write, buffer, lenp, ppos); |
38 | } | 39 | } |
39 | 40 | ||
41 | static int proc_ipc_dointvec_minmax(ctl_table *table, int write, | ||
42 | void __user *buffer, size_t *lenp, loff_t *ppos) | ||
43 | { | ||
44 | struct ctl_table ipc_table; | ||
45 | |||
46 | memcpy(&ipc_table, table, sizeof(ipc_table)); | ||
47 | ipc_table.data = get_ipc(table); | ||
48 | |||
49 | return proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos); | ||
50 | } | ||
51 | |||
52 | static int proc_ipc_dointvec_minmax_orphans(ctl_table *table, int write, | ||
53 | void __user *buffer, size_t *lenp, loff_t *ppos) | ||
54 | { | ||
55 | struct ipc_namespace *ns = current->nsproxy->ipc_ns; | ||
56 | int err = proc_ipc_dointvec_minmax(table, write, buffer, lenp, ppos); | ||
57 | |||
58 | if (err < 0) | ||
59 | return err; | ||
60 | if (ns->shm_rmid_forced) | ||
61 | shm_destroy_orphaned(ns); | ||
62 | return err; | ||
63 | } | ||
64 | |||
40 | static int proc_ipc_callback_dointvec(ctl_table *table, int write, | 65 | static int proc_ipc_callback_dointvec(ctl_table *table, int write, |
41 | void __user *buffer, size_t *lenp, loff_t *ppos) | 66 | void __user *buffer, size_t *lenp, loff_t *ppos) |
42 | { | 67 | { |
@@ -125,6 +150,8 @@ static int proc_ipcauto_dointvec_minmax(ctl_table *table, int write, | |||
125 | #else | 150 | #else |
126 | #define proc_ipc_doulongvec_minmax NULL | 151 | #define proc_ipc_doulongvec_minmax NULL |
127 | #define proc_ipc_dointvec NULL | 152 | #define proc_ipc_dointvec NULL |
153 | #define proc_ipc_dointvec_minmax NULL | ||
154 | #define proc_ipc_dointvec_minmax_orphans NULL | ||
128 | #define proc_ipc_callback_dointvec NULL | 155 | #define proc_ipc_callback_dointvec NULL |
129 | #define proc_ipcauto_dointvec_minmax NULL | 156 | #define proc_ipcauto_dointvec_minmax NULL |
130 | #endif | 157 | #endif |
@@ -155,6 +182,15 @@ static struct ctl_table ipc_kern_table[] = { | |||
155 | .proc_handler = proc_ipc_dointvec, | 182 | .proc_handler = proc_ipc_dointvec, |
156 | }, | 183 | }, |
157 | { | 184 | { |
185 | .procname = "shm_rmid_forced", | ||
186 | .data = &init_ipc_ns.shm_rmid_forced, | ||
187 | .maxlen = sizeof(init_ipc_ns.shm_rmid_forced), | ||
188 | .mode = 0644, | ||
189 | .proc_handler = proc_ipc_dointvec_minmax_orphans, | ||
190 | .extra1 = &zero, | ||
191 | .extra2 = &one, | ||
192 | }, | ||
193 | { | ||
158 | .procname = "msgmax", | 194 | .procname = "msgmax", |
159 | .data = &init_ipc_ns.msg_ctlmax, | 195 | .data = &init_ipc_ns.msg_ctlmax, |
160 | .maxlen = sizeof (init_ipc_ns.msg_ctlmax), | 196 | .maxlen = sizeof (init_ipc_ns.msg_ctlmax), |
diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 14fb6d67e6a..ed049ea568f 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c | |||
@@ -113,72 +113,75 @@ static struct inode *mqueue_get_inode(struct super_block *sb, | |||
113 | { | 113 | { |
114 | struct user_struct *u = current_user(); | 114 | struct user_struct *u = current_user(); |
115 | struct inode *inode; | 115 | struct inode *inode; |
116 | int ret = -ENOMEM; | ||
116 | 117 | ||
117 | inode = new_inode(sb); | 118 | inode = new_inode(sb); |
118 | if (inode) { | 119 | if (!inode) |
119 | inode->i_ino = get_next_ino(); | 120 | goto err; |
120 | inode->i_mode = mode; | ||
121 | inode->i_uid = current_fsuid(); | ||
122 | inode->i_gid = current_fsgid(); | ||
123 | inode->i_mtime = inode->i_ctime = inode->i_atime = | ||
124 | CURRENT_TIME; | ||
125 | 121 | ||
126 | if (S_ISREG(mode)) { | 122 | inode->i_ino = get_next_ino(); |
127 | struct mqueue_inode_info *info; | 123 | inode->i_mode = mode; |
128 | struct task_struct *p = current; | 124 | inode->i_uid = current_fsuid(); |
129 | unsigned long mq_bytes, mq_msg_tblsz; | 125 | inode->i_gid = current_fsgid(); |
130 | 126 | inode->i_mtime = inode->i_ctime = inode->i_atime = CURRENT_TIME; | |
131 | inode->i_fop = &mqueue_file_operations; | 127 | |
132 | inode->i_size = FILENT_SIZE; | 128 | if (S_ISREG(mode)) { |
133 | /* mqueue specific info */ | 129 | struct mqueue_inode_info *info; |
134 | info = MQUEUE_I(inode); | 130 | struct task_struct *p = current; |
135 | spin_lock_init(&info->lock); | 131 | unsigned long mq_bytes, mq_msg_tblsz; |
136 | init_waitqueue_head(&info->wait_q); | 132 | |
137 | INIT_LIST_HEAD(&info->e_wait_q[0].list); | 133 | inode->i_fop = &mqueue_file_operations; |
138 | INIT_LIST_HEAD(&info->e_wait_q[1].list); | 134 | inode->i_size = FILENT_SIZE; |
139 | info->notify_owner = NULL; | 135 | /* mqueue specific info */ |
140 | info->qsize = 0; | 136 | info = MQUEUE_I(inode); |
141 | info->user = NULL; /* set when all is ok */ | 137 | spin_lock_init(&info->lock); |
142 | memset(&info->attr, 0, sizeof(info->attr)); | 138 | init_waitqueue_head(&info->wait_q); |
143 | info->attr.mq_maxmsg = ipc_ns->mq_msg_max; | 139 | INIT_LIST_HEAD(&info->e_wait_q[0].list); |
144 | info->attr.mq_msgsize = ipc_ns->mq_msgsize_max; | 140 | INIT_LIST_HEAD(&info->e_wait_q[1].list); |
145 | if (attr) { | 141 | info->notify_owner = NULL; |
146 | info->attr.mq_maxmsg = attr->mq_maxmsg; | 142 | info->qsize = 0; |
147 | info->attr.mq_msgsize = attr->mq_msgsize; | 143 | info->user = NULL; /* set when all is ok */ |
148 | } | 144 | memset(&info->attr, 0, sizeof(info->attr)); |
149 | mq_msg_tblsz = info->attr.mq_maxmsg * sizeof(struct msg_msg *); | 145 | info->attr.mq_maxmsg = ipc_ns->mq_msg_max; |
150 | info->messages = kmalloc(mq_msg_tblsz, GFP_KERNEL); | 146 | info->attr.mq_msgsize = ipc_ns->mq_msgsize_max; |
151 | if (!info->messages) | 147 | if (attr) { |
152 | goto out_inode; | 148 | info->attr.mq_maxmsg = attr->mq_maxmsg; |
153 | 149 | info->attr.mq_msgsize = attr->mq_msgsize; | |
154 | mq_bytes = (mq_msg_tblsz + | 150 | } |
155 | (info->attr.mq_maxmsg * info->attr.mq_msgsize)); | 151 | mq_msg_tblsz = info->attr.mq_maxmsg * sizeof(struct msg_msg *); |
156 | 152 | info->messages = kmalloc(mq_msg_tblsz, GFP_KERNEL); | |
157 | spin_lock(&mq_lock); | 153 | if (!info->messages) |
158 | if (u->mq_bytes + mq_bytes < u->mq_bytes || | 154 | goto out_inode; |
159 | u->mq_bytes + mq_bytes > | ||
160 | task_rlimit(p, RLIMIT_MSGQUEUE)) { | ||
161 | spin_unlock(&mq_lock); | ||
162 | /* mqueue_evict_inode() releases info->messages */ | ||
163 | goto out_inode; | ||
164 | } | ||
165 | u->mq_bytes += mq_bytes; | ||
166 | spin_unlock(&mq_lock); | ||
167 | 155 | ||
168 | /* all is ok */ | 156 | mq_bytes = (mq_msg_tblsz + |
169 | info->user = get_uid(u); | 157 | (info->attr.mq_maxmsg * info->attr.mq_msgsize)); |
170 | } else if (S_ISDIR(mode)) { | 158 | |
171 | inc_nlink(inode); | 159 | spin_lock(&mq_lock); |
172 | /* Some things misbehave if size == 0 on a directory */ | 160 | if (u->mq_bytes + mq_bytes < u->mq_bytes || |
173 | inode->i_size = 2 * DIRENT_SIZE; | 161 | u->mq_bytes + mq_bytes > task_rlimit(p, RLIMIT_MSGQUEUE)) { |
174 | inode->i_op = &mqueue_dir_inode_operations; | 162 | spin_unlock(&mq_lock); |
175 | inode->i_fop = &simple_dir_operations; | 163 | /* mqueue_evict_inode() releases info->messages */ |
164 | ret = -EMFILE; | ||
165 | goto out_inode; | ||
176 | } | 166 | } |
167 | u->mq_bytes += mq_bytes; | ||
168 | spin_unlock(&mq_lock); | ||
169 | |||
170 | /* all is ok */ | ||
171 | info->user = get_uid(u); | ||
172 | } else if (S_ISDIR(mode)) { | ||
173 | inc_nlink(inode); | ||
174 | /* Some things misbehave if size == 0 on a directory */ | ||
175 | inode->i_size = 2 * DIRENT_SIZE; | ||
176 | inode->i_op = &mqueue_dir_inode_operations; | ||
177 | inode->i_fop = &simple_dir_operations; | ||
177 | } | 178 | } |
179 | |||
178 | return inode; | 180 | return inode; |
179 | out_inode: | 181 | out_inode: |
180 | iput(inode); | 182 | iput(inode); |
181 | return NULL; | 183 | err: |
184 | return ERR_PTR(ret); | ||
182 | } | 185 | } |
183 | 186 | ||
184 | static int mqueue_fill_super(struct super_block *sb, void *data, int silent) | 187 | static int mqueue_fill_super(struct super_block *sb, void *data, int silent) |
@@ -194,8 +197,8 @@ static int mqueue_fill_super(struct super_block *sb, void *data, int silent) | |||
194 | 197 | ||
195 | inode = mqueue_get_inode(sb, ns, S_IFDIR | S_ISVTX | S_IRWXUGO, | 198 | inode = mqueue_get_inode(sb, ns, S_IFDIR | S_ISVTX | S_IRWXUGO, |
196 | NULL); | 199 | NULL); |
197 | if (!inode) { | 200 | if (IS_ERR(inode)) { |
198 | error = -ENOMEM; | 201 | error = PTR_ERR(inode); |
199 | goto out; | 202 | goto out; |
200 | } | 203 | } |
201 | 204 | ||
@@ -315,8 +318,8 @@ static int mqueue_create(struct inode *dir, struct dentry *dentry, | |||
315 | spin_unlock(&mq_lock); | 318 | spin_unlock(&mq_lock); |
316 | 319 | ||
317 | inode = mqueue_get_inode(dir->i_sb, ipc_ns, mode, attr); | 320 | inode = mqueue_get_inode(dir->i_sb, ipc_ns, mode, attr); |
318 | if (!inode) { | 321 | if (IS_ERR(inode)) { |
319 | error = -ENOMEM; | 322 | error = PTR_ERR(inode); |
320 | spin_lock(&mq_lock); | 323 | spin_lock(&mq_lock); |
321 | ipc_ns->mq_queues_count--; | 324 | ipc_ns->mq_queues_count--; |
322 | goto out_unlock; | 325 | goto out_unlock; |
@@ -74,6 +74,7 @@ void shm_init_ns(struct ipc_namespace *ns) | |||
74 | ns->shm_ctlmax = SHMMAX; | 74 | ns->shm_ctlmax = SHMMAX; |
75 | ns->shm_ctlall = SHMALL; | 75 | ns->shm_ctlall = SHMALL; |
76 | ns->shm_ctlmni = SHMMNI; | 76 | ns->shm_ctlmni = SHMMNI; |
77 | ns->shm_rmid_forced = 0; | ||
77 | ns->shm_tot = 0; | 78 | ns->shm_tot = 0; |
78 | ipc_init_ids(&shm_ids(ns)); | 79 | ipc_init_ids(&shm_ids(ns)); |
79 | } | 80 | } |
@@ -104,9 +105,16 @@ void shm_exit_ns(struct ipc_namespace *ns) | |||
104 | } | 105 | } |
105 | #endif | 106 | #endif |
106 | 107 | ||
107 | void __init shm_init (void) | 108 | static int __init ipc_ns_init(void) |
108 | { | 109 | { |
109 | shm_init_ns(&init_ipc_ns); | 110 | shm_init_ns(&init_ipc_ns); |
111 | return 0; | ||
112 | } | ||
113 | |||
114 | pure_initcall(ipc_ns_init); | ||
115 | |||
116 | void __init shm_init (void) | ||
117 | { | ||
110 | ipc_init_proc_interface("sysvipc/shm", | 118 | ipc_init_proc_interface("sysvipc/shm", |
111 | #if BITS_PER_LONG <= 32 | 119 | #if BITS_PER_LONG <= 32 |
112 | " key shmid perms size cpid lpid nattch uid gid cuid cgid atime dtime ctime rss swap\n", | 120 | " key shmid perms size cpid lpid nattch uid gid cuid cgid atime dtime ctime rss swap\n", |
@@ -130,6 +138,12 @@ static inline struct shmid_kernel *shm_lock(struct ipc_namespace *ns, int id) | |||
130 | return container_of(ipcp, struct shmid_kernel, shm_perm); | 138 | return container_of(ipcp, struct shmid_kernel, shm_perm); |
131 | } | 139 | } |
132 | 140 | ||
141 | static inline void shm_lock_by_ptr(struct shmid_kernel *ipcp) | ||
142 | { | ||
143 | rcu_read_lock(); | ||
144 | spin_lock(&ipcp->shm_perm.lock); | ||
145 | } | ||
146 | |||
133 | static inline struct shmid_kernel *shm_lock_check(struct ipc_namespace *ns, | 147 | static inline struct shmid_kernel *shm_lock_check(struct ipc_namespace *ns, |
134 | int id) | 148 | int id) |
135 | { | 149 | { |
@@ -187,6 +201,23 @@ static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp) | |||
187 | } | 201 | } |
188 | 202 | ||
189 | /* | 203 | /* |
204 | * shm_may_destroy - identifies whether shm segment should be destroyed now | ||
205 | * | ||
206 | * Returns true if and only if there are no active users of the segment and | ||
207 | * one of the following is true: | ||
208 | * | ||
209 | * 1) shmctl(id, IPC_RMID, NULL) was called for this shp | ||
210 | * | ||
211 | * 2) sysctl kernel.shm_rmid_forced is set to 1. | ||
212 | */ | ||
213 | static bool shm_may_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp) | ||
214 | { | ||
215 | return (shp->shm_nattch == 0) && | ||
216 | (ns->shm_rmid_forced || | ||
217 | (shp->shm_perm.mode & SHM_DEST)); | ||
218 | } | ||
219 | |||
220 | /* | ||
190 | * remove the attach descriptor vma. | 221 | * remove the attach descriptor vma. |
191 | * free memory for segment if it is marked destroyed. | 222 | * free memory for segment if it is marked destroyed. |
192 | * The descriptor has already been removed from the current->mm->mmap list | 223 | * The descriptor has already been removed from the current->mm->mmap list |
@@ -206,14 +237,90 @@ static void shm_close(struct vm_area_struct *vma) | |||
206 | shp->shm_lprid = task_tgid_vnr(current); | 237 | shp->shm_lprid = task_tgid_vnr(current); |
207 | shp->shm_dtim = get_seconds(); | 238 | shp->shm_dtim = get_seconds(); |
208 | shp->shm_nattch--; | 239 | shp->shm_nattch--; |
209 | if(shp->shm_nattch == 0 && | 240 | if (shm_may_destroy(ns, shp)) |
210 | shp->shm_perm.mode & SHM_DEST) | ||
211 | shm_destroy(ns, shp); | 241 | shm_destroy(ns, shp); |
212 | else | 242 | else |
213 | shm_unlock(shp); | 243 | shm_unlock(shp); |
214 | up_write(&shm_ids(ns).rw_mutex); | 244 | up_write(&shm_ids(ns).rw_mutex); |
215 | } | 245 | } |
216 | 246 | ||
247 | /* Called with ns->shm_ids(ns).rw_mutex locked */ | ||
248 | static int shm_try_destroy_current(int id, void *p, void *data) | ||
249 | { | ||
250 | struct ipc_namespace *ns = data; | ||
251 | struct kern_ipc_perm *ipcp = p; | ||
252 | struct shmid_kernel *shp = container_of(ipcp, struct shmid_kernel, shm_perm); | ||
253 | |||
254 | if (shp->shm_creator != current) | ||
255 | return 0; | ||
256 | |||
257 | /* | ||
258 | * Mark it as orphaned to destroy the segment when | ||
259 | * kernel.shm_rmid_forced is changed. | ||
260 | * It is noop if the following shm_may_destroy() returns true. | ||
261 | */ | ||
262 | shp->shm_creator = NULL; | ||
263 | |||
264 | /* | ||
265 | * Don't even try to destroy it. If shm_rmid_forced=0 and IPC_RMID | ||
266 | * is not set, it shouldn't be deleted here. | ||
267 | */ | ||
268 | if (!ns->shm_rmid_forced) | ||
269 | return 0; | ||
270 | |||
271 | if (shm_may_destroy(ns, shp)) { | ||
272 | shm_lock_by_ptr(shp); | ||
273 | shm_destroy(ns, shp); | ||
274 | } | ||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | /* Called with ns->shm_ids(ns).rw_mutex locked */ | ||
279 | static int shm_try_destroy_orphaned(int id, void *p, void *data) | ||
280 | { | ||
281 | struct ipc_namespace *ns = data; | ||
282 | struct kern_ipc_perm *ipcp = p; | ||
283 | struct shmid_kernel *shp = container_of(ipcp, struct shmid_kernel, shm_perm); | ||
284 | |||
285 | /* | ||
286 | * We want to destroy segments without users and with already | ||
287 | * exit'ed originating process. | ||
288 | * | ||
289 | * As shp->* are changed under rw_mutex, it's safe to skip shp locking. | ||
290 | */ | ||
291 | if (shp->shm_creator != NULL) | ||
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 | void shm_destroy_orphaned(struct ipc_namespace *ns) | ||
302 | { | ||
303 | down_write(&shm_ids(ns).rw_mutex); | ||
304 | if (shm_ids(ns).in_use) | ||
305 | idr_for_each(&shm_ids(ns).ipcs_idr, &shm_try_destroy_orphaned, ns); | ||
306 | up_write(&shm_ids(ns).rw_mutex); | ||
307 | } | ||
308 | |||
309 | |||
310 | void exit_shm(struct task_struct *task) | ||
311 | { | ||
312 | struct ipc_namespace *ns = task->nsproxy->ipc_ns; | ||
313 | |||
314 | if (shm_ids(ns).in_use == 0) | ||
315 | return; | ||
316 | |||
317 | /* Destroy all already created segments, but not mapped yet */ | ||
318 | down_write(&shm_ids(ns).rw_mutex); | ||
319 | if (shm_ids(ns).in_use) | ||
320 | idr_for_each(&shm_ids(ns).ipcs_idr, &shm_try_destroy_current, ns); | ||
321 | up_write(&shm_ids(ns).rw_mutex); | ||
322 | } | ||
323 | |||
217 | static int shm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) | 324 | static int shm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) |
218 | { | 325 | { |
219 | struct file *file = vma->vm_file; | 326 | struct file *file = vma->vm_file; |
@@ -404,6 +511,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params) | |||
404 | shp->shm_segsz = size; | 511 | shp->shm_segsz = size; |
405 | shp->shm_nattch = 0; | 512 | shp->shm_nattch = 0; |
406 | shp->shm_file = file; | 513 | shp->shm_file = file; |
514 | shp->shm_creator = current; | ||
407 | /* | 515 | /* |
408 | * shmid gets reported as "inode#" in /proc/pid/maps. | 516 | * shmid gets reported as "inode#" in /proc/pid/maps. |
409 | * proc-ps tools use this. Changing this will break them. | 517 | * proc-ps tools use this. Changing this will break them. |
@@ -950,8 +1058,7 @@ out_nattch: | |||
950 | shp = shm_lock(ns, shmid); | 1058 | shp = shm_lock(ns, shmid); |
951 | BUG_ON(IS_ERR(shp)); | 1059 | BUG_ON(IS_ERR(shp)); |
952 | shp->shm_nattch--; | 1060 | shp->shm_nattch--; |
953 | if(shp->shm_nattch == 0 && | 1061 | if (shm_may_destroy(ns, shp)) |
954 | shp->shm_perm.mode & SHM_DEST) | ||
955 | shm_destroy(ns, shp); | 1062 | shm_destroy(ns, shp); |
956 | else | 1063 | else |
957 | shm_unlock(shp); | 1064 | shm_unlock(shp); |