aboutsummaryrefslogtreecommitdiffstats
path: root/ipc/sem.c
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/sem.c
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/sem.c')
-rw-r--r--ipc/sem.c44
1 files changed, 30 insertions, 14 deletions
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;