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/msg.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/msg.c')
-rw-r--r-- | ipc/msg.c | 41 |
1 files changed, 29 insertions, 12 deletions
@@ -34,7 +34,7 @@ | |||
34 | #include <linux/syscalls.h> | 34 | #include <linux/syscalls.h> |
35 | #include <linux/audit.h> | 35 | #include <linux/audit.h> |
36 | #include <linux/seq_file.h> | 36 | #include <linux/seq_file.h> |
37 | #include <linux/mutex.h> | 37 | #include <linux/rwsem.h> |
38 | #include <linux/nsproxy.h> | 38 | #include <linux/nsproxy.h> |
39 | 39 | ||
40 | #include <asm/current.h> | 40 | #include <asm/current.h> |
@@ -110,7 +110,7 @@ void msg_exit_ns(struct ipc_namespace *ns) | |||
110 | int next_id; | 110 | int next_id; |
111 | int total, in_use; | 111 | int total, in_use; |
112 | 112 | ||
113 | mutex_lock(&msg_ids(ns).mutex); | 113 | down_write(&msg_ids(ns).rw_mutex); |
114 | 114 | ||
115 | in_use = msg_ids(ns).in_use; | 115 | in_use = msg_ids(ns).in_use; |
116 | 116 | ||
@@ -122,7 +122,8 @@ void msg_exit_ns(struct ipc_namespace *ns) | |||
122 | freeque(ns, msq); | 122 | freeque(ns, msq); |
123 | total++; | 123 | total++; |
124 | } | 124 | } |
125 | mutex_unlock(&msg_ids(ns).mutex); | 125 | |
126 | up_write(&msg_ids(ns).rw_mutex); | ||
126 | 127 | ||
127 | kfree(ns->ids[IPC_MSG_IDS]); | 128 | kfree(ns->ids[IPC_MSG_IDS]); |
128 | ns->ids[IPC_MSG_IDS] = NULL; | 129 | ns->ids[IPC_MSG_IDS] = NULL; |
@@ -136,6 +137,22 @@ void __init msg_init(void) | |||
136 | IPC_MSG_IDS, sysvipc_msg_proc_show); | 137 | IPC_MSG_IDS, sysvipc_msg_proc_show); |
137 | } | 138 | } |
138 | 139 | ||
140 | /* | ||
141 | * This routine is called in the paths where the rw_mutex is held to protect | ||
142 | * access to the idr tree. | ||
143 | */ | ||
144 | static inline struct msg_queue *msg_lock_check_down(struct ipc_namespace *ns, | ||
145 | int id) | ||
146 | { | ||
147 | struct kern_ipc_perm *ipcp = ipc_lock_check_down(&msg_ids(ns), id); | ||
148 | |||
149 | return container_of(ipcp, struct msg_queue, q_perm); | ||
150 | } | ||
151 | |||
152 | /* | ||
153 | * msg_lock_(check_) routines are called in the paths where the rw_mutex | ||
154 | * is not held. | ||
155 | */ | ||
139 | static inline struct msg_queue *msg_lock(struct ipc_namespace *ns, int id) | 156 | static inline struct msg_queue *msg_lock(struct ipc_namespace *ns, int id) |
140 | { | 157 | { |
141 | struct kern_ipc_perm *ipcp = ipc_lock(&msg_ids(ns), id); | 158 | struct kern_ipc_perm *ipcp = ipc_lock(&msg_ids(ns), id); |
@@ -161,7 +178,7 @@ static inline void msg_rmid(struct ipc_namespace *ns, struct msg_queue *s) | |||
161 | * @ns: namespace | 178 | * @ns: namespace |
162 | * @params: ptr to the structure that contains the key and msgflg | 179 | * @params: ptr to the structure that contains the key and msgflg |
163 | * | 180 | * |
164 | * Called with msg_ids.mutex held | 181 | * Called with msg_ids.rw_mutex held (writer) |
165 | */ | 182 | */ |
166 | static int newque(struct ipc_namespace *ns, struct ipc_params *params) | 183 | static int newque(struct ipc_namespace *ns, struct ipc_params *params) |
167 | { | 184 | { |
@@ -260,8 +277,8 @@ static void expunge_all(struct msg_queue *msq, int res) | |||
260 | * removes the message queue from message queue ID IDR, and cleans up all the | 277 | * removes the message queue from message queue ID IDR, and cleans up all the |
261 | * messages associated with this queue. | 278 | * messages associated with this queue. |
262 | * | 279 | * |
263 | * msg_ids.mutex and the spinlock for this message queue are held | 280 | * msg_ids.rw_mutex (writer) and the spinlock for this message queue are held |
264 | * before freeque() is called. msg_ids.mutex remains locked on exit. | 281 | * before freeque() is called. msg_ids.rw_mutex remains locked on exit. |
265 | */ | 282 | */ |
266 | static void freeque(struct ipc_namespace *ns, struct msg_queue *msq) | 283 | static void freeque(struct ipc_namespace *ns, struct msg_queue *msq) |
267 | { | 284 | { |
@@ -286,7 +303,7 @@ static void freeque(struct ipc_namespace *ns, struct msg_queue *msq) | |||
286 | } | 303 | } |
287 | 304 | ||
288 | /* | 305 | /* |
289 | * Called with msg_ids.mutex and ipcp locked. | 306 | * Called with msg_ids.rw_mutex and ipcp locked. |
290 | */ | 307 | */ |
291 | static inline int msg_security(struct kern_ipc_perm *ipcp, int msgflg) | 308 | static inline int msg_security(struct kern_ipc_perm *ipcp, int msgflg) |
292 | { | 309 | { |
@@ -444,7 +461,7 @@ asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf) | |||
444 | msginfo.msgmnb = ns->msg_ctlmnb; | 461 | msginfo.msgmnb = ns->msg_ctlmnb; |
445 | msginfo.msgssz = MSGSSZ; | 462 | msginfo.msgssz = MSGSSZ; |
446 | msginfo.msgseg = MSGSEG; | 463 | msginfo.msgseg = MSGSEG; |
447 | mutex_lock(&msg_ids(ns).mutex); | 464 | down_read(&msg_ids(ns).rw_mutex); |
448 | if (cmd == MSG_INFO) { | 465 | if (cmd == MSG_INFO) { |
449 | msginfo.msgpool = msg_ids(ns).in_use; | 466 | msginfo.msgpool = msg_ids(ns).in_use; |
450 | msginfo.msgmap = atomic_read(&msg_hdrs); | 467 | msginfo.msgmap = atomic_read(&msg_hdrs); |
@@ -455,7 +472,7 @@ asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf) | |||
455 | msginfo.msgtql = MSGTQL; | 472 | msginfo.msgtql = MSGTQL; |
456 | } | 473 | } |
457 | max_id = ipc_get_maxid(&msg_ids(ns)); | 474 | max_id = ipc_get_maxid(&msg_ids(ns)); |
458 | mutex_unlock(&msg_ids(ns).mutex); | 475 | up_read(&msg_ids(ns).rw_mutex); |
459 | if (copy_to_user(buf, &msginfo, sizeof(struct msginfo))) | 476 | if (copy_to_user(buf, &msginfo, sizeof(struct msginfo))) |
460 | return -EFAULT; | 477 | return -EFAULT; |
461 | return (max_id < 0) ? 0 : max_id; | 478 | return (max_id < 0) ? 0 : max_id; |
@@ -516,8 +533,8 @@ asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf) | |||
516 | return -EINVAL; | 533 | return -EINVAL; |
517 | } | 534 | } |
518 | 535 | ||
519 | mutex_lock(&msg_ids(ns).mutex); | 536 | down_write(&msg_ids(ns).rw_mutex); |
520 | msq = msg_lock_check(ns, msqid); | 537 | msq = msg_lock_check_down(ns, msqid); |
521 | if (IS_ERR(msq)) { | 538 | if (IS_ERR(msq)) { |
522 | err = PTR_ERR(msq); | 539 | err = PTR_ERR(msq); |
523 | goto out_up; | 540 | goto out_up; |
@@ -576,7 +593,7 @@ asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf) | |||
576 | } | 593 | } |
577 | err = 0; | 594 | err = 0; |
578 | out_up: | 595 | out_up: |
579 | mutex_unlock(&msg_ids(ns).mutex); | 596 | up_write(&msg_ids(ns).rw_mutex); |
580 | return err; | 597 | return err; |
581 | out_unlock_up: | 598 | out_unlock_up: |
582 | msg_unlock(msq); | 599 | msg_unlock(msq); |