aboutsummaryrefslogtreecommitdiffstats
path: root/ipc
diff options
context:
space:
mode:
authorRik van Riel <riel@surriel.com>2013-04-30 22:15:44 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-05-01 11:12:58 -0400
commit6062a8dc0517bce23e3c2f7d2fea5e22411269a3 (patch)
treee1dd1553167fccb726a8aa9352b27ba14f188374 /ipc
parent9f1bc2c9022c1d4944c4a1a44c2f365487420aca (diff)
ipc,sem: fine grained locking for semtimedop
Introduce finer grained locking for semtimedop, to handle the common case of a program wanting to manipulate one semaphore from an array with multiple semaphores. If the call is a semop manipulating just one semaphore in an array with multiple semaphores, only take the lock for that semaphore itself. If the call needs to manipulate multiple semaphores, or another caller is in a transaction that manipulates multiple semaphores, the sem_array lock is taken, as well as all the locks for the individual semaphores. On a 24 CPU system, performance numbers with the semop-multi test with N threads and N semaphores, look like this: vanilla Davidlohr's Davidlohr's + Davidlohr's + threads patches rwlock patches v3 patches 10 610652 726325 1783589 2142206 20 341570 365699 1520453 1977878 30 288102 307037 1498167 2037995 40 290714 305955 1612665 2256484 50 288620 312890 1733453 2650292 60 289987 306043 1649360 2388008 70 291298 306347 1723167 2717486 80 290948 305662 1729545 2763582 90 290996 306680 1736021 2757524 100 292243 306700 1773700 3059159 [davidlohr.bueso@hp.com: do not call sem_lock when bogus sma] [davidlohr.bueso@hp.com: make refcounter atomic] Signed-off-by: Rik van Riel <riel@redhat.com> Suggested-by: Linus Torvalds <torvalds@linux-foundation.org> Acked-by: Davidlohr Bueso <davidlohr.bueso@hp.com> Cc: Chegu Vinod <chegu_vinod@hp.com> Cc: Jason Low <jason.low2@hp.com> Reviewed-by: Michel Lespinasse <walken@google.com> Cc: Peter Hurley <peter@hurleysoftware.com> Cc: Stanislav Kinsbursky <skinsbursky@parallels.com> Tested-by: Emmanuel Benisty <benisty.e@gmail.com> Tested-by: Sedat Dilek <sedat.dilek@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'ipc')
-rw-r--r--ipc/msg.c7
-rw-r--r--ipc/sem.c271
-rw-r--r--ipc/util.c48
-rw-r--r--ipc/util.h2
4 files changed, 203 insertions, 125 deletions
diff --git a/ipc/msg.c b/ipc/msg.c
index a80aaf463d9c..09a1f41e6595 100644
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -687,7 +687,12 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext,
687 goto out_unlock_free; 687 goto out_unlock_free;
688 } 688 }
689 ss_add(msq, &s); 689 ss_add(msq, &s);
690 ipc_rcu_getref(msq); 690
691 if (!ipc_rcu_getref(msq)) {
692 err = -EIDRM;
693 goto out_unlock_free;
694 }
695
691 msg_unlock(msq); 696 msg_unlock(msq);
692 schedule(); 697 schedule();
693 698
diff --git a/ipc/sem.c b/ipc/sem.c
index f68b61749a85..e78ee3186d1f 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -94,6 +94,7 @@
94struct sem { 94struct sem {
95 int semval; /* current value */ 95 int semval; /* current value */
96 int sempid; /* pid of last operation */ 96 int sempid; /* pid of last operation */
97 spinlock_t lock; /* spinlock for fine-grained semtimedop */
97 struct list_head sem_pending; /* pending single-sop operations */ 98 struct list_head sem_pending; /* pending single-sop operations */
98}; 99};
99 100
@@ -137,7 +138,6 @@ struct sem_undo_list {
137 138
138#define sem_ids(ns) ((ns)->ids[IPC_SEM_IDS]) 139#define sem_ids(ns) ((ns)->ids[IPC_SEM_IDS])
139 140
140#define sem_unlock(sma) ipc_unlock(&(sma)->sem_perm)
141#define sem_checkid(sma, semid) ipc_checkid(&sma->sem_perm, semid) 141#define sem_checkid(sma, semid) ipc_checkid(&sma->sem_perm, semid)
142 142
143static int newary(struct ipc_namespace *, struct ipc_params *); 143static int newary(struct ipc_namespace *, struct ipc_params *);
@@ -190,10 +190,89 @@ void __init sem_init (void)
190} 190}
191 191
192/* 192/*
193 * If the request contains only one semaphore operation, and there are
194 * no complex transactions pending, lock only the semaphore involved.
195 * Otherwise, lock the entire semaphore array, since we either have
196 * multiple semaphores in our own semops, or we need to look at
197 * semaphores from other pending complex operations.
198 *
199 * Carefully guard against sma->complex_count changing between zero
200 * and non-zero while we are spinning for the lock. The value of
201 * sma->complex_count cannot change while we are holding the lock,
202 * so sem_unlock should be fine.
203 *
204 * The global lock path checks that all the local locks have been released,
205 * checking each local lock once. This means that the local lock paths
206 * cannot start their critical sections while the global lock is held.
207 */
208static inline int sem_lock(struct sem_array *sma, struct sembuf *sops,
209 int nsops)
210{
211 int locknum;
212 again:
213 if (nsops == 1 && !sma->complex_count) {
214 struct sem *sem = sma->sem_base + sops->sem_num;
215
216 /* Lock just the semaphore we are interested in. */
217 spin_lock(&sem->lock);
218
219 /*
220 * If sma->complex_count was set while we were spinning,
221 * we may need to look at things we did not lock here.
222 */
223 if (unlikely(sma->complex_count)) {
224 spin_unlock(&sem->lock);
225 goto lock_array;
226 }
227
228 /*
229 * Another process is holding the global lock on the
230 * sem_array; we cannot enter our critical section,
231 * but have to wait for the global lock to be released.
232 */
233 if (unlikely(spin_is_locked(&sma->sem_perm.lock))) {
234 spin_unlock(&sem->lock);
235 spin_unlock_wait(&sma->sem_perm.lock);
236 goto again;
237 }
238
239 locknum = sops->sem_num;
240 } else {
241 int i;
242 /*
243 * Lock the semaphore array, and wait for all of the
244 * individual semaphore locks to go away. The code
245 * above ensures no new single-lock holders will enter
246 * their critical section while the array lock is held.
247 */
248 lock_array:
249 spin_lock(&sma->sem_perm.lock);
250 for (i = 0; i < sma->sem_nsems; i++) {
251 struct sem *sem = sma->sem_base + i;
252 spin_unlock_wait(&sem->lock);
253 }
254 locknum = -1;
255 }
256 return locknum;
257}
258
259static inline void sem_unlock(struct sem_array *sma, int locknum)
260{
261 if (locknum == -1) {
262 spin_unlock(&sma->sem_perm.lock);
263 } else {
264 struct sem *sem = sma->sem_base + locknum;
265 spin_unlock(&sem->lock);
266 }
267 rcu_read_unlock();
268}
269
270/*
193 * sem_lock_(check_) routines are called in the paths where the rw_mutex 271 * sem_lock_(check_) routines are called in the paths where the rw_mutex
194 * is not held. 272 * is not held.
195 */ 273 */
196static inline struct sem_array *sem_obtain_lock(struct ipc_namespace *ns, int id) 274static inline struct sem_array *sem_obtain_lock(struct ipc_namespace *ns,
275 int id, struct sembuf *sops, int nsops, int *locknum)
197{ 276{
198 struct kern_ipc_perm *ipcp; 277 struct kern_ipc_perm *ipcp;
199 struct sem_array *sma; 278 struct sem_array *sma;
@@ -205,7 +284,8 @@ static inline struct sem_array *sem_obtain_lock(struct ipc_namespace *ns, int id
205 goto err; 284 goto err;
206 } 285 }
207 286
208 spin_lock(&ipcp->lock); 287 sma = container_of(ipcp, struct sem_array, sem_perm);
288 *locknum = sem_lock(sma, sops, nsops);
209 289
210 /* ipc_rmid() may have already freed the ID while sem_lock 290 /* ipc_rmid() may have already freed the ID while sem_lock
211 * was spinning: verify that the structure is still valid 291 * was spinning: verify that the structure is still valid
@@ -213,7 +293,7 @@ static inline struct sem_array *sem_obtain_lock(struct ipc_namespace *ns, int id
213 if (!ipcp->deleted) 293 if (!ipcp->deleted)
214 return container_of(ipcp, struct sem_array, sem_perm); 294 return container_of(ipcp, struct sem_array, sem_perm);
215 295
216 spin_unlock(&ipcp->lock); 296 sem_unlock(sma, *locknum);
217 sma = ERR_PTR(-EINVAL); 297 sma = ERR_PTR(-EINVAL);
218err: 298err:
219 rcu_read_unlock(); 299 rcu_read_unlock();
@@ -230,17 +310,6 @@ static inline struct sem_array *sem_obtain_object(struct ipc_namespace *ns, int
230 return container_of(ipcp, struct sem_array, sem_perm); 310 return container_of(ipcp, struct sem_array, sem_perm);
231} 311}
232 312
233static inline struct sem_array *sem_lock_check(struct ipc_namespace *ns,
234 int id)
235{
236 struct kern_ipc_perm *ipcp = ipc_lock_check(&sem_ids(ns), id);
237
238 if (IS_ERR(ipcp))
239 return ERR_CAST(ipcp);
240
241 return container_of(ipcp, struct sem_array, sem_perm);
242}
243
244static inline struct sem_array *sem_obtain_object_check(struct ipc_namespace *ns, 313static inline struct sem_array *sem_obtain_object_check(struct ipc_namespace *ns,
245 int id) 314 int id)
246{ 315{
@@ -254,21 +323,21 @@ static inline struct sem_array *sem_obtain_object_check(struct ipc_namespace *ns
254 323
255static inline void sem_lock_and_putref(struct sem_array *sma) 324static inline void sem_lock_and_putref(struct sem_array *sma)
256{ 325{
257 ipc_lock_by_ptr(&sma->sem_perm); 326 rcu_read_lock();
327 sem_lock(sma, NULL, -1);
258 ipc_rcu_putref(sma); 328 ipc_rcu_putref(sma);
259} 329}
260 330
261static inline void sem_getref_and_unlock(struct sem_array *sma) 331static inline void sem_getref_and_unlock(struct sem_array *sma)
262{ 332{
263 ipc_rcu_getref(sma); 333 WARN_ON_ONCE(!ipc_rcu_getref(sma));
264 ipc_unlock(&(sma)->sem_perm);