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 | { |