aboutsummaryrefslogtreecommitdiffstats
path: root/ipc
diff options
context:
space:
mode:
authorNadia Derbey <Nadia.Derbey@bull.net>2007-10-19 02:40:54 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-10-19 14:53:48 -0400
commit3e148c79938aa39035669c1cfa3ff60722134535 (patch)
tree0effb3edfece56ea38a9727ec8f4721d9a4c3ea8 /ipc
parentf4566f04854d78acfc74b9acb029744acde9d033 (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.c41
-rw-r--r--ipc/sem.c44
-rw-r--r--ipc/shm.c77
-rw-r--r--ipc/util.c78
-rw-r--r--ipc/util.h28
5 files changed, 198 insertions, 70 deletions
diff --git a/ipc/msg.c b/ipc/msg.c
index b7274dbf0917..413bf9c7aec3 100644
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -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 */
144static 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 */
139static inline struct msg_queue *msg_lock(struct ipc_namespace *ns, int id) 156static 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 */
166static int newque(struct ipc_namespace *ns, struct ipc_params *params) 183static 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 */
266static void freeque(struct ipc_namespace *ns, struct msg_queue *msq) 283static 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 */
291static inline int msg_security(struct kern_ipc_perm *ipcp, int msgflg) 308static 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;
578out_up: 595out_up:
579 mutex_unlock(&msg_ids(ns).mutex); 596 up_write(&msg_ids(ns).rw_mutex);
580 return err; 597 return err;
581out_unlock_up: 598out_unlock_up:
582 msg_unlock(msq); 599 msg_unlock(msq);
diff --git a/ipc/sem.c b/ipc/sem.c
index 45c7e573c201..019e21332dd6 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -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 */
181static 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 */
177static inline struct sem_array *sem_lock(struct ipc_namespace *ns, int id) 193static 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
239static int newary(struct ipc_namespace *ns, struct ipc_params *params) 255static 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 */
295static inline int sem_security(struct kern_ipc_perm *ipcp, int semflg) 311static 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 */
306static inline int sem_more_checks(struct kern_ipc_perm *ipcp, 322static 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 */
535static void freeary(struct ipc_namespace *ns, struct sem_array *sma) 551static 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;
diff --git a/ipc/shm.c b/ipc/shm.c
index f28f2a3163e1..b27d31f3aaba 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -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 */
89static void do_shm_rmid(struct ipc_namespace *ns, struct shmid_kernel *shp) 89static 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 */
148static 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
156static 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 */
144static inline struct shmid_kernel *shm_lock(struct ipc_namespace *ns, int id) 169static 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 */
195static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp) 220static 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
238static int shm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 263static 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
359static int newseg(struct ipc_namespace *ns, struct ipc_params *params) 384static 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 */
447static inline int shm_security(struct kern_ipc_perm *ipcp, int shmflg) 472static 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 */
458static inline int shm_more_checks(struct kern_ipc_perm *ipcp, 483static 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 */
583static void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss, 608static 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)
864out_unlock_up: 889out_unlock_up:
865 shm_unlock(shp); 890 shm_unlock(shp);
866out_up: 891out_up:
867 mutex_unlock(&shm_ids(ns).mutex); 892 up_write(&shm_ids(ns).rw_mutex);
868 goto out; 893 goto out;
869out_unlock: 894out_unlock:
870 shm_unlock(shp); 895 shm_unlock(shp);
@@ -998,8 +1023,8 @@ invalid:
998 fput(file); 1023 fput(file);
999 1024
1000out_nattch: 1025out_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
1012out: 1037out:
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
137void ipc_init_ids(struct ipc_ids *ids) 138void 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
231int ipc_get_maxid(struct ipc_ids *ids) 232int 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
269int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size) 270int 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 */
340static int ipc_check_perms(struct kern_ipc_perm *ipcp, struct ipc_ops *ops, 341static 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
422void ipc_rmid(struct ipc_ids *ids, struct kern_ipc_perm *ipcp) 427void 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
694struct kern_ipc_perm *ipc_lock(struct ipc_ids *ids, int id) 701struct 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
745struct 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
841static int sysvipc_proc_show(struct seq_file *s, void *it) 887static 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 */
85int ipc_addid(struct ipc_ids *, struct kern_ipc_perm *, int); 85int ipc_addid(struct ipc_ids *, struct kern_ipc_perm *, int);
86
87/* must be called with ids->rw_mutex acquired for reading */
86int ipc_get_maxid(struct ipc_ids *); 88int 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);
107void ipc_rcu_getref(void *ptr); 109void ipc_rcu_getref(void *ptr);
108void ipc_rcu_putref(void *ptr); 110void 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 */
116struct kern_ipc_perm *ipc_lock_down(struct ipc_ids *, int);
110struct kern_ipc_perm *ipc_lock(struct ipc_ids *, int); 117struct kern_ipc_perm *ipc_lock(struct ipc_ids *, int);
111 118
112void kernel_to_ipc64_perm(struct kern_ipc_perm *in, struct ipc64_perm *out); 119void 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
165static 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
158static inline struct kern_ipc_perm *ipc_lock_check(struct ipc_ids *ids, 182static inline struct kern_ipc_perm *ipc_lock_check(struct ipc_ids *ids,
159 int id) 183 int id)
160{ 184{