diff options
| author | Nadia Derbey <Nadia.Derbey@bull.net> | 2007-10-19 02:40:54 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-19 14:53:48 -0400 |
| commit | 3e148c79938aa39035669c1cfa3ff60722134535 (patch) | |
| tree | 0effb3edfece56ea38a9727ec8f4721d9a4c3ea8 /ipc | |
| parent | f4566f04854d78acfc74b9acb029744acde9d033 (diff) | |
fix idr_find() locking
This is a patch that fixes the way idr_find() used to be called in ipc_lock():
in all the paths that don't imply an update of the ipcs idr, it was called
without the idr tree being locked.
The changes are:
. in ipc_ids, the mutex has been changed into a reader/writer semaphore.
. ipc_lock() now takes the mutex as a reader during the idr_find().
. a new routine ipc_lock_down() has been defined: it doesn't take the
mutex, assuming that it is being held by the caller. This is the routine
that is now called in all the update paths.
Signed-off-by: Nadia Derbey <Nadia.Derbey@bull.net>
Acked-by: Jarek Poplawski <jarkao2@o2.pl>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'ipc')
| -rw-r--r-- | ipc/msg.c | 41 | ||||
| -rw-r--r-- | ipc/sem.c | 44 | ||||
| -rw-r--r-- | ipc/shm.c | 77 | ||||
| -rw-r--r-- | ipc/util.c | 78 | ||||
| -rw-r--r-- | ipc/util.h | 28 |
5 files changed, 198 insertions, 70 deletions
| @@ -34,7 +34,7 @@ | |||
| 34 | #include <linux/syscalls.h> | 34 | #include <linux/syscalls.h> |
| 35 | #include <linux/audit.h> | 35 | #include <linux/audit.h> |
| 36 | #include <linux/seq_file.h> | 36 | #include <linux/seq_file.h> |
| 37 | #include <linux/mutex.h> | 37 | #include <linux/rwsem.h> |
| 38 | #include <linux/nsproxy.h> | 38 | #include <linux/nsproxy.h> |
| 39 | 39 | ||
| 40 | #include <asm/current.h> | 40 | #include <asm/current.h> |
| @@ -110,7 +110,7 @@ void msg_exit_ns(struct ipc_namespace *ns) | |||
| 110 | int next_id; | 110 | int next_id; |
| 111 | int total, in_use; | 111 | int total, in_use; |
| 112 | 112 | ||
| 113 | mutex_lock(&msg_ids(ns).mutex); | 113 | down_write(&msg_ids(ns).rw_mutex); |
| 114 | 114 | ||
| 115 | in_use = msg_ids(ns).in_use; | 115 | in_use = msg_ids(ns).in_use; |
| 116 | 116 | ||
| @@ -122,7 +122,8 @@ void msg_exit_ns(struct ipc_namespace *ns) | |||
| 122 | freeque(ns, msq); | 122 | freeque(ns, msq); |
| 123 | total++; | 123 | total++; |
| 124 | } | 124 | } |
| 125 | mutex_unlock(&msg_ids(ns).mutex); | 125 | |
| 126 | up_write(&msg_ids(ns).rw_mutex); | ||
| 126 | 127 | ||
| 127 | kfree(ns->ids[IPC_MSG_IDS]); | 128 | kfree(ns->ids[IPC_MSG_IDS]); |
| 128 | ns->ids[IPC_MSG_IDS] = NULL; | 129 | ns->ids[IPC_MSG_IDS] = NULL; |
| @@ -136,6 +137,22 @@ void __init msg_init(void) | |||
| 136 | IPC_MSG_IDS, sysvipc_msg_proc_show); | 137 | IPC_MSG_IDS, sysvipc_msg_proc_show); |
| 137 | } | 138 | } |
| 138 | 139 | ||
| 140 | /* | ||
| 141 | * This routine is called in the paths where the rw_mutex is held to protect | ||
| 142 | * access to the idr tree. | ||
| 143 | */ | ||
| 144 | static inline struct msg_queue *msg_lock_check_down(struct ipc_namespace *ns, | ||
| 145 | int id) | ||
| 146 | { | ||
| 147 | struct kern_ipc_perm *ipcp = ipc_lock_check_down(&msg_ids(ns), id); | ||
| 148 | |||
| 149 | return container_of(ipcp, struct msg_queue, q_perm); | ||
| 150 | } | ||
| 151 | |||
| 152 | /* | ||
| 153 | * msg_lock_(check_) routines are called in the paths where the rw_mutex | ||
| 154 | * is not held. | ||
| 155 | */ | ||
| 139 | static inline struct msg_queue *msg_lock(struct ipc_namespace *ns, int id) | 156 | static inline struct msg_queue *msg_lock(struct ipc_namespace *ns, int id) |
| 140 | { | 157 | { |
| 141 | struct kern_ipc_perm *ipcp = ipc_lock(&msg_ids(ns), id); | 158 | struct kern_ipc_perm *ipcp = ipc_lock(&msg_ids(ns), id); |
| @@ -161,7 +178,7 @@ static inline void msg_rmid(struct ipc_namespace *ns, struct msg_queue *s) | |||
| 161 | * @ns: namespace | 178 | * @ns: namespace |
| 162 | * @params: ptr to the structure that contains the key and msgflg | 179 | * @params: ptr to the structure that contains the key and msgflg |
| 163 | * | 180 | * |
| 164 | * Called with msg_ids.mutex held | 181 | * Called with msg_ids.rw_mutex held (writer) |
| 165 | */ | 182 | */ |
| 166 | static int newque(struct ipc_namespace *ns, struct ipc_params *params) | 183 | static int newque(struct ipc_namespace *ns, struct ipc_params *params) |
| 167 | { | 184 | { |
| @@ -260,8 +277,8 @@ static void expunge_all(struct msg_queue *msq, int res) | |||
| 260 | * removes the message queue from message queue ID IDR, and cleans up all the | 277 | * removes the message queue from message queue ID IDR, and cleans up all the |
| 261 | * messages associated with this queue. | 278 | * messages associated with this queue. |
| 262 | * | 279 | * |
| 263 | * msg_ids.mutex and the spinlock for this message queue are held | 280 | * msg_ids.rw_mutex (writer) and the spinlock for this message queue are held |
| 264 | * before freeque() is called. msg_ids.mutex remains locked on exit. | 281 | * before freeque() is called. msg_ids.rw_mutex remains locked on exit. |
| 265 | */ | 282 | */ |
| 266 | static void freeque(struct ipc_namespace *ns, struct msg_queue *msq) | 283 | static void freeque(struct ipc_namespace *ns, struct msg_queue *msq) |
| 267 | { | 284 | { |
| @@ -286,7 +303,7 @@ static void freeque(struct ipc_namespace *ns, struct msg_queue *msq) | |||
| 286 | } | 303 | } |
| 287 | 304 | ||
| 288 | /* | 305 | /* |
| 289 | * Called with msg_ids.mutex and ipcp locked. | 306 | * Called with msg_ids.rw_mutex and ipcp locked. |
| 290 | */ | 307 | */ |
| 291 | static inline int msg_security(struct kern_ipc_perm *ipcp, int msgflg) | 308 | static inline int msg_security(struct kern_ipc_perm *ipcp, int msgflg) |
| 292 | { | 309 | { |
| @@ -444,7 +461,7 @@ asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf) | |||
| 444 | msginfo.msgmnb = ns->msg_ctlmnb; | 461 | msginfo.msgmnb = ns->msg_ctlmnb; |
| 445 | msginfo.msgssz = MSGSSZ; | 462 | msginfo.msgssz = MSGSSZ; |
| 446 | msginfo.msgseg = MSGSEG; | 463 | msginfo.msgseg = MSGSEG; |
| 447 | mutex_lock(&msg_ids(ns).mutex); | 464 | down_read(&msg_ids(ns).rw_mutex); |
| 448 | if (cmd == MSG_INFO) { | 465 | if (cmd == MSG_INFO) { |
| 449 | msginfo.msgpool = msg_ids(ns).in_use; | 466 | msginfo.msgpool = msg_ids(ns).in_use; |
| 450 | msginfo.msgmap = atomic_read(&msg_hdrs); | 467 | msginfo.msgmap = atomic_read(&msg_hdrs); |
| @@ -455,7 +472,7 @@ asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf) | |||
| 455 | msginfo.msgtql = MSGTQL; | 472 | msginfo.msgtql = MSGTQL; |
| 456 | } | 473 | } |
| 457 | max_id = ipc_get_maxid(&msg_ids(ns)); | 474 | max_id = ipc_get_maxid(&msg_ids(ns)); |
| 458 | mutex_unlock(&msg_ids(ns).mutex); | 475 | up_read(&msg_ids(ns).rw_mutex); |
| 459 | if (copy_to_user(buf, &msginfo, sizeof(struct msginfo))) | 476 | if (copy_to_user(buf, &msginfo, sizeof(struct msginfo))) |
| 460 | return -EFAULT; | 477 | return -EFAULT; |
| 461 | return (max_id < 0) ? 0 : max_id; | 478 | return (max_id < 0) ? 0 : max_id; |
| @@ -516,8 +533,8 @@ asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf) | |||
| 516 | return -EINVAL; | 533 | return -EINVAL; |
| 517 | } | 534 | } |
| 518 | 535 | ||
| 519 | mutex_lock(&msg_ids(ns).mutex); | 536 | down_write(&msg_ids(ns).rw_mutex); |
| 520 | msq = msg_lock_check(ns, msqid); | 537 | msq = msg_lock_check_down(ns, msqid); |
| 521 | if (IS_ERR(msq)) { | 538 | if (IS_ERR(msq)) { |
| 522 | err = PTR_ERR(msq); | 539 | err = PTR_ERR(msq); |
| 523 | goto out_up; | 540 | goto out_up; |
| @@ -576,7 +593,7 @@ asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf) | |||
| 576 | } | 593 | } |
| 577 | err = 0; | 594 | err = 0; |
| 578 | out_up: | 595 | out_up: |
| 579 | mutex_unlock(&msg_ids(ns).mutex); | 596 | up_write(&msg_ids(ns).rw_mutex); |
| 580 | return err; | 597 | return err; |
| 581 | out_unlock_up: | 598 | out_unlock_up: |
| 582 | msg_unlock(msq); | 599 | msg_unlock(msq); |
| @@ -80,7 +80,7 @@ | |||
| 80 | #include <linux/audit.h> | 80 | #include <linux/audit.h> |
| 81 | #include <linux/capability.h> | 81 | #include <linux/capability.h> |
| 82 | #include <linux/seq_file.h> | 82 | #include <linux/seq_file.h> |
| 83 | #include <linux/mutex.h> | 83 | #include <linux/rwsem.h> |
| 84 | #include <linux/nsproxy.h> | 84 | #include <linux/nsproxy.h> |
| 85 | 85 | ||
| 86 | #include <asm/uaccess.h> | 86 | #include <asm/uaccess.h> |
| @@ -148,7 +148,7 @@ void sem_exit_ns(struct ipc_namespace *ns) | |||
| 148 | int next_id; | 148 | int next_id; |
| 149 | int total, in_use; | 149 | int total, in_use; |
| 150 | 150 | ||
| 151 | mutex_lock(&sem_ids(ns).mutex); | 151 | down_write(&sem_ids(ns).rw_mutex); |
| 152 | 152 | ||
| 153 | in_use = sem_ids(ns).in_use; | 153 | in_use = sem_ids(ns).in_use; |
| 154 | 154 | ||
| @@ -160,7 +160,7 @@ void sem_exit_ns(struct ipc_namespace *ns) | |||
| 160 | freeary(ns, sma); | 160 | freeary(ns, sma); |
| 161 | total++; | 161 | total++; |
| 162 | } | 162 | } |
| 163 | mutex_unlock(&sem_ids(ns).mutex); | 163 | up_write(&sem_ids(ns).rw_mutex); |
| 164 | 164 | ||
| 165 | kfree(ns->ids[IPC_SEM_IDS]); | 165 | kfree(ns->ids[IPC_SEM_IDS]); |
| 166 | ns->ids[IPC_SEM_IDS] = NULL; | 166 | ns->ids[IPC_SEM_IDS] = NULL; |
| @@ -174,6 +174,22 @@ void __init sem_init (void) | |||
| 174 | IPC_SEM_IDS, sysvipc_sem_proc_show); | 174 | IPC_SEM_IDS, sysvipc_sem_proc_show); |
| 175 | } | 175 | } |
| 176 | 176 | ||
| 177 | /* | ||
| 178 | * This routine is called in the paths where the rw_mutex is held to protect | ||
| 179 | * access to the idr tree. | ||
| 180 | */ | ||
| 181 | static inline struct sem_array *sem_lock_check_down(struct ipc_namespace *ns, | ||
| 182 | int id) | ||
| 183 | { | ||
| 184 | struct kern_ipc_perm *ipcp = ipc_lock_check_down(&sem_ids(ns), id); | ||
| 185 | |||
| 186 | return container_of(ipcp, struct sem_array, sem_perm); | ||
| 187 | } | ||
| 188 | |||
| 189 | /* | ||
| 190 | * sem_lock_(check_) routines are called in the paths where the rw_mutex | ||
| 191 | * is not held. | ||
| 192 | */ | ||
| 177 | static inline struct sem_array *sem_lock(struct ipc_namespace *ns, int id) | 193 | static inline struct sem_array *sem_lock(struct ipc_namespace *ns, int id) |
| 178 | { | 194 | { |
| 179 | struct kern_ipc_perm *ipcp = ipc_lock(&sem_ids(ns), id); | 195 | struct kern_ipc_perm *ipcp = ipc_lock(&sem_ids(ns), id); |
| @@ -233,7 +249,7 @@ static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s) | |||
| 233 | * @ns: namespace | 249 | * @ns: namespace |
| 234 | * @params: ptr to the structure that contains key, semflg and nsems | 250 | * @params: ptr to the structure that contains key, semflg and nsems |
| 235 | * | 251 | * |
| 236 | * Called with sem_ids.mutex held | 252 | * Called with sem_ids.rw_mutex held (as a writer) |
| 237 | */ | 253 | */ |
| 238 | 254 | ||
| 239 | static int newary(struct ipc_namespace *ns, struct ipc_params *params) | 255 | static int newary(struct ipc_namespace *ns, struct ipc_params *params) |
| @@ -290,7 +306,7 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params) | |||
| 290 | 306 | ||
| 291 | 307 | ||
| 292 | /* | 308 | /* |
| 293 | * Called with sem_ids.mutex and ipcp locked. | 309 | * Called with sem_ids.rw_mutex and ipcp locked. |
| 294 | */ | 310 | */ |
| 295 | static inline int sem_security(struct kern_ipc_perm *ipcp, int semflg) | 311 | static inline int sem_security(struct kern_ipc_perm *ipcp, int semflg) |
| 296 | { | 312 | { |
| @@ -301,7 +317,7 @@ static inline int sem_security(struct kern_ipc_perm *ipcp, int semflg) | |||
| 301 | } | 317 | } |
| 302 | 318 | ||
| 303 | /* | 319 | /* |
| 304 | * Called with sem_ids.mutex and ipcp locked. | 320 | * Called with sem_ids.rw_mutex and ipcp locked. |
| 305 | */ | 321 | */ |
| 306 | static inline int sem_more_checks(struct kern_ipc_perm *ipcp, | 322 | static inline int sem_more_checks(struct kern_ipc_perm *ipcp, |
| 307 | struct ipc_params *params) | 323 | struct ipc_params *params) |
| @@ -528,9 +544,9 @@ static int count_semzcnt (struct sem_array * sma, ushort semnum) | |||
| 528 | return semzcnt; | 544 | return semzcnt; |
| 529 | } | 545 | } |
| 530 | 546 | ||
| 531 | /* Free a semaphore set. freeary() is called with sem_ids.mutex locked and | 547 | /* Free a semaphore set. freeary() is called with sem_ids.rw_mutex locked |
| 532 | * the spinlock for this semaphore set hold. sem_ids.mutex remains locked | 548 | * as a writer and the spinlock for this semaphore set hold. sem_ids.rw_mutex |
| 533 | * on exit. | 549 | * remains locked on exit. |
| 534 | */ | 550 | */ |
| 535 | static void freeary(struct ipc_namespace *ns, struct sem_array *sma) | 551 | static void freeary(struct ipc_namespace *ns, struct sem_array *sma) |
| 536 | { | 552 | { |
| @@ -615,7 +631,7 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid, int semnum, | |||
| 615 | seminfo.semmnu = SEMMNU; | 631 | seminfo.semmnu = SEMMNU; |
| 616 | seminfo.semmap = SEMMAP; | 632 | seminfo.semmap = SEMMAP; |
| 617 | seminfo.semume = SEMUME; | 633 | seminfo.semume = SEMUME; |
| 618 | mutex_lock(&sem_ids(ns).mutex); | 634 | down_read(&sem_ids(ns).rw_mutex); |
| 619 | if (cmd == SEM_INFO) { | 635 | if (cmd == SEM_INFO) { |
| 620 | seminfo.semusz = sem_ids(ns).in_use; | 636 | seminfo.semusz = sem_ids(ns).in_use; |
| 621 | seminfo.semaem = ns->used_sems; | 637 | seminfo.semaem = ns->used_sems; |
| @@ -624,7 +640,7 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid, int semnum, | |||
| 624 | seminfo.semaem = SEMAEM; | 640 | seminfo.semaem = SEMAEM; |
| 625 | } | 641 | } |
| 626 | max_id = ipc_get_maxid(&sem_ids(ns)); | 642 | max_id = ipc_get_maxid(&sem_ids(ns)); |
| 627 | mutex_unlock(&sem_ids(ns).mutex); | 643 | up_read(&sem_ids(ns).rw_mutex); |
| 628 | if (copy_to_user (arg.__buf, &seminfo, sizeof(struct seminfo))) | 644 | if (copy_to_user (arg.__buf, &seminfo, sizeof(struct seminfo))) |
| 629 | return -EFAULT; | 645 | return -EFAULT; |
| 630 | return (max_id < 0) ? 0: max_id; | 646 | return (max_id < 0) ? 0: max_id; |
| @@ -895,7 +911,7 @@ static int semctl_down(struct ipc_namespace *ns, int semid, int semnum, | |||
| 895 | if(copy_semid_from_user (&setbuf, arg.buf, version)) | 911 | if(copy_semid_from_user (&setbuf, arg.buf, version)) |
| 896 | return -EFAULT; | 912 | return -EFAULT; |
| 897 | } | 913 | } |
| 898 | sma = sem_lock_check(ns, semid); | 914 | sma = sem_lock_check_down(ns, semid); |
| 899 | if (IS_ERR(sma)) | 915 | if (IS_ERR(sma)) |
| 900 | return PTR_ERR(sma); | 916 | return PTR_ERR(sma); |
| 901 | 917 | ||
| @@ -976,9 +992,9 @@ asmlinkage long sys_semctl (int semid, int semnum, int cmd, union semun arg) | |||
| 976 | return err; | 992 | return err; |
| 977 | case IPC_RMID: | 993 | case IPC_RMID: |
| 978 | case IPC_SET: | 994 | case IPC_SET: |
| 979 | mutex_lock(&sem_ids(ns).mutex); | 995 | down_write(&sem_ids(ns).rw_mutex); |
| 980 | err = semctl_down(ns,semid,semnum,cmd,version,arg); | 996 | err = semctl_down(ns,semid,semnum,cmd,version,arg); |
| 981 | mutex_unlock(&sem_ids(ns).mutex); | 997 | up_write(&sem_ids(ns).rw_mutex); |
| 982 | return err; | 998 | return err; |
| 983 | default: | 999 | default: |
| 984 | return -EINVAL; | 1000 | return -EINVAL; |
| @@ -35,7 +35,7 @@ | |||
| 35 | #include <linux/capability.h> | 35 | #include <linux/capability.h> |
| 36 | #include <linux/ptrace.h> | 36 | #include <linux/ptrace.h> |
| 37 | #include <linux/seq_file.h> | 37 | #include <linux/seq_file.h> |
| 38 | #include <linux/mutex.h> | 38 | #include <linux/rwsem.h> |
| 39 | #include <linux/nsproxy.h> | 39 | #include <linux/nsproxy.h> |
| 40 | #include <linux/mount.h> | 40 | #include <linux/mount.h> |
| 41 | 41 | ||
| @@ -83,8 +83,8 @@ static void __shm_init_ns(struct ipc_namespace *ns, struct ipc_ids *ids) | |||
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | /* | 85 | /* |
| 86 | * Called with shm_ids.mutex and the shp structure locked. | 86 | * Called with shm_ids.rw_mutex (writer) and the shp structure locked. |
| 87 | * Only shm_ids.mutex remains locked on exit. | 87 | * Only shm_ids.rw_mutex remains locked on exit. |
| 88 | */ | 88 | */ |
| 89 | static void do_shm_rmid(struct ipc_namespace *ns, struct shmid_kernel *shp) | 89 | static void do_shm_rmid(struct ipc_namespace *ns, struct shmid_kernel *shp) |
| 90 | { | 90 | { |
| @@ -115,7 +115,7 @@ void shm_exit_ns(struct ipc_namespace *ns) | |||
| 115 | int next_id; | 115 | int next_id; |
| 116 | int total, in_use; | 116 | int total, in_use; |
| 117 | 117 | ||
| 118 | mutex_lock(&shm_ids(ns).mutex); | 118 | down_write(&shm_ids(ns).rw_mutex); |
| 119 | 119 | ||
| 120 | in_use = shm_ids(ns).in_use; | 120 | in_use = shm_ids(ns).in_use; |
| 121 | 121 | ||
| @@ -127,7 +127,7 @@ void shm_exit_ns(struct ipc_namespace *ns) | |||
| 127 | do_shm_rmid(ns, shp); | 127 | do_shm_rmid(ns, shp); |
| 128 | total++; | 128 | total++; |
| 129 | } | 129 | } |
| 130 | mutex_unlock(&shm_ids(ns).mutex); | 130 | up_write(&shm_ids(ns).rw_mutex); |
| 131 | 131 | ||
| 132 | kfree(ns->ids[IPC_SHM_IDS]); | 132 | kfree(ns->ids[IPC_SHM_IDS]); |
| 133 | ns->ids[IPC_SHM_IDS] = NULL; | 133 | ns->ids[IPC_SHM_IDS] = NULL; |
| @@ -141,6 +141,31 @@ void __init shm_init (void) | |||
| 141 | IPC_SHM_IDS, sysvipc_shm_proc_show); | 141 | IPC_SHM_IDS, sysvipc_shm_proc_show); |
| 142 | } | 142 | } |
| 143 | 143 | ||
| 144 | /* | ||
| 145 | * shm_lock_(check_)down routines are called in the paths where the rw_mutex | ||
| 146 | * is held to protect access to the idr tree. | ||
| 147 | */ | ||
| 148 | static inline struct shmid_kernel *shm_lock_down(struct ipc_namespace *ns, | ||
| 149 | int id) | ||
| 150 | { | ||
| 151 | struct kern_ipc_perm *ipcp = ipc_lock_down(&shm_ids(ns), id); | ||
| 152 | |||
| 153 | return container_of(ipcp, struct shmid_kernel, shm_perm); | ||
| 154 | } | ||
| 155 | |||
| 156 | static inline struct shmid_kernel *shm_lock_check_down( | ||
| 157 | struct ipc_namespace *ns, | ||
| 158 | int id) | ||
| 159 | { | ||
| 160 | struct kern_ipc_perm *ipcp = ipc_lock_check_down(&shm_ids(ns), id); | ||
| 161 | |||
| 162 | return container_of(ipcp, struct shmid_kernel, shm_perm); | ||
| 163 | } | ||
| 164 | |||
| 165 | /* | ||
| 166 | * shm_lock_(check_) routines are called in the paths where the rw_mutex | ||
| 167 | * is not held. | ||
| 168 | */ | ||
| 144 | static inline struct shmid_kernel *shm_lock(struct ipc_namespace *ns, int id) | 169 | static inline struct shmid_kernel *shm_lock(struct ipc_namespace *ns, int id) |
| 145 | { | 170 | { |
| 146 | struct kern_ipc_perm *ipcp = ipc_lock(&shm_ids(ns), id); | 171 | struct kern_ipc_perm *ipcp = ipc_lock(&shm_ids(ns), id); |
| @@ -189,7 +214,7 @@ static void shm_open(struct vm_area_struct *vma) | |||
| 189 | * @ns: namespace | 214 | * @ns: namespace |
| 190 | * @shp: struct to free | 215 | * @shp: struct to free |
| 191 | * | 216 | * |
| 192 | * It has to be called with shp and shm_ids.mutex locked, | 217 | * It has to be called with shp and shm_ids.rw_mutex (writer) locked, |
| 193 | * but returns with shp unlocked and freed. | 218 | * but returns with shp unlocked and freed. |
| 194 | */ | 219 | */ |
| 195 | static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp) | 220 | static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp) |
| @@ -220,9 +245,9 @@ static void shm_close(struct vm_area_struct *vma) | |||
| 220 | struct shmid_kernel *shp; | 245 | struct shmid_kernel *shp; |
| 221 | struct ipc_namespace *ns = sfd->ns; | 246 | struct ipc_namespace *ns = sfd->ns; |
| 222 | 247 | ||
| 223 | mutex_lock(&shm_ids(ns).mutex); | 248 | down_write(&shm_ids(ns).rw_mutex); |
| 224 | /* remove from the list of attaches of the shm segment */ | 249 | /* remove from the list of attaches of the shm segment */ |
| 225 | shp = shm_lock(ns, sfd->id); | 250 | shp = shm_lock_down(ns, sfd->id); |
| 226 | BUG_ON(IS_ERR(shp)); | 251 | BUG_ON(IS_ERR(shp)); |
| 227 | shp->shm_lprid = task_tgid_vnr(current); | 252 | shp->shm_lprid = task_tgid_vnr(current); |
| 228 | shp->shm_dtim = get_seconds(); | 253 | shp->shm_dtim = get_seconds(); |
| @@ -232,7 +257,7 @@ static void shm_close(struct vm_area_struct *vma) | |||
| 232 | shm_destroy(ns, shp); | 257 | shm_destroy(ns, shp); |
| 233 | else | 258 | else |
| 234 | shm_unlock(shp); | 259 | shm_unlock(shp); |
| 235 | mutex_unlock(&shm_ids(ns).mutex); | 260 | up_write(&shm_ids(ns).rw_mutex); |
| 236 | } | 261 | } |
| 237 | 262 | ||
| 238 | static int shm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) | 263 | static int shm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) |
| @@ -353,7 +378,7 @@ static struct vm_operations_struct shm_vm_ops = { | |||
| 353 | * @ns: namespace | 378 | * @ns: namespace |
| 354 | * @params: ptr to the structure that contains key, size and shmflg | 379 | * @params: ptr to the structure that contains key, size and shmflg |
| 355 | * | 380 | * |
| 356 | * Called with shm_ids.mutex held | 381 | * Called with shm_ids.rw_mutex held as a writer. |
| 357 | */ | 382 | */ |
| 358 | 383 | ||
| 359 | static int newseg(struct ipc_namespace *ns, struct ipc_params *params) | 384 | static int newseg(struct ipc_namespace *ns, struct ipc_params *params) |
| @@ -442,7 +467,7 @@ no_file: | |||
| 442 | } | 467 | } |
| 443 | 468 | ||
| 444 | /* | 469 | /* |
| 445 | * Called with shm_ids.mutex and ipcp locked. | 470 | * Called with shm_ids.rw_mutex and ipcp locked. |
| 446 | */ | 471 | */ |
| 447 | static inline int shm_security(struct kern_ipc_perm *ipcp, int shmflg) | 472 | static inline int shm_security(struct kern_ipc_perm *ipcp, int shmflg) |
| 448 | { | 473 | { |
| @@ -453,7 +478,7 @@ static inline int shm_security(struct kern_ipc_perm *ipcp, int shmflg) | |||
| 453 | } | 478 | } |
| 454 | 479 | ||
| 455 | /* | 480 | /* |
| 456 | * Called with shm_ids.mutex and ipcp locked. | 481 | * Called with shm_ids.rw_mutex and ipcp locked. |
| 457 | */ | 482 | */ |
| 458 | static inline int shm_more_checks(struct kern_ipc_perm *ipcp, | 483 | static inline int shm_more_checks(struct kern_ipc_perm *ipcp, |
| 459 | struct ipc_params *params) | 484 | struct ipc_params *params) |
| @@ -578,7 +603,7 @@ static inline unsigned long copy_shminfo_to_user(void __user *buf, struct shminf | |||
| 578 | } | 603 | } |
| 579 | 604 | ||
| 580 | /* | 605 | /* |
| 581 | * Called with shm_ids.mutex held | 606 | * Called with shm_ids.rw_mutex held as a reader |
| 582 | */ | 607 | */ |
| 583 | static void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss, | 608 | static void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss, |
| 584 | unsigned long *swp) | 609 | unsigned long *swp) |
| @@ -649,9 +674,9 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) | |||
| 649 | if(copy_shminfo_to_user (buf, &shminfo, version)) | 674 | if(copy_shminfo_to_user (buf, &shminfo, version)) |
| 650 | return -EFAULT; | 675 | return -EFAULT; |
| 651 | 676 | ||
| 652 | mutex_lock(&shm_ids(ns).mutex); | 677 | down_read(&shm_ids(ns).rw_mutex); |
| 653 | err = ipc_get_maxid(&shm_ids(ns)); | 678 | err = ipc_get_maxid(&shm_ids(ns)); |
| 654 | mutex_unlock(&shm_ids(ns).mutex); | 679 | up_read(&shm_ids(ns).rw_mutex); |
| 655 | 680 | ||
| 656 | if(err<0) | 681 | if(err<0) |
| 657 | err = 0; | 682 | err = 0; |
| @@ -666,14 +691,14 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) | |||
| 666 | return err; | 691 | return err; |
| 667 | 692 | ||
| 668 | memset(&shm_info,0,sizeof(shm_info)); | 693 | memset(&shm_info,0,sizeof(shm_info)); |
| 669 | mutex_lock(&shm_ids(ns).mutex); | 694 | down_read(&shm_ids(ns).rw_mutex); |
| 670 | shm_info.used_ids = shm_ids(ns).in_use; | 695 | shm_info.used_ids = shm_ids(ns).in_use; |
| 671 | shm_get_stat (ns, &shm_info.shm_rss, &shm_info.shm_swp); | 696 | shm_get_stat (ns, &shm_info.shm_rss, &shm_info.shm_swp); |
| 672 | shm_info.shm_tot = ns->shm_tot; | 697 | shm_info.shm_tot = ns->shm_tot; |
| 673 | shm_info.swap_attempts = 0; | 698 | shm_info.swap_attempts = 0; |
| 674 | shm_info.swap_successes = 0; | 699 | shm_info.swap_successes = 0; |
| 675 | err = ipc_get_maxid(&shm_ids(ns)); | 700 | err = ipc_get_maxid(&shm_ids(ns)); |
| 676 | mutex_unlock(&shm_ids(ns).mutex); | 701 | up_read(&shm_ids(ns).rw_mutex); |
| 677 | if(copy_to_user (buf, &shm_info, sizeof(shm_info))) { | 702 | if(copy_to_user (buf, &shm_info, sizeof(shm_info))) { |
| 678 | err = -EFAULT; | 703 | err = -EFAULT; |
| 679 | goto out; | 704 | goto out; |
| @@ -786,8 +811,8 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) | |||
| 786 | * Instead we set a destroyed flag, and then blow | 811 | * Instead we set a destroyed flag, and then blow |
| 787 | * the name away when the usage hits zero. | 812 | * the name away when the usage hits zero. |
| 788 | */ | 813 | */ |
| 789 | mutex_lock(&shm_ids(ns).mutex); | 814 | down_write(&shm_ids(ns).rw_mutex); |
| 790 | shp = shm_lock_check(ns, shmid); | 815 | shp = shm_lock_check_down(ns, shmid); |
| 791 | if (IS_ERR(shp)) { | 816 | if (IS_ERR(shp)) { |
| 792 | err = PTR_ERR(shp); | 817 | err = PTR_ERR(shp); |
| 793 | goto out_up; | 818 | goto out_up; |
| @@ -809,7 +834,7 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) | |||
| 809 | goto out_unlock_up; | 834 | goto out_unlock_up; |
| 810 | 835 | ||
| 811 | do_shm_rmid(ns, shp); | 836 | do_shm_rmid(ns, shp); |
| 812 | mutex_unlock(&shm_ids(ns).mutex); | 837 | up_write(&shm_ids(ns).rw_mutex); |
| 813 | goto out; | 838 | goto out; |
| 814 | } | 839 | } |
| 815 | 840 | ||
| @@ -824,8 +849,8 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) | |||
| 824 | err = -EFAULT; | 849 | err = -EFAULT; |
| 825 | goto out; | 850 | goto out; |
| 826 | } | 851 | } |
| 827 | mutex_lock(&shm_ids(ns).mutex); | 852 | down_write(&shm_ids(ns).rw_mutex); |
| 828 | shp = shm_lock_check(ns, shmid); | 853 | shp = shm_lock_check_down(ns, shmid); |
| 829 | if (IS_ERR(shp)) { | 854 | if (IS_ERR(shp)) { |
| 830 | err = PTR_ERR(shp); | 855 | err = PTR_ERR(shp); |
| 831 | goto out_up; | 856 | goto out_up; |
| @@ -864,7 +889,7 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) | |||
| 864 | out_unlock_up: | 889 | out_unlock_up: |
| 865 | shm_unlock(shp); | 890 | shm_unlock(shp); |
| 866 | out_up: | 891 | out_up: |
| 867 | mutex_unlock(&shm_ids(ns).mutex); | 892 | up_write(&shm_ids(ns).rw_mutex); |
| 868 | goto out; | 893 | goto out; |
| 869 | out_unlock: | 894 | out_unlock: |
| 870 | shm_unlock(shp); | 895 | shm_unlock(shp); |
| @@ -998,8 +1023,8 @@ invalid: | |||
| 998 | fput(file); | 1023 | fput(file); |
| 999 | 1024 | ||
| 1000 | out_nattch: | 1025 | out_nattch: |
| 1001 | mutex_lock(&shm_ids(ns).mutex); | 1026 | down_write(&shm_ids(ns).rw_mutex); |
| 1002 | shp = shm_lock(ns, shmid); | 1027 | shp = shm_lock_down(ns, shmid); |
| 1003 | BUG_ON(IS_ERR(shp)); | 1028 | BUG_ON(IS_ERR(shp)); |
| 1004 | shp->shm_nattch--; | 1029 | shp->shm_nattch--; |
| 1005 | if(shp->shm_nattch == 0 && | 1030 | if(shp->shm_nattch == 0 && |
| @@ -1007,7 +1032,7 @@ out_nattch: | |||
| 1007 | shm_destroy(ns, shp); | 1032 | shm_destroy(ns, shp); |
| 1008 | else | 1033 | else |
| 1009 | shm_unlock(shp); | 1034 | shm_unlock(shp); |
| 1010 | mutex_unlock(&shm_ids(ns).mutex); | 1035 | up_write(&shm_ids(ns).rw_mutex); |
| 1011 | 1036 | ||
| 1012 | out: | 1037 | out: |
| 1013 | return err; | 1038 | return err; |
diff --git a/ipc/util.c b/ipc/util.c index fd29246dc3c8..b42fbd58973a 100644 --- a/ipc/util.c +++ b/ipc/util.c | |||
| @@ -32,6 +32,7 @@ | |||
| 32 | #include <linux/proc_fs.h> | 32 | #include <linux/proc_fs.h> |
| 33 | #include <linux/audit.h> | 33 | #include <linux/audit.h> |
| 34 | #include <linux/nsproxy.h> | 34 | #include <linux/nsproxy.h> |
| 35 | #include <linux/rwsem.h> | ||
| 35 | 36 | ||
| 36 | #include <asm/unistd.h> | 37 | #include <asm/unistd.h> |
| 37 | 38 | ||
| @@ -136,7 +137,7 @@ __initcall(ipc_init); | |||
| 136 | 137 | ||
| 137 | void ipc_init_ids(struct ipc_ids *ids) | 138 | void ipc_init_ids(struct ipc_ids *ids) |
| 138 | { | 139 | { |
| 139 | mutex_init(&ids->mutex); | 140 | init_rwsem(&ids->rw_mutex); |
| 140 | 141 | ||
| 141 | ids->in_use = 0; | 142 | ids->in_use = 0; |
| 142 | ids->seq = 0; | 143 | ids->seq = 0; |
| @@ -191,7 +192,7 @@ void __init ipc_init_proc_interface(const char *path, const char *header, | |||
| 191 | * @ids: Identifier set | 192 | * @ids: Identifier set |
| 192 | * @key: The key to find | 193 | * @key: The key to find |
| 193 | * | 194 | * |
| 194 | * Requires ipc_ids.mutex locked. | 195 | * Requires ipc_ids.rw_mutex locked. |
| 195 | * Returns the LOCKED pointer to the ipc structure if found or NULL | 196 | * Returns the LOCKED pointer to the ipc structure if found or NULL |
| 196 | * if not. | 197 | * if not. |
| 197 | * If key is found ipc points to the owning ipc structure | 198 | * If key is found ipc points to the owning ipc structure |
| @@ -225,7 +226,7 @@ static struct kern_ipc_perm *ipc_findkey(struct ipc_ids *ids, key_t key) | |||
| 225 | * ipc_get_maxid - get the last assigned id | 226 | * ipc_get_maxid - get the last assigned id |
| 226 | * @ids: IPC identifier set | 227 | * @ids: IPC identifier set |
| 227 | * | 228 | * |
| 228 | * Called with ipc_ids.mutex held. | 229 | * Called with ipc_ids.rw_mutex held. |
| 229 | */ | 230 | */ |
| 230 | 231 | ||
| 231 | int ipc_get_maxid(struct ipc_ids *ids) | 232 | int ipc_get_maxid(struct ipc_ids *ids) |
| @@ -263,7 +264,7 @@ int ipc_get_maxid(struct ipc_ids *ids) | |||
| 263 | * is returned. The 'new' entry is returned in a locked state on success. | 264 | * is returned. The 'new' entry is returned in a locked state on success. |
| 264 | * On failure the entry is not locked and -1 is returned. | 265 | * On failure the entry is not locked and -1 is returned. |
| 265 | * | 266 | * |
| 266 | * Called with ipc_ids.mutex held. | 267 | * Called with ipc_ids.rw_mutex held as a writer. |
| 267 | */ | 268 | */ |
| 268 | 269 | ||
| 269 | int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size) | 270 | int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size) |
| @@ -316,9 +317,9 @@ int ipcget_new(struct ipc_namespace *ns, struct ipc_ids *ids, | |||
| 316 | if (!err) | 317 | if (!err) |
| 317 | return -ENOMEM; | 318 | return -ENOMEM; |
| 318 | 319 | ||
| 319 | mutex_lock(&ids->mutex); | 320 | down_write(&ids->rw_mutex); |
| 320 | err = ops->getnew(ns, params); | 321 | err = ops->getnew(ns, params); |
| 321 | mutex_unlock(&ids->mutex); | 322 | up_write(&ids->rw_mutex); |
| 322 | 323 | ||
| 323 | return err; | 324 | return err; |
| 324 | } | 325 | } |
| @@ -335,7 +336,7 @@ int ipcget_new(struct ipc_namespace *ns, struct ipc_ids *ids, | |||
| 335 | * | 336 | * |
| 336 | * On success, the IPC id is returned. | 337 | * On success, the IPC id is returned. |
| 337 | * | 338 | * |
| 338 | * It is called with ipc_ids.mutex and ipcp->lock held. | 339 | * It is called with ipc_ids.rw_mutex and ipcp->lock held. |
| 339 | */ | 340 | */ |
| 340 | static int ipc_check_perms(struct kern_ipc_perm *ipcp, struct ipc_ops *ops, | 341 | static int ipc_check_perms(struct kern_ipc_perm *ipcp, struct ipc_ops *ops, |
| 341 | struct ipc_params *params) | 342 | struct ipc_params *params) |
| @@ -376,7 +377,11 @@ int ipcget_public(struct ipc_namespace *ns, struct ipc_ids *ids, | |||
| 376 | 377 | ||
| 377 | err = idr_pre_get(&ids->ipcs_idr, GFP_KERNEL); | 378 | err = idr_pre_get(&ids->ipcs_idr, GFP_KERNEL); |
| 378 | 379 | ||
| 379 | mutex_lock(&ids->mutex); | 380 | /* |
| 381 | * Take the lock as a writer since we are potentially going to add | ||
| 382 | * a new entry + read locks are not "upgradable" | ||
| 383 | */ | ||
| 384 | down_write(&ids->rw_mutex); | ||
| 380 | ipcp = ipc_findkey(ids, params->key); | 385 | ipcp = ipc_findkey(ids, params->key); |
| 381 | if (ipcp == NULL) { | 386 | if (ipcp == NULL) { |
| 382 | /* key not used */ | 387 | /* key not used */ |
| @@ -404,7 +409,7 @@ int ipcget_public(struct ipc_namespace *ns, struct ipc_ids *ids, | |||
| 404 | } | 409 | } |
| 405 | ipc_unlock(ipcp); | 410 | ipc_unlock(ipcp); |
| 406 | } | 411 | } |
| 407 | mutex_unlock(&ids->mutex); | 412 | up_write(&ids->rw_mutex); |
| 408 | 413 | ||
| 409 | return err; | 414 | return err; |
| 410 | } | 415 | } |
| @@ -415,8 +420,8 @@ int ipcget_public(struct ipc_namespace *ns, struct ipc_ids *ids, | |||
| 415 | * @ids: IPC identifier set | 420 | * @ids: IPC identifier set |
| 416 | * @ipcp: ipc perm structure containing the identifier to remove | 421 | * @ipcp: ipc perm structure containing the identifier to remove |
| 417 | * | 422 | * |
| 418 | * ipc_ids.mutex and the spinlock for this ID are held before this | 423 | * ipc_ids.rw_mutex (as a writer) and the spinlock for this ID are held |
| 419 | * function is called, and remain locked on the exit. | 424 | * before this function is called, and remain locked on the exit. |
| 420 | */ | 425 | */ |
| 421 | 426 | ||
| 422 | void ipc_rmid(struct ipc_ids *ids, struct kern_ipc_perm *ipcp) | 427 | void ipc_rmid(struct ipc_ids *ids, struct kern_ipc_perm *ipcp) |
| @@ -680,15 +685,17 @@ void ipc64_perm_to_ipc_perm (struct ipc64_perm *in, struct ipc_perm *out) | |||
| 680 | } | 685 | } |
| 681 | 686 | ||
| 682 | /** | 687 | /** |
| 683 | * ipc_lock - Lock an ipc structure | 688 | * ipc_lock - Lock an ipc structure without rw_mutex held |
| 684 | * @ids: IPC identifier set | 689 | * @ids: IPC identifier set |
| 685 | * @id: ipc id to look for | 690 | * @id: ipc id to look for |
| 686 | * | 691 | * |
| 687 | * Look for an id in the ipc ids idr and lock the associated ipc object. | 692 | * Look for an id in the ipc ids idr and lock the associated ipc object. |
| 688 | * | 693 | * |
| 689 | * ipc_ids.mutex is not necessarily held before this function is called, | ||
| 690 | * that's why we enter a RCU read section. | ||
| 691 | * The ipc object is locked on exit. | 694 | * The ipc object is locked on exit. |
| 695 | * | ||
| 696 | * This is the routine that should be called when the rw_mutex is not already | ||
| 697 | * held, i.e. idr tree not protected: it protects the idr tree in read mode | ||
| 698 | * during the idr_find(). | ||
| 692 | */ | 699 | */ |
| 693 | 700 | ||
| 694 | struct kern_ipc_perm *ipc_lock(struct ipc_ids *ids, int id) | 701 | struct kern_ipc_perm *ipc_lock(struct ipc_ids *ids, int id) |
| @@ -696,13 +703,18 @@ struct kern_ipc_perm *ipc_lock(struct ipc_ids *ids, int id) | |||
| 696 | struct kern_ipc_perm *out; | 703 | struct kern_ipc_perm *out; |
| 697 | int lid = ipcid_to_idx(id); | 704 | int lid = ipcid_to_idx(id); |
| 698 | 705 | ||
| 706 | down_read(&ids->rw_mutex); | ||
| 707 | |||
| 699 | rcu_read_lock(); | 708 | rcu_read_lock(); |
| 700 | out = idr_find(&ids->ipcs_idr, lid); | 709 | out = idr_find(&ids->ipcs_idr, lid); |
| 701 | if (out == NULL) { | 710 | if (out == NULL) { |
| 702 | rcu_read_unlock(); | 711 | rcu_read_unlock(); |
| 712 | up_read(&ids->rw_mutex); | ||
| 703 | return ERR_PTR(-EINVAL); | 713 | return ERR_PTR(-EINVAL); |
| 704 | } | 714 | } |
| 705 | 715 | ||
| 716 | up_read(&ids->rw_mutex); | ||
| 717 | |||
| 706 | spin_lock(&out->lock); | 718 | spin_lock(&out->lock); |
| 707 | 719 | ||
| 708 | /* ipc_rmid() may have already freed the ID while ipc_lock | 720 | /* ipc_rmid() may have already freed the ID while ipc_lock |
| @@ -717,6 +729,40 @@ struct kern_ipc_perm *ipc_lock(struct ipc_ids *ids, int id) | |||
| 717 | return out; | 729 | return out; |
| 718 | } | 730 | } |
| 719 | 731 | ||
| 732 | /** | ||
| 733 | * ipc_lock_down - Lock an ipc structure with rw_sem held | ||
| 734 | * @ids: IPC identifier set | ||
| 735 | * @id: ipc id to look for | ||
| 736 | * | ||
| 737 | * Look for an id in the ipc ids idr and lock the associated ipc object. | ||
| 738 | * | ||
| 739 | * The ipc object is locked on exit. | ||
| 740 | * | ||
| 741 | * This is the routine that should be called when the rw_mutex is already | ||
| 742 | * held, i.e. idr tree protected. | ||
| 743 | */ | ||
| 744 | |||
| 745 | struct kern_ipc_perm *ipc_lock_down(struct ipc_ids *ids, int id) | ||
| 746 | { | ||
| 747 | struct kern_ipc_perm *out; | ||
| 748 | int lid = ipcid_to_idx(id); | ||
| 749 | |||
| 750 | rcu_read_lock(); | ||
| 751 | out = idr_find(&ids->ipcs_idr, lid); | ||
| 752 | if (out == NULL) { | ||
| 753 | rcu_read_unlock(); | ||
| 754 | return ERR_PTR(-EINVAL); | ||
| 755 | } | ||
| 756 | |||
| 757 | spin_lock(&out->lock); | ||
| 758 | |||
| 759 | /* | ||
| 760 | * No need to verify that the structure is still valid since the | ||
| 761 | * rw_mutex is held. | ||
| 762 | */ | ||
| 763 | return out; | ||
| 764 | } | ||
| 765 | |||
| 720 | #ifdef __ARCH_WANT_IPC_PARSE_VERSION | 766 | #ifdef __ARCH_WANT_IPC_PARSE_VERSION |
| 721 | 767 | ||
| 722 | 768 | ||
| @@ -808,7 +854,7 @@ static void *sysvipc_proc_start(struct seq_file *s, loff_t *pos) | |||
| 808 | * Take the lock - this will be released by the corresponding | 854 | * Take the lock - this will be released by the corresponding |
| 809 | * call to stop(). | 855 | * call to stop(). |
| 810 | */ | 856 | */ |
| 811 | mutex_lock(&ids->mutex); | 857 | down_read(&ids->rw_mutex); |
| 812 | 858 | ||
| 813 | /* pos < 0 is invalid */ | 859 | /* pos < 0 is invalid */ |
| 814 | if (*pos < 0) | 860 | if (*pos < 0) |
| @@ -835,7 +881,7 @@ static void sysvipc_proc_stop(struct seq_file *s, void *it) | |||
| 835 | 881 | ||
| 836 | ids = iter->ns->ids[iface->ids]; | 882 | ids = iter->ns->ids[iface->ids]; |
| 837 | /* Release the lock we took in start() */ | 883 | /* Release the lock we took in start() */ |
| 838 | mutex_unlock(&ids->mutex); | 884 | up_read(&ids->rw_mutex); |
| 839 | } | 885 | } |
| 840 | 886 | ||
| 841 | static int sysvipc_proc_show(struct seq_file *s, void *it) | 887 | static int sysvipc_proc_show(struct seq_file *s, void *it) |
diff --git a/ipc/util.h b/ipc/util.h index 99414a36a250..bd47687077e0 100644 --- a/ipc/util.h +++ b/ipc/util.h | |||
| @@ -32,7 +32,7 @@ struct ipc_ids { | |||
| 32 | int in_use; | 32 | int in_use; |
| 33 | unsigned short seq; | 33 | unsigned short seq; |
| 34 | unsigned short seq_max; | 34 | unsigned short seq_max; |
| 35 | struct mutex mutex; | 35 | struct rw_semaphore rw_mutex; |
| 36 | struct idr ipcs_idr; | 36 | struct idr ipcs_idr; |
| 37 | }; | 37 | }; |
| 38 | 38 | ||
| @@ -81,8 +81,10 @@ void __init ipc_init_proc_interface(const char *path, const char *header, | |||
| 81 | 81 | ||
| 82 | #define ipcid_to_idx(id) ((id) % SEQ_MULTIPLIER) | 82 | #define ipcid_to_idx(id) ((id) % SEQ_MULTIPLIER) |
| 83 | 83 | ||
| 84 | /* must be called with ids->mutex acquired.*/ | 84 | /* must be called with ids->rw_mutex acquired for writing */ |
| 85 | int ipc_addid(struct ipc_ids *, struct kern_ipc_perm *, int); | 85 | int ipc_addid(struct ipc_ids *, struct kern_ipc_perm *, int); |
| 86 | |||
| 87 | /* must be called with ids->rw_mutex acquired for reading */ | ||
| 86 | int ipc_get_maxid(struct ipc_ids *); | 88 | int ipc_get_maxid(struct ipc_ids *); |
| 87 | 89 | ||
| 88 | /* must be called with both locks acquired. */ | 90 | /* must be called with both locks acquired. */ |
| @@ -107,6 +109,11 @@ void* ipc_rcu_alloc(int size); | |||
| 107 | void ipc_rcu_getref(void *ptr); | 109 | void ipc_rcu_getref(void *ptr); |
| 108 | void ipc_rcu_putref(void *ptr); | 110 | void ipc_rcu_putref(void *ptr); |
| 109 | 111 | ||
| 112 | /* | ||
| 113 | * ipc_lock_down: called with rw_mutex held | ||
| 114 | * ipc_lock: called without that lock held | ||
| 115 | */ | ||
| 116 | struct kern_ipc_perm *ipc_lock_down(struct ipc_ids *, int); | ||
| 110 | struct kern_ipc_perm *ipc_lock(struct ipc_ids *, int); | 117 | struct kern_ipc_perm *ipc_lock(struct ipc_ids *, int); |
| 111 | 118 | ||
| 112 | void kernel_to_ipc64_perm(struct kern_ipc_perm *in, struct ipc64_perm *out); | 119 | void kernel_to_ipc64_perm(struct kern_ipc_perm *in, struct ipc64_perm *out); |
| @@ -155,6 +162,23 @@ static inline void ipc_unlock(struct kern_ipc_perm *perm) | |||
| 155 | rcu_read_unlock(); | 162 | rcu_read_unlock(); |
| 156 | } | 163 | } |
| 157 | 164 | ||
| 165 | static inline struct kern_ipc_perm *ipc_lock_check_down(struct ipc_ids *ids, | ||
| 166 | int id) | ||
| 167 | { | ||
| 168 | struct kern_ipc_perm *out; | ||
| 169 | |||
| 170 | out = ipc_lock_down(ids, id); | ||
| 171 | if (IS_ERR(out)) | ||
| 172 | return out; | ||
| 173 | |||
| 174 | if (ipc_checkid(ids, out, id)) { | ||
| 175 | ipc_unlock(out); | ||
| 176 | return ERR_PTR(-EIDRM); | ||
| 177 | } | ||
| 178 | |||
| 179 | return out; | ||
| 180 | } | ||
| 181 | |||
| 158 | static inline struct kern_ipc_perm *ipc_lock_check(struct ipc_ids *ids, | 182 | static inline struct kern_ipc_perm *ipc_lock_check(struct ipc_ids *ids, |
| 159 | int id) | 183 | int id) |
| 160 | { | 184 | { |
