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/shm.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/shm.c')
-rw-r--r-- | ipc/shm.c | 77 |
1 files changed, 51 insertions, 26 deletions
@@ -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; |