aboutsummaryrefslogtreecommitdiffstats
path: root/ipc/util.c
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/util.c
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/util.c')
-rw-r--r--ipc/util.c48
1 files changed, 25 insertions, 23 deletions
diff --git a/ipc/util.c b/ipc/util.c
index 3df0af3158a5..579201e4bc01 100644
--- a/ipc/util.c
+++ b/ipc/util.c
@@ -439,9 +439,9 @@ void ipc_rmid(struct ipc_ids *ids, struct kern_ipc_perm *ipcp)
439 * NULL is returned if the allocation fails 439 * NULL is returned if the allocation fails
440 */ 440 */
441 441
442void* ipc_alloc(int size) 442void *ipc_alloc(int size)
443{ 443{
444 void* out; 444 void *out;
445 if(size > PAGE_SIZE) 445 if(size > PAGE_SIZE)
446 out = vmalloc(size); 446 out = vmalloc(size);
447 else 447 else
@@ -478,7 +478,7 @@ void ipc_free(void* ptr, int size)
478 */ 478 */
479struct ipc_rcu_hdr 479struct ipc_rcu_hdr
480{ 480{
481 int refcount; 481 atomic_t refcount;
482 int is_vmalloc; 482 int is_vmalloc;
483 void *data[0]; 483 void *data[0];
484}; 484};
@@ -516,39 +516,41 @@ static inline int rcu_use_vmalloc(int size)
516 * @size: size desired 516 * @size: size desired
517 * 517 *
518 * Allocate memory for the rcu header structure + the object. 518 * Allocate memory for the rcu header structure + the object.
519 * Returns the pointer to the object. 519 * Returns the pointer to the object or NULL upon failure.
520 * NULL is returned if the allocation fails.
521 */ 520 */
522 521void *ipc_rcu_alloc(int size)
523void* ipc_rcu_alloc(int size)
524{ 522{
525 void* out; 523 void *out;
526 /* 524
525 /*
527 * We prepend the allocation with the rcu struct, and 526 * We prepend the allocation with the rcu struct, and
528 * workqueue if necessary (for vmalloc). 527 * workqueue if necessary (for vmalloc).
529 */ 528 */
530 if (rcu_use_vmalloc(size)) { 529 if (rcu_use_vmalloc(size)) {
531 out = vmalloc(HDRLEN_VMALLOC + size); 530 out = vmalloc(HDRLEN_VMALLOC + size);
532 if (out) { 531 if (!out)
533 out += HDRLEN_VMALLOC; 532 goto done;
534 container_of(out, struct ipc_rcu_hdr, data)->is_vmalloc = 1; 533
535 container_of(out, struct ipc_rcu_hdr, data)->refcount = 1; 534 out += HDRLEN_VMALLOC;
536 } 535 container_of(out, struct ipc_rcu_hdr, data)->is_vmalloc = 1;
537 } else { 536 } else {
538 out = kmalloc(HDRLEN_KMALLOC + size, GFP_KERNEL); 537 out = kmalloc(HDRLEN_KMALLOC + size, GFP_KERNEL);
539 if (out) { 538 if (!out)
540 out += HDRLEN_KMALLOC; 539 goto done;
541 container_of(out, struct ipc_rcu_hdr, data)->is_vmalloc = 0; 540
542 container_of(out, struct ipc_rcu_hdr, data)->refcount = 1; 541 out += HDRLEN_KMALLOC;
543 } 542 container_of(out, struct ipc_rcu_hdr, data)->is_vmalloc = 0;
544 } 543 }
545 544
545 /* set reference counter no matter what kind of allocation was done */
546 atomic_set(&container_of(out, struct ipc_rcu_hdr, data)->refcount, 1);
547done:
546 return out; 548 return out;
547} 549}
548 550
549void ipc_rcu_getref(void *ptr) 551int ipc_rcu_getref(void *ptr)
550{ 552{
551 container_of(ptr, struct ipc_rcu_hdr, data)->refcount++; 553 return atomic_inc_not_zero(&container_of(ptr, struct ipc_rcu_hdr, data)->refcount);
552} 554}
553 555
554static void ipc_do_vfree(struct work_struct *work) 556static void ipc_do_vfree(struct work_struct *work)
@@ -578,7 +580,7 @@ static void ipc_schedule_free(struct rcu_head *head)
578 580
579void ipc_rcu_putref(void *ptr) 581void ipc_rcu_putref(void *ptr)
580{ 582{
581 if (--container_of(ptr, struct ipc_rcu_hdr, data)->refcount > 0) 583 if (!atomic_dec_and_test(&container_of(ptr, struct ipc_rcu_hdr, data)->refcount))
582 return; 584 return;
583 585
584 if (container_of(ptr, struct ipc_rcu_hdr, data)->is_vmalloc) { 586 if (container_of(ptr, struct ipc_rcu_hdr, data)->is_vmalloc) {