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/sem.c | |
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/sem.c')
-rw-r--r-- | ipc/sem.c | 44 |
1 files changed, 30 insertions, 14 deletions
@@ -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; |