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/util.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/util.c')
-rw-r--r-- | ipc/util.c | 78 |
1 files changed, 62 insertions, 16 deletions
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) |