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); |
