diff options
Diffstat (limited to 'ipc')
-rw-r--r-- | ipc/ipc_sysctl.c | 72 | ||||
-rw-r--r-- | ipc/ipcns_notifier.c | 20 | ||||
-rw-r--r-- | ipc/mqueue.c | 31 | ||||
-rw-r--r-- | ipc/sem.c | 316 | ||||
-rw-r--r-- | ipc/shm.c | 24 | ||||
-rw-r--r-- | ipc/util.c | 61 | ||||
-rw-r--r-- | ipc/util.h | 6 |
7 files changed, 263 insertions, 267 deletions
diff --git a/ipc/ipc_sysctl.c b/ipc/ipc_sysctl.c index d3497465cc0a..69bc85978ba0 100644 --- a/ipc/ipc_sysctl.c +++ b/ipc/ipc_sysctl.c | |||
@@ -27,15 +27,17 @@ static void *get_ipc(ctl_table *table) | |||
27 | } | 27 | } |
28 | 28 | ||
29 | /* | 29 | /* |
30 | * Routine that is called when a tunable has successfully been changed by | 30 | * Routine that is called when the file "auto_msgmni" has successfully been |
31 | * hand and it has a callback routine registered on the ipc namespace notifier | 31 | * written. |
32 | * chain: we don't want such tunables to be recomputed anymore upon memory | 32 | * Two values are allowed: |
33 | * add/remove or ipc namespace creation/removal. | 33 | * 0: unregister msgmni's callback routine from the ipc namespace notifier |
34 | * They can come back to a recomputable state by being set to a <0 value. | 34 | * chain. This means that msgmni won't be recomputed anymore upon memory |
35 | * add/remove or ipc namespace creation/removal. | ||
36 | * 1: register back the callback routine. | ||
35 | */ | 37 | */ |
36 | static void tunable_set_callback(int val) | 38 | static void ipc_auto_callback(int val) |
37 | { | 39 | { |
38 | if (val >= 0) | 40 | if (!val) |
39 | unregister_ipcns_notifier(current->nsproxy->ipc_ns); | 41 | unregister_ipcns_notifier(current->nsproxy->ipc_ns); |
40 | else { | 42 | else { |
41 | /* | 43 | /* |
@@ -71,7 +73,12 @@ static int proc_ipc_callback_dointvec(ctl_table *table, int write, | |||
71 | rc = proc_dointvec(&ipc_table, write, filp, buffer, lenp, ppos); | 73 | rc = proc_dointvec(&ipc_table, write, filp, buffer, lenp, ppos); |
72 | 74 | ||
73 | if (write && !rc && lenp_bef == *lenp) | 75 | if (write && !rc && lenp_bef == *lenp) |
74 | tunable_set_callback(*((int *)(ipc_table.data))); | 76 | /* |
77 | * Tunable has successfully been changed by hand. Disable its | ||
78 | * automatic adjustment. This simply requires unregistering | ||
79 | * the notifiers that trigger recalculation. | ||
80 | */ | ||
81 | unregister_ipcns_notifier(current->nsproxy->ipc_ns); | ||
75 | 82 | ||
76 | return rc; | 83 | return rc; |
77 | } | 84 | } |
@@ -87,10 +94,39 @@ static int proc_ipc_doulongvec_minmax(ctl_table *table, int write, | |||
87 | lenp, ppos); | 94 | lenp, ppos); |
88 | } | 95 | } |
89 | 96 | ||
97 | static int proc_ipcauto_dointvec_minmax(ctl_table *table, int write, | ||
98 | struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos) | ||
99 | { | ||
100 | struct ctl_table ipc_table; | ||
101 | size_t lenp_bef = *lenp; | ||
102 | int oldval; | ||
103 | int rc; | ||
104 | |||
105 | memcpy(&ipc_table, table, sizeof(ipc_table)); | ||
106 | ipc_table.data = get_ipc(table); | ||
107 | oldval = *((int *)(ipc_table.data)); | ||
108 | |||
109 | rc = proc_dointvec_minmax(&ipc_table, write, filp, buffer, lenp, ppos); | ||
110 | |||
111 | if (write && !rc && lenp_bef == *lenp) { | ||
112 | int newval = *((int *)(ipc_table.data)); | ||
113 | /* | ||
114 | * The file "auto_msgmni" has correctly been set. | ||
115 | * React by (un)registering the corresponding tunable, if the | ||
116 | * value has changed. | ||
117 | */ | ||
118 | if (newval != oldval) | ||
119 | ipc_auto_callback(newval); | ||
120 | } | ||
121 | |||
122 | return rc; | ||
123 | } | ||
124 | |||
90 | #else | 125 | #else |
91 | #define proc_ipc_doulongvec_minmax NULL | 126 | #define proc_ipc_doulongvec_minmax NULL |
92 | #define proc_ipc_dointvec NULL | 127 | #define proc_ipc_dointvec NULL |
93 | #define proc_ipc_callback_dointvec NULL | 128 | #define proc_ipc_callback_dointvec NULL |
129 | #define proc_ipcauto_dointvec_minmax NULL | ||
94 | #endif | 130 | #endif |
95 | 131 | ||
96 | #ifdef CONFIG_SYSCTL_SYSCALL | 132 | #ifdef CONFIG_SYSCTL_SYSCALL |
@@ -142,14 +178,11 @@ static int sysctl_ipc_registered_data(ctl_table *table, int __user *name, | |||
142 | rc = sysctl_ipc_data(table, name, nlen, oldval, oldlenp, newval, | 178 | rc = sysctl_ipc_data(table, name, nlen, oldval, oldlenp, newval, |
143 | newlen); | 179 | newlen); |
144 | 180 | ||
145 | if (newval && newlen && rc > 0) { | 181 | if (newval && newlen && rc > 0) |
146 | /* | 182 | /* |
147 | * Tunable has successfully been changed from userland | 183 | * Tunable has successfully been changed from userland |
148 | */ | 184 | */ |
149 | int *data = get_ipc(table); | 185 | unregister_ipcns_notifier(current->nsproxy->ipc_ns); |
150 | |||
151 | tunable_set_callback(*data); | ||
152 | } | ||
153 | 186 | ||
154 | return rc; | 187 | return rc; |
155 | } | 188 | } |
@@ -158,6 +191,9 @@ static int sysctl_ipc_registered_data(ctl_table *table, int __user *name, | |||
158 | #define sysctl_ipc_registered_data NULL | 191 | #define sysctl_ipc_registered_data NULL |
159 | #endif | 192 | #endif |
160 | 193 | ||
194 | static int zero; | ||
195 | static int one = 1; | ||
196 | |||
161 | static struct ctl_table ipc_kern_table[] = { | 197 | static struct ctl_table ipc_kern_table[] = { |
162 | { | 198 | { |
163 | .ctl_name = KERN_SHMMAX, | 199 | .ctl_name = KERN_SHMMAX, |
@@ -222,6 +258,16 @@ static struct ctl_table ipc_kern_table[] = { | |||
222 | .proc_handler = proc_ipc_dointvec, | 258 | .proc_handler = proc_ipc_dointvec, |
223 | .strategy = sysctl_ipc_data, | 259 | .strategy = sysctl_ipc_data, |
224 | }, | 260 | }, |
261 | { | ||
262 | .ctl_name = CTL_UNNUMBERED, | ||
263 | .procname = "auto_msgmni", | ||
264 | .data = &init_ipc_ns.auto_msgmni, | ||
265 | .maxlen = sizeof(int), | ||
266 | .mode = 0644, | ||
267 | .proc_handler = proc_ipcauto_dointvec_minmax, | ||
268 | .extra1 = &zero, | ||
269 | .extra2 = &one, | ||
270 | }, | ||
225 | {} | 271 | {} |
226 | }; | 272 | }; |
227 | 273 | ||
diff --git a/ipc/ipcns_notifier.c b/ipc/ipcns_notifier.c index 70ff09183f7b..b9b31a4f77e1 100644 --- a/ipc/ipcns_notifier.c +++ b/ipc/ipcns_notifier.c | |||
@@ -55,25 +55,35 @@ static int ipcns_callback(struct notifier_block *self, | |||
55 | 55 | ||
56 | int register_ipcns_notifier(struct ipc_namespace *ns) | 56 | int register_ipcns_notifier(struct ipc_namespace *ns) |
57 | { | 57 | { |
58 | int rc; | ||
59 | |||
58 | memset(&ns->ipcns_nb, 0, sizeof(ns->ipcns_nb)); | 60 | memset(&ns->ipcns_nb, 0, sizeof(ns->ipcns_nb)); |
59 | ns->ipcns_nb.notifier_call = ipcns_callback; | 61 | ns->ipcns_nb.notifier_call = ipcns_callback; |
60 | ns->ipcns_nb.priority = IPCNS_CALLBACK_PRI; | 62 | ns->ipcns_nb.priority = IPCNS_CALLBACK_PRI; |
61 | return blocking_notifier_chain_register(&ipcns_chain, &ns->ipcns_nb); | 63 | rc = blocking_notifier_chain_register(&ipcns_chain, &ns->ipcns_nb); |
64 | if (!rc) | ||
65 | ns->auto_msgmni = 1; | ||
66 | return rc; | ||
62 | } | 67 | } |
63 | 68 | ||
64 | int cond_register_ipcns_notifier(struct ipc_namespace *ns) | 69 | int cond_register_ipcns_notifier(struct ipc_namespace *ns) |
65 | { | 70 | { |
71 | int rc; | ||
72 | |||
66 | memset(&ns->ipcns_nb, 0, sizeof(ns->ipcns_nb)); | 73 | memset(&ns->ipcns_nb, 0, sizeof(ns->ipcns_nb)); |
67 | ns->ipcns_nb.notifier_call = ipcns_callback; | 74 | ns->ipcns_nb.notifier_call = ipcns_callback; |
68 | ns->ipcns_nb.priority = IPCNS_CALLBACK_PRI; | 75 | ns->ipcns_nb.priority = IPCNS_CALLBACK_PRI; |
69 | return blocking_notifier_chain_cond_register(&ipcns_chain, | 76 | rc = blocking_notifier_chain_cond_register(&ipcns_chain, |
70 | &ns->ipcns_nb); | 77 | &ns->ipcns_nb); |
78 | if (!rc) | ||
79 | ns->auto_msgmni = 1; | ||
80 | return rc; | ||
71 | } | 81 | } |
72 | 82 | ||
73 | int unregister_ipcns_notifier(struct ipc_namespace *ns) | 83 | void unregister_ipcns_notifier(struct ipc_namespace *ns) |
74 | { | 84 | { |
75 | return blocking_notifier_chain_unregister(&ipcns_chain, | 85 | blocking_notifier_chain_unregister(&ipcns_chain, &ns->ipcns_nb); |
76 | &ns->ipcns_nb); | 86 | ns->auto_msgmni = 0; |
77 | } | 87 | } |
78 | 88 | ||
79 | int ipcns_notify(unsigned long val) | 89 | int ipcns_notify(unsigned long val) |
diff --git a/ipc/mqueue.c b/ipc/mqueue.c index b3b69fd51330..96fb36cd9874 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c | |||
@@ -207,7 +207,7 @@ static int mqueue_get_sb(struct file_system_type *fs_type, | |||
207 | return get_sb_single(fs_type, flags, data, mqueue_fill_super, mnt); | 207 | return get_sb_single(fs_type, flags, data, mqueue_fill_super, mnt); |
208 | } | 208 | } |
209 | 209 | ||
210 | static void init_once(struct kmem_cache *cachep, void *foo) | 210 | static void init_once(void *foo) |
211 | { | 211 | { |
212 | struct mqueue_inode_info *p = (struct mqueue_inode_info *) foo; | 212 | struct mqueue_inode_info *p = (struct mqueue_inode_info *) foo; |
213 | 213 | ||
@@ -314,15 +314,11 @@ static int mqueue_unlink(struct inode *dir, struct dentry *dentry) | |||
314 | * through std routines) | 314 | * through std routines) |
315 | */ | 315 | */ |
316 | static ssize_t mqueue_read_file(struct file *filp, char __user *u_data, | 316 | static ssize_t mqueue_read_file(struct file *filp, char __user *u_data, |
317 | size_t count, loff_t * off) | 317 | size_t count, loff_t *off) |
318 | { | 318 | { |
319 | struct mqueue_inode_info *info = MQUEUE_I(filp->f_path.dentry->d_inode); | 319 | struct mqueue_inode_info *info = MQUEUE_I(filp->f_path.dentry->d_inode); |
320 | char buffer[FILENT_SIZE]; | 320 | char buffer[FILENT_SIZE]; |
321 | size_t slen; | 321 | ssize_t ret; |
322 | loff_t o; | ||
323 | |||
324 | if (!count) | ||
325 | return 0; | ||
326 | 322 | ||
327 | spin_lock(&info->lock); | 323 | spin_lock(&info->lock); |
328 | snprintf(buffer, sizeof(buffer), | 324 | snprintf(buffer, sizeof(buffer), |
@@ -335,21 +331,14 @@ static ssize_t mqueue_read_file(struct file *filp, char __user *u_data, | |||
335 | pid_vnr(info->notify_owner)); | 331 | pid_vnr(info->notify_owner)); |
336 | spin_unlock(&info->lock); | 332 | spin_unlock(&info->lock); |
337 | buffer[sizeof(buffer)-1] = '\0'; | 333 | buffer[sizeof(buffer)-1] = '\0'; |
338 | slen = strlen(buffer)+1; | ||
339 | |||
340 | o = *off; | ||
341 | if (o > slen) | ||
342 | return 0; | ||
343 | |||
344 | if (o + count > slen) | ||
345 | count = slen - o; | ||
346 | 334 | ||
347 | if (copy_to_user(u_data, buffer + o, count)) | 335 | ret = simple_read_from_buffer(u_data, count, off, buffer, |
348 | return -EFAULT; | 336 | strlen(buffer)); |
337 | if (ret <= 0) | ||
338 | return ret; | ||
349 | 339 | ||
350 | *off = o + count; | ||
351 | filp->f_path.dentry->d_inode->i_atime = filp->f_path.dentry->d_inode->i_ctime = CURRENT_TIME; | 340 | filp->f_path.dentry->d_inode->i_atime = filp->f_path.dentry->d_inode->i_ctime = CURRENT_TIME; |
352 | return count; | 341 | return ret; |
353 | } | 342 | } |
354 | 343 | ||
355 | static int mqueue_flush_file(struct file *filp, fl_owner_t id) | 344 | static int mqueue_flush_file(struct file *filp, fl_owner_t id) |
@@ -649,7 +638,7 @@ static int oflag2acc[O_ACCMODE] = { MAY_READ, MAY_WRITE, | |||
649 | return ERR_PTR(-EINVAL); | 638 | return ERR_PTR(-EINVAL); |
650 | } | 639 | } |
651 | 640 | ||
652 | if (permission(dentry->d_inode, oflag2acc[oflag & O_ACCMODE], NULL)) { | 641 | if (inode_permission(dentry->d_inode, oflag2acc[oflag & O_ACCMODE])) { |
653 | dput(dentry); | 642 | dput(dentry); |
654 | mntput(mqueue_mnt); | 643 | mntput(mqueue_mnt); |
655 | return ERR_PTR(-EACCES); | 644 | return ERR_PTR(-EACCES); |
@@ -1054,7 +1043,7 @@ retry: | |||
1054 | } | 1043 | } |
1055 | 1044 | ||
1056 | timeo = MAX_SCHEDULE_TIMEOUT; | 1045 | timeo = MAX_SCHEDULE_TIMEOUT; |
1057 | ret = netlink_attachskb(sock, nc, 0, &timeo, NULL); | 1046 | ret = netlink_attachskb(sock, nc, &timeo, NULL); |
1058 | if (ret == 1) | 1047 | if (ret == 1) |
1059 | goto retry; | 1048 | goto retry; |
1060 | if (ret) { | 1049 | if (ret) { |
@@ -272,9 +272,8 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params) | |||
272 | ns->used_sems += nsems; | 272 | ns->used_sems += nsems; |
273 | 273 | ||
274 | sma->sem_base = (struct sem *) &sma[1]; | 274 | sma->sem_base = (struct sem *) &sma[1]; |
275 | /* sma->sem_pending = NULL; */ | 275 | INIT_LIST_HEAD(&sma->sem_pending); |
276 | sma->sem_pending_last = &sma->sem_pending; | 276 | INIT_LIST_HEAD(&sma->list_id); |
277 | /* sma->undo = NULL; */ | ||
278 | sma->sem_nsems = nsems; | 277 | sma->sem_nsems = nsems; |
279 | sma->sem_ctime = get_seconds(); | 278 | sma->sem_ctime = get_seconds(); |
280 | sem_unlock(sma); | 279 | sem_unlock(sma); |
@@ -331,38 +330,6 @@ asmlinkage long sys_semget(key_t key, int nsems, int semflg) | |||
331 | return ipcget(ns, &sem_ids(ns), &sem_ops, &sem_params); | 330 | return ipcget(ns, &sem_ids(ns), &sem_ops, &sem_params); |
332 | } | 331 | } |
333 | 332 | ||
334 | /* Manage the doubly linked list sma->sem_pending as a FIFO: | ||
335 | * insert new queue elements at the tail sma->sem_pending_last. | ||
336 | */ | ||
337 | static inline void append_to_queue (struct sem_array * sma, | ||
338 | struct sem_queue * q) | ||
339 | { | ||
340 | *(q->prev = sma->sem_pending_last) = q; | ||
341 | *(sma->sem_pending_last = &q->next) = NULL; | ||
342 | } | ||
343 | |||
344 | static inline void prepend_to_queue (struct sem_array * sma, | ||
345 | struct sem_queue * q) | ||
346 | { | ||
347 | q->next = sma->sem_pending; | ||
348 | *(q->prev = &sma->sem_pending) = q; | ||
349 | if (q->next) | ||
350 | q->next->prev = &q->next; | ||
351 | else /* sma->sem_pending_last == &sma->sem_pending */ | ||
352 | sma->sem_pending_last = &q->next; | ||
353 | } | ||
354 | |||
355 | static inline void remove_from_queue (struct sem_array * sma, | ||
356 | struct sem_queue * q) | ||
357 | { | ||
358 | *(q->prev) = q->next; | ||
359 | if (q->next) | ||
360 | q->next->prev = q->prev; | ||
361 | else /* sma->sem_pending_last == &q->next */ | ||
362 | sma->sem_pending_last = q->prev; | ||
363 | q->prev = NULL; /* mark as removed */ | ||
364 | } | ||
365 | |||
366 | /* | 333 | /* |
367 | * Determine whether a sequence of semaphore operations would succeed | 334 | * Determine whether a sequence of semaphore operations would succeed |
368 | * all at once. Return 0 if yes, 1 if need to sleep, else return error code. | 335 | * all at once. Return 0 if yes, 1 if need to sleep, else return error code. |
@@ -438,16 +405,15 @@ static void update_queue (struct sem_array * sma) | |||
438 | int error; | 405 | int error; |
439 | struct sem_queue * q; | 406 | struct sem_queue * q; |
440 | 407 | ||
441 | q = sma->sem_pending; | 408 | q = list_entry(sma->sem_pending.next, struct sem_queue, list); |
442 | while(q) { | 409 | while (&q->list != &sma->sem_pending) { |
443 | error = try_atomic_semop(sma, q->sops, q->nsops, | 410 | error = try_atomic_semop(sma, q->sops, q->nsops, |
444 | q->undo, q->pid); | 411 | q->undo, q->pid); |
445 | 412 | ||
446 | /* Does q->sleeper still need to sleep? */ | 413 | /* Does q->sleeper still need to sleep? */ |
447 | if (error <= 0) { | 414 | if (error <= 0) { |
448 | struct sem_queue *n; | 415 | struct sem_queue *n; |
449 | remove_from_queue(sma,q); | 416 | |
450 | q->status = IN_WAKEUP; | ||
451 | /* | 417 | /* |
452 | * Continue scanning. The next operation | 418 | * Continue scanning. The next operation |
453 | * that must be checked depends on the type of the | 419 | * that must be checked depends on the type of the |
@@ -458,11 +424,26 @@ static void update_queue (struct sem_array * sma) | |||
458 | * for semaphore values to become 0. | 424 | * for semaphore values to become 0. |
459 | * - if the operation didn't modify the array, | 425 | * - if the operation didn't modify the array, |
460 | * then just continue. | 426 | * then just continue. |
427 | * The order of list_del() and reading ->next | ||
428 | * is crucial: In the former case, the list_del() | ||
429 | * must be done first [because we might be the | ||
430 | * first entry in ->sem_pending], in the latter | ||
431 | * case the list_del() must be done last | ||
432 | * [because the list is invalid after the list_del()] | ||
461 | */ | 433 | */ |
462 | if (q->alter) | 434 | if (q->alter) { |
463 | n = sma->sem_pending; | 435 | list_del(&q->list); |
464 | else | 436 | n = list_entry(sma->sem_pending.next, |
465 | n = q->next; | 437 | struct sem_queue, list); |
438 | } else { | ||
439 | n = list_entry(q->list.next, struct sem_queue, | ||
440 | list); | ||
441 | list_del(&q->list); | ||
442 | } | ||
443 | |||
444 | /* wake up the waiting thread */ | ||
445 | q->status = IN_WAKEUP; | ||
446 | |||
466 | wake_up_process(q->sleeper); | 447 | wake_up_process(q->sleeper); |
467 | /* hands-off: q will disappear immediately after | 448 | /* hands-off: q will disappear immediately after |
468 | * writing q->status. | 449 | * writing q->status. |
@@ -471,7 +452,7 @@ static void update_queue (struct sem_array * sma) | |||
471 | q->status = error; | 452 | q->status = error; |
472 | q = n; | 453 | q = n; |
473 | } else { | 454 | } else { |
474 | q = q->next; | 455 | q = list_entry(q->list.next, struct sem_queue, list); |
475 | } | 456 | } |
476 | } | 457 | } |
477 | } | 458 | } |
@@ -491,7 +472,7 @@ static int count_semncnt (struct sem_array * sma, ushort semnum) | |||
491 | struct sem_queue * q; | 472 | struct sem_queue * q; |
492 | 473 | ||
493 | semncnt = 0; | 474 | semncnt = 0; |
494 | for (q = sma->sem_pending; q; q = q->next) { | 475 | list_for_each_entry(q, &sma->sem_pending, list) { |
495 | struct sembuf * sops = q->sops; | 476 | struct sembuf * sops = q->sops; |
496 | int nsops = q->nsops; | 477 | int nsops = q->nsops; |
497 | int i; | 478 | int i; |
@@ -503,13 +484,14 @@ static int count_semncnt (struct sem_array * sma, ushort semnum) | |||
503 | } | 484 | } |
504 | return semncnt; | 485 | return semncnt; |
505 | } | 486 | } |
487 | |||
506 | static int count_semzcnt (struct sem_array * sma, ushort semnum) | 488 | static int count_semzcnt (struct sem_array * sma, ushort semnum) |
507 | { | 489 | { |
508 | int semzcnt; | 490 | int semzcnt; |
509 | struct sem_queue * q; | 491 | struct sem_queue * q; |
510 | 492 | ||
511 | semzcnt = 0; | 493 | semzcnt = 0; |
512 | for (q = sma->sem_pending; q; q = q->next) { | 494 | list_for_each_entry(q, &sma->sem_pending, list) { |
513 | struct sembuf * sops = q->sops; | 495 | struct sembuf * sops = q->sops; |
514 | int nsops = q->nsops; | 496 | int nsops = q->nsops; |
515 | int i; | 497 | int i; |
@@ -522,35 +504,41 @@ static int count_semzcnt (struct sem_array * sma, ushort semnum) | |||
522 | return semzcnt; | 504 | return semzcnt; |
523 | } | 505 | } |
524 | 506 | ||
507 | void free_un(struct rcu_head *head) | ||
508 | { | ||
509 | struct sem_undo *un = container_of(head, struct sem_undo, rcu); | ||
510 | kfree(un); | ||
511 | } | ||
512 | |||
525 | /* Free a semaphore set. freeary() is called with sem_ids.rw_mutex locked | 513 | /* Free a semaphore set. freeary() is called with sem_ids.rw_mutex locked |
526 | * as a writer and the spinlock for this semaphore set hold. sem_ids.rw_mutex | 514 | * as a writer and the spinlock for this semaphore set hold. sem_ids.rw_mutex |
527 | * remains locked on exit. | 515 | * remains locked on exit. |
528 | */ | 516 | */ |
529 | static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) | 517 | static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) |
530 | { | 518 | { |
531 | struct sem_undo *un; | 519 | struct sem_undo *un, *tu; |
532 | struct sem_queue *q; | 520 | struct sem_queue *q, *tq; |
533 | struct sem_array *sma = container_of(ipcp, struct sem_array, sem_perm); | 521 | struct sem_array *sma = container_of(ipcp, struct sem_array, sem_perm); |
534 | 522 | ||
535 | /* Invalidate the existing undo structures for this semaphore set. | 523 | /* Free the existing undo structures for this semaphore set. */ |
536 | * (They will be freed without any further action in exit_sem() | 524 | assert_spin_locked(&sma->sem_perm.lock); |
537 | * or during the next semop.) | 525 | list_for_each_entry_safe(un, tu, &sma->list_id, list_id) { |
538 | */ | 526 | list_del(&un->list_id); |
539 | for (un = sma->undo; un; un = un->id_next) | 527 | spin_lock(&un->ulp->lock); |
540 | un->semid = -1; | 528 | un->semid = -1; |
529 | list_del_rcu(&un->list_proc); | ||
530 | spin_unlock(&un->ulp->lock); | ||
531 | call_rcu(&un->rcu, free_un); | ||
532 | } | ||
541 | 533 | ||
542 | /* Wake up all pending processes and let them fail with EIDRM. */ | 534 | /* Wake up all pending processes and let them fail with EIDRM. */ |
543 | q = sma->sem_pending; | 535 | list_for_each_entry_safe(q, tq, &sma->sem_pending, list) { |
544 | while(q) { | 536 | list_del(&q->list); |
545 | struct sem_queue *n; | 537 | |
546 | /* lazy remove_from_queue: we are killing the whole queue */ | ||
547 | q->prev = NULL; | ||
548 | n = q->next; | ||
549 | q->status = IN_WAKEUP; | 538 | q->status = IN_WAKEUP; |
550 | wake_up_process(q->sleeper); /* doesn't sleep */ | 539 | wake_up_process(q->sleeper); /* doesn't sleep */ |
551 | smp_wmb(); | 540 | smp_wmb(); |
552 | q->status = -EIDRM; /* hands-off q */ | 541 | q->status = -EIDRM; /* hands-off q */ |
553 | q = n; | ||
554 | } | 542 | } |
555 | 543 | ||
556 | /* Remove the semaphore set from the IDR */ | 544 | /* Remove the semaphore set from the IDR */ |
@@ -763,9 +751,12 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, | |||
763 | 751 | ||
764 | for (i = 0; i < nsems; i++) | 752 | for (i = 0; i < nsems; i++) |
765 | sma->sem_base[i].semval = sem_io[i]; | 753 | sma->sem_base[i].semval = sem_io[i]; |
766 | for (un = sma->undo; un; un = un->id_next) | 754 | |
755 | assert_spin_locked(&sma->sem_perm.lock); | ||
756 | list_for_each_entry(un, &sma->list_id, list_id) { | ||
767 | for (i = 0; i < nsems; i++) | 757 | for (i = 0; i < nsems; i++) |
768 | un->semadj[i] = 0; | 758 | un->semadj[i] = 0; |
759 | } | ||
769 | sma->sem_ctime = get_seconds(); | 760 | sma->sem_ctime = get_seconds(); |
770 | /* maybe some queued-up processes were waiting for this */ | 761 | /* maybe some queued-up processes were waiting for this */ |
771 | update_queue(sma); | 762 | update_queue(sma); |
@@ -797,12 +788,15 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, | |||
797 | { | 788 | { |
798 | int val = arg.val; | 789 | int val = arg.val; |
799 | struct sem_undo *un; | 790 | struct sem_undo *un; |
791 | |||
800 | err = -ERANGE; | 792 | err = -ERANGE; |
801 | if (val > SEMVMX || val < 0) | 793 | if (val > SEMVMX || val < 0) |
802 | goto out_unlock; | 794 | goto out_unlock; |
803 | 795 | ||
804 | for (un = sma->undo; un; un = un->id_next) | 796 | assert_spin_locked(&sma->sem_perm.lock); |
797 | list_for_each_entry(un, &sma->list_id, list_id) | ||
805 | un->semadj[semnum] = 0; | 798 | un->semadj[semnum] = 0; |
799 | |||
806 | curr->semval = val; | 800 | curr->semval = val; |
807 | curr->sempid = task_tgid_vnr(current); | 801 | curr->sempid = task_tgid_vnr(current); |
808 | sma->sem_ctime = get_seconds(); | 802 | sma->sem_ctime = get_seconds(); |
@@ -952,6 +946,8 @@ static inline int get_undo_list(struct sem_undo_list **undo_listp) | |||
952 | return -ENOMEM; | 946 | return -ENOMEM; |
953 | spin_lock_init(&undo_list->lock); | 947 | spin_lock_init(&undo_list->lock); |
954 | atomic_set(&undo_list->refcnt, 1); | 948 | atomic_set(&undo_list->refcnt, 1); |
949 | INIT_LIST_HEAD(&undo_list->list_proc); | ||
950 | |||
955 | current->sysvsem.undo_list = undo_list; | 951 | current->sysvsem.undo_list = undo_list; |
956 | } | 952 | } |
957 | *undo_listp = undo_list; | 953 | *undo_listp = undo_list; |
@@ -960,25 +956,27 @@ static inline int get_undo_list(struct sem_undo_list **undo_listp) | |||
960 | 956 | ||
961 | static struct sem_undo *lookup_undo(struct sem_undo_list *ulp, int semid) | 957 | static struct sem_undo *lookup_undo(struct sem_undo_list *ulp, int semid) |
962 | { | 958 | { |
963 | struct sem_undo **last, *un; | 959 | struct sem_undo *walk; |
964 | 960 | ||
965 | last = &ulp->proc_list; | 961 | list_for_each_entry_rcu(walk, &ulp->list_proc, list_proc) { |
966 | un = *last; | 962 | if (walk->semid == semid) |
967 | while(un != NULL) { | 963 | return walk; |
968 | if(un->semid==semid) | ||
969 | break; | ||
970 | if(un->semid==-1) { | ||
971 | *last=un->proc_next; | ||
972 | kfree(un); | ||
973 | } else { | ||
974 | last=&un->proc_next; | ||
975 | } | ||
976 | un=*last; | ||
977 | } | 964 | } |
978 | return un; | 965 | return NULL; |
979 | } | 966 | } |
980 | 967 | ||
981 | static struct sem_undo *find_undo(struct ipc_namespace *ns, int semid) | 968 | /** |
969 | * find_alloc_undo - Lookup (and if not present create) undo array | ||
970 | * @ns: namespace | ||
971 | * @semid: semaphore array id | ||
972 | * | ||
973 | * The function looks up (and if not present creates) the undo structure. | ||
974 | * The size of the undo structure depends on the size of the semaphore | ||
975 | * array, thus the alloc path is not that straightforward. | ||
976 | * Lifetime-rules: sem_undo is rcu-protected, on success, the function | ||
977 | * performs a rcu_read_lock(). | ||
978 | */ | ||
979 | static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid) | ||
982 | { | 980 | { |
983 | struct sem_array *sma; | 981 | struct sem_array *sma; |
984 | struct sem_undo_list *ulp; | 982 | struct sem_undo_list *ulp; |
@@ -990,13 +988,16 @@ static struct sem_undo *find_undo(struct ipc_namespace *ns, int semid) | |||
990 | if (error) | 988 | if (error) |
991 | return ERR_PTR(error); | 989 | return ERR_PTR(error); |
992 | 990 | ||
991 | rcu_read_lock(); | ||
993 | spin_lock(&ulp->lock); | 992 | spin_lock(&ulp->lock); |
994 | un = lookup_undo(ulp, semid); | 993 | un = lookup_undo(ulp, semid); |
995 | spin_unlock(&ulp->lock); | 994 | spin_unlock(&ulp->lock); |
996 | if (likely(un!=NULL)) | 995 | if (likely(un!=NULL)) |
997 | goto out; | 996 | goto out; |
997 | rcu_read_unlock(); | ||
998 | 998 | ||
999 | /* no undo structure around - allocate one. */ | 999 | /* no undo structure around - allocate one. */ |
1000 | /* step 1: figure out the size of the semaphore array */ | ||
1000 | sma = sem_lock_check(ns, semid); | 1001 | sma = sem_lock_check(ns, semid); |
1001 | if (IS_ERR(sma)) | 1002 | if (IS_ERR(sma)) |
1002 | return ERR_PTR(PTR_ERR(sma)); | 1003 | return ERR_PTR(PTR_ERR(sma)); |
@@ -1004,37 +1005,45 @@ static struct sem_undo *find_undo(struct ipc_namespace *ns, int semid) | |||
1004 | nsems = sma->sem_nsems; | 1005 | nsems = sma->sem_nsems; |
1005 | sem_getref_and_unlock(sma); | 1006 | sem_getref_and_unlock(sma); |
1006 | 1007 | ||
1008 | /* step 2: allocate new undo structure */ | ||
1007 | new = kzalloc(sizeof(struct sem_undo) + sizeof(short)*nsems, GFP_KERNEL); | 1009 | new = kzalloc(sizeof(struct sem_undo) + sizeof(short)*nsems, GFP_KERNEL); |
1008 | if (!new) { | 1010 | if (!new) { |
1009 | sem_putref(sma); | 1011 | sem_putref(sma); |
1010 | return ERR_PTR(-ENOMEM); | 1012 | return ERR_PTR(-ENOMEM); |
1011 | } | 1013 | } |
1012 | new->semadj = (short *) &new[1]; | ||
1013 | new->semid = semid; | ||
1014 | 1014 | ||
1015 | spin_lock(&ulp->lock); | 1015 | /* step 3: Acquire the lock on semaphore array */ |
1016 | un = lookup_undo(ulp, semid); | ||
1017 | if (un) { | ||
1018 | spin_unlock(&ulp->lock); | ||
1019 | kfree(new); | ||
1020 | sem_putref(sma); | ||
1021 | goto out; | ||
1022 | } | ||
1023 | sem_lock_and_putref(sma); | 1016 | sem_lock_and_putref(sma); |
1024 | if (sma->sem_perm.deleted) { | 1017 | if (sma->sem_perm.deleted) { |
1025 | sem_unlock(sma); | 1018 | sem_unlock(sma); |
1026 | spin_unlock(&ulp->lock); | ||
1027 | kfree(new); | 1019 | kfree(new); |
1028 | un = ERR_PTR(-EIDRM); | 1020 | un = ERR_PTR(-EIDRM); |
1029 | goto out; | 1021 | goto out; |
1030 | } | 1022 | } |
1031 | new->proc_next = ulp->proc_list; | 1023 | spin_lock(&ulp->lock); |
1032 | ulp->proc_list = new; | 1024 | |
1033 | new->id_next = sma->undo; | 1025 | /* |
1034 | sma->undo = new; | 1026 | * step 4: check for races: did someone else allocate the undo struct? |
1035 | sem_unlock(sma); | 1027 | */ |
1028 | un = lookup_undo(ulp, semid); | ||
1029 | if (un) { | ||
1030 | kfree(new); | ||
1031 | goto success; | ||
1032 | } | ||
1033 | /* step 5: initialize & link new undo structure */ | ||
1034 | new->semadj = (short *) &new[1]; | ||
1035 | new->ulp = ulp; | ||
1036 | new->semid = semid; | ||
1037 | assert_spin_locked(&ulp->lock); | ||
1038 | list_add_rcu(&new->list_proc, &ulp->list_proc); | ||
1039 | assert_spin_locked(&sma->sem_perm.lock); | ||
1040 | list_add(&new->list_id, &sma->list_id); | ||
1036 | un = new; | 1041 | un = new; |
1042 | |||
1043 | success: | ||
1037 | spin_unlock(&ulp->lock); | 1044 | spin_unlock(&ulp->lock); |
1045 | rcu_read_lock(); | ||
1046 | sem_unlock(sma); | ||
1038 | out: | 1047 | out: |
1039 | return un; | 1048 | return un; |
1040 | } | 1049 | } |
@@ -1090,9 +1099,8 @@ asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops, | |||
1090 | alter = 1; | 1099 | alter = 1; |
1091 | } | 1100 | } |
1092 | 1101 | ||
1093 | retry_undos: | ||
1094 | if (undos) { | 1102 | if (undos) { |
1095 | un = find_undo(ns, semid); | 1103 | un = find_alloc_undo(ns, semid); |
1096 | if (IS_ERR(un)) { | 1104 | if (IS_ERR(un)) { |
1097 | error = PTR_ERR(un); | 1105 | error = PTR_ERR(un); |
1098 | goto out_free; | 1106 | goto out_free; |
@@ -1102,19 +1110,37 @@ retry_undos: | |||
1102 | 1110 | ||
1103 | sma = sem_lock_check(ns, semid); | 1111 | sma = sem_lock_check(ns, semid); |
1104 | if (IS_ERR(sma)) { | 1112 | if (IS_ERR(sma)) { |
1113 | if (un) | ||
1114 | rcu_read_unlock(); | ||
1105 | error = PTR_ERR(sma); | 1115 | error = PTR_ERR(sma); |
1106 | goto out_free; | 1116 | goto out_free; |
1107 | } | 1117 | } |
1108 | 1118 | ||
1109 | /* | 1119 | /* |
1110 | * semid identifiers are not unique - find_undo may have | 1120 | * semid identifiers are not unique - find_alloc_undo may have |
1111 | * allocated an undo structure, it was invalidated by an RMID | 1121 | * allocated an undo structure, it was invalidated by an RMID |
1112 | * and now a new array with received the same id. Check and retry. | 1122 | * and now a new array with received the same id. Check and fail. |
1123 | * This case can be detected checking un->semid. The existance of | ||
1124 | * "un" itself is guaranteed by rcu. | ||
1113 | */ | 1125 | */ |
1114 | if (un && un->semid == -1) { | 1126 | error = -EIDRM; |
1115 | sem_unlock(sma); | 1127 | if (un) { |
1116 | goto retry_undos; | 1128 | if (un->semid == -1) { |
1129 | rcu_read_unlock(); | ||
1130 | goto out_unlock_free; | ||
1131 | } else { | ||
1132 | /* | ||
1133 | * rcu lock can be released, "un" cannot disappear: | ||
1134 | * - sem_lock is acquired, thus IPC_RMID is | ||
1135 | * impossible. | ||
1136 | * - exit_sem is impossible, it always operates on | ||
1137 | * current (or a dead task). | ||
1138 | */ | ||
1139 | |||
1140 | rcu_read_unlock(); | ||
1141 | } | ||
1117 | } | 1142 | } |
1143 | |||
1118 | error = -EFBIG; | 1144 | error = -EFBIG; |
1119 | if (max >= sma->sem_nsems) | 1145 | if (max >= sma->sem_nsems) |
1120 | goto out_unlock_free; | 1146 | goto out_unlock_free; |
@@ -1138,17 +1164,15 @@ retry_undos: | |||
1138 | * task into the pending queue and go to sleep. | 1164 | * task into the pending queue and go to sleep. |
1139 | */ | 1165 | */ |
1140 | 1166 | ||
1141 | queue.sma = sma; | ||
1142 | queue.sops = sops; | 1167 | queue.sops = sops; |
1143 | queue.nsops = nsops; | 1168 | queue.nsops = nsops; |
1144 | queue.undo = un; | 1169 | queue.undo = un; |
1145 | queue.pid = task_tgid_vnr(current); | 1170 | queue.pid = task_tgid_vnr(current); |
1146 | queue.id = semid; | ||
1147 | queue.alter = alter; | 1171 | queue.alter = alter; |
1148 | if (alter) | 1172 | if (alter) |
1149 | append_to_queue(sma ,&queue); | 1173 | list_add_tail(&queue.list, &sma->sem_pending); |
1150 | else | 1174 | else |
1151 | prepend_to_queue(sma ,&queue); | 1175 | list_add(&queue.list, &sma->sem_pending); |
1152 | 1176 | ||
1153 | queue.status = -EINTR; | 1177 | queue.status = -EINTR; |
1154 | queue.sleeper = current; | 1178 | queue.sleeper = current; |
@@ -1174,7 +1198,6 @@ retry_undos: | |||
1174 | 1198 | ||
1175 | sma = sem_lock(ns, semid); | 1199 | sma = sem_lock(ns, semid); |
1176 | if (IS_ERR(sma)) { | 1200 | if (IS_ERR(sma)) { |
1177 | BUG_ON(queue.prev != NULL); | ||
1178 | error = -EIDRM; | 1201 | error = -EIDRM; |
1179 | goto out_free; | 1202 | goto out_free; |
1180 | } | 1203 | } |
@@ -1192,7 +1215,7 @@ retry_undos: | |||
1192 | */ | 1215 | */ |
1193 | if (timeout && jiffies_left == 0) | 1216 | if (timeout && jiffies_left == 0) |
1194 | error = -EAGAIN; | 1217 | error = -EAGAIN; |
1195 | remove_from_queue(sma,&queue); | 1218 | list_del(&queue.list); |
1196 | goto out_unlock_free; | 1219 | goto out_unlock_free; |
1197 | 1220 | ||
1198 | out_unlock_free: | 1221 | out_unlock_free: |
@@ -1243,56 +1266,62 @@ int copy_semundo(unsigned long clone_flags, struct task_struct *tsk) | |||
1243 | */ | 1266 | */ |
1244 | void exit_sem(struct task_struct *tsk) | 1267 | void exit_sem(struct task_struct *tsk) |
1245 | { | 1268 | { |
1246 | struct sem_undo_list *undo_list; | 1269 | struct sem_undo_list *ulp; |
1247 | struct sem_undo *u, **up; | ||
1248 | struct ipc_namespace *ns; | ||
1249 | 1270 | ||
1250 | undo_list = tsk->sysvsem.undo_list; | 1271 | ulp = tsk->sysvsem.undo_list; |
1251 | if (!undo_list) | 1272 | if (!ulp) |
1252 | return; | 1273 | return; |
1253 | tsk->sysvsem.undo_list = NULL; | 1274 | tsk->sysvsem.undo_list = NULL; |
1254 | 1275 | ||
1255 | if (!atomic_dec_and_test(&undo_list->refcnt)) | 1276 | if (!atomic_dec_and_test(&ulp->refcnt)) |
1256 | return; | 1277 | return; |
1257 | 1278 | ||
1258 | ns = tsk->nsproxy->ipc_ns; | 1279 | for (;;) { |
1259 | /* There's no need to hold the semundo list lock, as current | ||
1260 | * is the last task exiting for this undo list. | ||
1261 | */ | ||
1262 | for (up = &undo_list->proc_list; (u = *up); *up = u->proc_next, kfree(u)) { | ||
1263 | struct sem_array *sma; | 1280 | struct sem_array *sma; |
1264 | int nsems, i; | 1281 | struct sem_undo *un; |
1265 | struct sem_undo *un, **unp; | ||
1266 | int semid; | 1282 | int semid; |
1267 | 1283 | int i; | |
1268 | semid = u->semid; | ||
1269 | 1284 | ||
1270 | if(semid == -1) | 1285 | rcu_read_lock(); |
1271 | continue; | 1286 | un = list_entry(rcu_dereference(ulp->list_proc.next), |
1272 | sma = sem_lock(ns, semid); | 1287 | struct sem_undo, list_proc); |
1288 | if (&un->list_proc == &ulp->list_proc) | ||
1289 | semid = -1; | ||
1290 | else | ||
1291 | semid = un->semid; | ||
1292 | rcu_read_unlock(); | ||
1293 | |||
1294 | if (semid == -1) | ||
1295 | break; | ||
1296 | |||
1297 | sma = sem_lock_check(tsk->nsproxy->ipc_ns, un->semid); | ||
1298 | |||
1299 | /* exit_sem raced with IPC_RMID, nothing to do */ | ||
1273 | if (IS_ERR(sma)) | 1300 | if (IS_ERR(sma)) |
1274 | continue; | 1301 | continue; |
1275 | 1302 | ||
1276 | if (u->semid == -1) | 1303 | un = lookup_undo(ulp, semid); |
1277 | goto next_entry; | 1304 | if (un == NULL) { |
1305 | /* exit_sem raced with IPC_RMID+semget() that created | ||
1306 | * exactly the same semid. Nothing to do. | ||
1307 | */ | ||
1308 | sem_unlock(sma); | ||
1309 | continue; | ||
1310 | } | ||
1278 | 1311 | ||
1279 | BUG_ON(sem_checkid(sma, u->semid)); | 1312 | /* remove un from the linked lists */ |
1313 | assert_spin_locked(&sma->sem_perm.lock); | ||
1314 | list_del(&un->list_id); | ||
1280 | 1315 | ||
1281 | /* remove u from the sma->undo list */ | 1316 | spin_lock(&ulp->lock); |
1282 | for (unp = &sma->undo; (un = *unp); unp = &un->id_next) { | 1317 | list_del_rcu(&un->list_proc); |
1283 | if (u == un) | 1318 | spin_unlock(&ulp->lock); |
1284 | goto found; | 1319 | |
1285 | } | 1320 | /* perform adjustments registered in un */ |
1286 | printk ("exit_sem undo list error id=%d\n", u->semid); | 1321 | for (i = 0; i < sma->sem_nsems; i++) { |
1287 | goto next_entry; | ||
1288 | found: | ||
1289 | *unp = un->id_next; | ||
1290 | /* perform adjustments registered in u */ | ||
1291 | nsems = sma->sem_nsems; | ||
1292 | for (i = 0; i < nsems; i++) { | ||
1293 | struct sem * semaphore = &sma->sem_base[i]; | 1322 | struct sem * semaphore = &sma->sem_base[i]; |
1294 | if (u->semadj[i]) { | 1323 | if (un->semadj[i]) { |
1295 | semaphore->semval += u->semadj[i]; | 1324 | semaphore->semval += un->semadj[i]; |
1296 | /* | 1325 | /* |
1297 | * Range checks of the new semaphore value, | 1326 | * Range checks of the new semaphore value, |
1298 | * not defined by sus: | 1327 | * not defined by sus: |
@@ -1316,10 +1345,11 @@ found: | |||
1316 | sma->sem_otime = get_seconds(); | 1345 | sma->sem_otime = get_seconds(); |
1317 | /* maybe some queued-up processes were waiting for this */ | 1346 | /* maybe some queued-up processes were waiting for this */ |
1318 | update_queue(sma); | 1347 | update_queue(sma); |
1319 | next_entry: | ||
1320 | sem_unlock(sma); | 1348 | sem_unlock(sma); |
1349 | |||
1350 | call_rcu(&un->rcu, free_un); | ||
1321 | } | 1351 | } |
1322 | kfree(undo_list); | 1352 | kfree(ulp); |
1323 | } | 1353 | } |
1324 | 1354 | ||
1325 | #ifdef CONFIG_PROC_FS | 1355 | #ifdef CONFIG_PROC_FS |
@@ -112,23 +112,8 @@ void __init shm_init (void) | |||
112 | } | 112 | } |
113 | 113 | ||
114 | /* | 114 | /* |
115 | * shm_lock_(check_)down routines are called in the paths where the rw_mutex | ||
116 | * is held to protect access to the idr tree. | ||
117 | */ | ||
118 | static inline struct shmid_kernel *shm_lock_down(struct ipc_namespace *ns, | ||
119 | int id) | ||
120 | { | ||
121 | struct kern_ipc_perm *ipcp = ipc_lock_down(&shm_ids(ns), id); | ||
122 | |||
123 | if (IS_ERR(ipcp)) | ||
124 | return (struct shmid_kernel *)ipcp; | ||
125 | |||
126 | return container_of(ipcp, struct shmid_kernel, shm_perm); | ||
127 | } | ||
128 | |||
129 | /* | ||
130 | * shm_lock_(check_) routines are called in the paths where the rw_mutex | 115 | * shm_lock_(check_) routines are called in the paths where the rw_mutex |
131 | * is not held. | 116 | * is not necessarily held. |
132 | */ | 117 | */ |
133 | static inline struct shmid_kernel *shm_lock(struct ipc_namespace *ns, int id) | 118 | static inline struct shmid_kernel *shm_lock(struct ipc_namespace *ns, int id) |
134 | { | 119 | { |
@@ -211,7 +196,7 @@ static void shm_close(struct vm_area_struct *vma) | |||
211 | 196 | ||
212 | down_write(&shm_ids(ns).rw_mutex); | 197 | down_write(&shm_ids(ns).rw_mutex); |
213 | /* remove from the list of attaches of the shm segment */ | 198 | /* remove from the list of attaches of the shm segment */ |
214 | shp = shm_lock_down(ns, sfd->id); | 199 | shp = shm_lock(ns, sfd->id); |
215 | BUG_ON(IS_ERR(shp)); | 200 | BUG_ON(IS_ERR(shp)); |
216 | shp->shm_lprid = task_tgid_vnr(current); | 201 | shp->shm_lprid = task_tgid_vnr(current); |
217 | shp->shm_dtim = get_seconds(); | 202 | shp->shm_dtim = get_seconds(); |
@@ -577,7 +562,8 @@ static void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss, | |||
577 | 562 | ||
578 | if (is_file_hugepages(shp->shm_file)) { | 563 | if (is_file_hugepages(shp->shm_file)) { |
579 | struct address_space *mapping = inode->i_mapping; | 564 | struct address_space *mapping = inode->i_mapping; |
580 | *rss += (HPAGE_SIZE/PAGE_SIZE)*mapping->nrpages; | 565 | struct hstate *h = hstate_file(shp->shm_file); |
566 | *rss += pages_per_huge_page(h) * mapping->nrpages; | ||
581 | } else { | 567 | } else { |
582 | struct shmem_inode_info *info = SHMEM_I(inode); | 568 | struct shmem_inode_info *info = SHMEM_I(inode); |
583 | spin_lock(&info->lock); | 569 | spin_lock(&info->lock); |
@@ -931,7 +917,7 @@ invalid: | |||
931 | 917 | ||
932 | out_nattch: | 918 | out_nattch: |
933 | down_write(&shm_ids(ns).rw_mutex); | 919 | down_write(&shm_ids(ns).rw_mutex); |
934 | shp = shm_lock_down(ns, shmid); | 920 | shp = shm_lock(ns, shmid); |
935 | BUG_ON(IS_ERR(shp)); | 921 | BUG_ON(IS_ERR(shp)); |
936 | shp->shm_nattch--; | 922 | shp->shm_nattch--; |
937 | if(shp->shm_nattch == 0 && | 923 | if(shp->shm_nattch == 0 && |
diff --git a/ipc/util.c b/ipc/util.c index 3339177b336c..49b3ea615dc5 100644 --- a/ipc/util.c +++ b/ipc/util.c | |||
@@ -688,10 +688,6 @@ void ipc64_perm_to_ipc_perm (struct ipc64_perm *in, struct ipc_perm *out) | |||
688 | * Look for an id in the ipc ids idr and lock the associated ipc object. | 688 | * Look for an id in the ipc ids idr and lock the associated ipc object. |
689 | * | 689 | * |
690 | * The ipc object is locked on exit. | 690 | * The ipc object is locked on exit. |
691 | * | ||
692 | * This is the routine that should be called when the rw_mutex is not already | ||
693 | * held, i.e. idr tree not protected: it protects the idr tree in read mode | ||
694 | * during the idr_find(). | ||
695 | */ | 691 | */ |
696 | 692 | ||
697 | struct kern_ipc_perm *ipc_lock(struct ipc_ids *ids, int id) | 693 | struct kern_ipc_perm *ipc_lock(struct ipc_ids *ids, int id) |
@@ -699,18 +695,13 @@ struct kern_ipc_perm *ipc_lock(struct ipc_ids *ids, int id) | |||
699 | struct kern_ipc_perm *out; | 695 | struct kern_ipc_perm *out; |
700 | int lid = ipcid_to_idx(id); | 696 | int lid = ipcid_to_idx(id); |
701 | 697 | ||
702 | down_read(&ids->rw_mutex); | ||
703 | |||
704 | rcu_read_lock(); | 698 | rcu_read_lock(); |
705 | out = idr_find(&ids->ipcs_idr, lid); | 699 | out = idr_find(&ids->ipcs_idr, lid); |
706 | if (out == NULL) { | 700 | if (out == NULL) { |
707 | rcu_read_unlock(); | 701 | rcu_read_unlock(); |
708 | up_read(&ids->rw_mutex); | ||
709 | return ERR_PTR(-EINVAL); | 702 | return ERR_PTR(-EINVAL); |
710 | } | 703 | } |
711 | 704 | ||
712 | up_read(&ids->rw_mutex); | ||
713 | |||
714 | spin_lock(&out->lock); | 705 | spin_lock(&out->lock); |
715 | 706 | ||
716 | /* ipc_rmid() may have already freed the ID while ipc_lock | 707 | /* ipc_rmid() may have already freed the ID while ipc_lock |
@@ -725,56 +716,6 @@ struct kern_ipc_perm *ipc_lock(struct ipc_ids *ids, int id) | |||
725 | return out; | 716 | return out; |
726 | } | 717 | } |
727 | 718 | ||
728 | /** | ||
729 | * ipc_lock_down - Lock an ipc structure with rw_sem held | ||
730 | * @ids: IPC identifier set | ||
731 | * @id: ipc id to look for | ||
732 | * | ||
733 | * Look for an id in the ipc ids idr and lock the associated ipc object. | ||
734 | * | ||
735 | * The ipc object is locked on exit. | ||
736 | * | ||
737 | * This is the routine that should be called when the rw_mutex is already | ||
738 | * held, i.e. idr tree protected. | ||
739 | */ | ||
740 | |||
741 | struct kern_ipc_perm *ipc_lock_down(struct ipc_ids *ids, int id) | ||
742 | { | ||
743 | struct kern_ipc_perm *out; | ||
744 | int lid = ipcid_to_idx(id); | ||
745 | |||
746 | rcu_read_lock(); | ||
747 | out = idr_find(&ids->ipcs_idr, lid); | ||
748 | if (out == NULL) { | ||
749 | rcu_read_unlock(); | ||
750 | return ERR_PTR(-EINVAL); | ||
751 | } | ||
752 | |||
753 | spin_lock(&out->lock); | ||
754 | |||
755 | /* | ||
756 | * No need to verify that the structure is still valid since the | ||
757 | * rw_mutex is held. | ||
758 | */ | ||
759 | return out; | ||
760 | } | ||
761 | |||
762 | struct kern_ipc_perm *ipc_lock_check_down(struct ipc_ids *ids, int id) | ||
763 | { | ||
764 | struct kern_ipc_perm *out; | ||
765 | |||
766 | out = ipc_lock_down(ids, id); | ||
767 | if (IS_ERR(out)) | ||
768 | return out; | ||
769 | |||
770 | if (ipc_checkid(out, id)) { | ||
771 | ipc_unlock(out); | ||
772 | return ERR_PTR(-EIDRM); | ||
773 | } | ||
774 | |||
775 | return out; | ||
776 | } | ||
777 | |||
778 | struct kern_ipc_perm *ipc_lock_check(struct ipc_ids *ids, int id) | 719 | struct kern_ipc_perm *ipc_lock_check(struct ipc_ids *ids, int id) |
779 | { | 720 | { |
780 | struct kern_ipc_perm *out; | 721 | struct kern_ipc_perm *out; |
@@ -846,7 +787,7 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_ids *ids, int id, int cmd, | |||
846 | int err; | 787 | int err; |
847 | 788 | ||
848 | down_write(&ids->rw_mutex); | 789 | down_write(&ids->rw_mutex); |
849 | ipcp = ipc_lock_check_down(ids, id); | 790 | ipcp = ipc_lock_check(ids, id); |
850 | if (IS_ERR(ipcp)) { | 791 | if (IS_ERR(ipcp)) { |
851 | err = PTR_ERR(ipcp); | 792 | err = PTR_ERR(ipcp); |
852 | goto out_up; | 793 | goto out_up; |
diff --git a/ipc/util.h b/ipc/util.h index cdb966aebe07..3646b45a03c9 100644 --- a/ipc/util.h +++ b/ipc/util.h | |||
@@ -102,11 +102,6 @@ void* ipc_rcu_alloc(int size); | |||
102 | void ipc_rcu_getref(void *ptr); | 102 | void ipc_rcu_getref(void *ptr); |
103 | void ipc_rcu_putref(void *ptr); | 103 | void ipc_rcu_putref(void *ptr); |
104 | 104 | ||
105 | /* | ||
106 | * ipc_lock_down: called with rw_mutex held | ||
107 | * ipc_lock: called without that lock held | ||
108 | */ | ||
109 | struct kern_ipc_perm *ipc_lock_down(struct ipc_ids *, int); | ||
110 | struct kern_ipc_perm *ipc_lock(struct ipc_ids *, int); | 105 | struct kern_ipc_perm *ipc_lock(struct ipc_ids *, int); |
111 | 106 | ||
112 | void kernel_to_ipc64_perm(struct kern_ipc_perm *in, struct ipc64_perm *out); | 107 | void kernel_to_ipc64_perm(struct kern_ipc_perm *in, struct ipc64_perm *out); |
@@ -155,7 +150,6 @@ static inline void ipc_unlock(struct kern_ipc_perm *perm) | |||
155 | rcu_read_unlock(); | 150 | rcu_read_unlock(); |
156 | } | 151 | } |
157 | 152 | ||
158 | struct kern_ipc_perm *ipc_lock_check_down(struct ipc_ids *ids, int id); | ||
159 | struct kern_ipc_perm *ipc_lock_check(struct ipc_ids *ids, int id); | 153 | struct kern_ipc_perm *ipc_lock_check(struct ipc_ids *ids, int id); |
160 | int ipcget(struct ipc_namespace *ns, struct ipc_ids *ids, | 154 | int ipcget(struct ipc_namespace *ns, struct ipc_ids *ids, |
161 | struct ipc_ops *ops, struct ipc_params *params); | 155 | struct ipc_ops *ops, struct ipc_params *params); |