aboutsummaryrefslogtreecommitdiffstats
path: root/ipc/util.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/util.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/util.c')
-rw-r--r--ipc/util.c78
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
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)