Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

locking/mutex: Enable optimistic spinning of woken waiter

This patch makes the waiter that sets the HANDOFF flag start spinning
instead of sleeping until the handoff is complete or the owner
sleeps. Otherwise, the handoff will cause the optimistic spinners to
abort spinning as the handed-off owner may not be running.

Tested-by: Jason Low <jason.low2@hpe.com>
Signed-off-by: Waiman Long <Waiman.Long@hpe.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Davidlohr Bueso <dave@stgolabs.net>
Cc: Ding Tianhong <dingtianhong@huawei.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Cc: Paul E. McKenney <paulmck@us.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Tim Chen <tim.c.chen@linux.intel.com>
Cc: Will Deacon <Will.Deacon@arm.com>
Link: http://lkml.kernel.org/r/1472254509-27508-2-git-send-email-Waiman.Long@hpe.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>

authored by

Waiman Long and committed by
Ingo Molnar
b341afb3 a40ca565

+54 -23
+54 -23
kernel/locking/mutex.c
··· 416 416 * 417 417 * Returns true when the lock was taken, otherwise false, indicating 418 418 * that we need to jump to the slowpath and sleep. 419 + * 420 + * The waiter flag is set to true if the spinner is a waiter in the wait 421 + * queue. The waiter-spinner will spin on the lock directly and concurrently 422 + * with the spinner at the head of the OSQ, if present, until the owner is 423 + * changed to itself. 419 424 */ 420 425 static bool mutex_optimistic_spin(struct mutex *lock, 421 - struct ww_acquire_ctx *ww_ctx, const bool use_ww_ctx) 426 + struct ww_acquire_ctx *ww_ctx, 427 + const bool use_ww_ctx, const bool waiter) 422 428 { 423 429 struct task_struct *task = current; 424 430 425 - if (!mutex_can_spin_on_owner(lock)) 426 - goto done; 431 + if (!waiter) { 432 + /* 433 + * The purpose of the mutex_can_spin_on_owner() function is 434 + * to eliminate the overhead of osq_lock() and osq_unlock() 435 + * in case spinning isn't possible. As a waiter-spinner 436 + * is not going to take OSQ lock anyway, there is no need 437 + * to call mutex_can_spin_on_owner(). 438 + */ 439 + if (!mutex_can_spin_on_owner(lock)) 440 + goto fail; 427 441 428 - /* 429 - * In order to avoid a stampede of mutex spinners trying to 430 - * acquire the mutex all at once, the spinners need to take a 431 - * MCS (queued) lock first before spinning on the owner field. 432 - */ 433 - if (!osq_lock(&lock->osq)) 434 - goto done; 442 + /* 443 + * In order to avoid a stampede of mutex spinners trying to 444 + * acquire the mutex all at once, the spinners need to take a 445 + * MCS (queued) lock first before spinning on the owner field. 446 + */ 447 + if (!osq_lock(&lock->osq)) 448 + goto fail; 449 + } 435 450 436 - while (true) { 451 + for (;;) { 437 452 struct task_struct *owner; 438 453 439 454 if (use_ww_ctx && ww_ctx->acquired > 0) { ··· 464 449 * performed the optimistic spinning cannot be done. 465 450 */ 466 451 if (READ_ONCE(ww->ctx)) 467 - break; 452 + goto fail_unlock; 468 453 } 469 454 470 455 /* ··· 472 457 * release the lock or go to sleep. 473 458 */ 474 459 owner = __mutex_owner(lock); 475 - if (owner && !mutex_spin_on_owner(lock, owner)) 476 - break; 460 + if (owner) { 461 + if (waiter && owner == task) { 462 + smp_mb(); /* ACQUIRE */ 463 + break; 464 + } 465 + 466 + if (!mutex_spin_on_owner(lock, owner)) 467 + goto fail_unlock; 468 + } 477 469 478 470 /* Try to acquire the mutex if it is unlocked. */ 479 - if (__mutex_trylock(lock, false)) { 480 - osq_unlock(&lock->osq); 481 - return true; 482 - } 471 + if (__mutex_trylock(lock, waiter)) 472 + break; 483 473 484 474 /* 485 475 * The cpu_relax() call is a compiler barrier which forces ··· 495 475 cpu_relax_lowlatency(); 496 476 } 497 477 498 - osq_unlock(&lock->osq); 499 - done: 478 + if (!waiter) 479 + osq_unlock(&lock->osq); 480 + 481 + return true; 482 + 483 + 484 + fail_unlock: 485 + if (!waiter) 486 + osq_unlock(&lock->osq); 487 + 488 + fail: 500 489 /* 501 490 * If we fell out of the spin path because of need_resched(), 502 491 * reschedule now, before we try-lock the mutex. This avoids getting ··· 524 495 } 525 496 #else 526 497 static bool mutex_optimistic_spin(struct mutex *lock, 527 - struct ww_acquire_ctx *ww_ctx, const bool use_ww_ctx) 498 + struct ww_acquire_ctx *ww_ctx, 499 + const bool use_ww_ctx, const bool waiter) 528 500 { 529 501 return false; 530 502 } ··· 630 600 mutex_acquire_nest(&lock->dep_map, subclass, 0, nest_lock, ip); 631 601 632 602 if (__mutex_trylock(lock, false) || 633 - mutex_optimistic_spin(lock, ww_ctx, use_ww_ctx)) { 603 + mutex_optimistic_spin(lock, ww_ctx, use_ww_ctx, false)) { 634 604 /* got the lock, yay! */ 635 605 lock_acquired(&lock->dep_map, ip); 636 606 if (use_ww_ctx) ··· 699 669 * state back to RUNNING and fall through the next schedule(), 700 670 * or we must see its unlock and acquire. 701 671 */ 702 - if (__mutex_trylock(lock, first)) 672 + if ((first && mutex_optimistic_spin(lock, ww_ctx, use_ww_ctx, true)) || 673 + __mutex_trylock(lock, first)) 703 674 break; 704 675 705 676 spin_lock_mutex(&lock->wait_lock, flags);