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

soc: qcom: rpmh-rsc: Remove the pm_lock

It has been postulated that the pm_lock is bad for performance because
a CPU currently running rpmh_flush() could block other CPUs from
coming out of idle. Similarly CPUs coming out of / going into idle
all need to contend with each other for the spinlock just to update
the variable tracking who's in PM.

Let's optimize this a bit. Specifically:

- Use a count rather than a bitmask. This is faster to access and
also means we can use the atomic_inc_return() function to really
detect who the last one to enter PM was.
- Accept that it's OK if we race and are doing the flush (because we
think we're last) while another CPU is coming out of idle. As long
as we block that CPU if/when it tries to do an active-only transfer
we're OK.

Signed-off-by: Douglas Anderson <dianders@chromium.org>
Reviewed-by: Stephen Boyd <swboyd@chromium.org>
Link: https://lore.kernel.org/r/20200504104917.v6.5.I295cb72bc5334a2af80313cbe97cb5c9dcb1442c@changeid
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>

authored by

Douglas Anderson and committed by
Bjorn Andersson
d2a8cfc6 555701a4

+67 -44
+5 -6
drivers/soc/qcom/rpmh-internal.h
··· 95 95 * @num_tcs: Number of TCSes in this DRV. 96 96 * @rsc_pm: CPU PM notifier for controller. 97 97 * Used when solver mode is not present. 98 - * @cpus_entered_pm: CPU mask for cpus in idle power collapse. 98 + * @cpus_in_pm: Number of CPUs not in idle power collapse. 99 99 * Used when solver mode is not present. 100 100 * @tcs: TCS groups. 101 101 * @tcs_in_use: S/W state of the TCS; only set for ACTIVE_ONLY ··· 103 103 * it was borrowed for an active_only transfer. You 104 104 * must hold the lock in this struct (AKA drv->lock) in 105 105 * order to update this. 106 - * @lock: Synchronize state of the controller. 107 - * @pm_lock: Synchronize during PM notifications. 108 - * Used when solver mode is not present. 106 + * @lock: Synchronize state of the controller. If RPMH's cache 107 + * lock will also be held, the order is: drv->lock then 108 + * cache_lock. 109 109 * @client: Handle to the DRV's client. 110 110 */ 111 111 struct rsc_drv { ··· 114 114 int id; 115 115 int num_tcs; 116 116 struct notifier_block rsc_pm; 117 - struct cpumask cpus_entered_pm; 117 + atomic_t cpus_in_pm; 118 118 struct tcs_group tcs[TCS_TYPE_NR]; 119 119 DECLARE_BITMAP(tcs_in_use, MAX_TCS_NR); 120 120 spinlock_t lock; 121 - spinlock_t pm_lock; 122 121 struct rpmh_ctrlr client; 123 122 }; 124 123
+46 -29
drivers/soc/qcom/rpmh-rsc.c
··· 750 750 * SLEEP and WAKE sets. If AMCs are busy, controller can not enter 751 751 * power collapse, so deny from the last cpu's pm notification. 752 752 * 753 + * Context: Must be called with the drv->lock held. 754 + * 753 755 * Return: 754 756 * * False - AMCs are idle 755 757 * * True - AMCs are busy ··· 766 764 * dedicated TCS for active state use, then re-purposed wake TCSes 767 765 * should be checked for not busy, because we used wake TCSes for 768 766 * active requests in this case. 769 - * 770 - * Since this is called from the last cpu, need not take drv->lock 771 - * before checking tcs_is_free(). 772 767 */ 773 768 if (!tcs->num_tcs) 774 769 tcs = &drv->tcs[WAKE_TCS]; ··· 800 801 { 801 802 struct rsc_drv *drv = container_of(nfb, struct rsc_drv, rsc_pm); 802 803 int ret = NOTIFY_OK; 803 - 804 - spin_lock(&drv->pm_lock); 804 + int cpus_in_pm; 805 805 806 806 switch (action) { 807 807 case CPU_PM_ENTER: 808 - cpumask_set_cpu(smp_processor_id(), &drv->cpus_entered_pm); 809 - 810 - if (!cpumask_equal(&drv->cpus_entered_pm, cpu_online_mask)) 811 - goto exit; 808 + cpus_in_pm = atomic_inc_return(&drv->cpus_in_pm); 809 + /* 810 + * NOTE: comments for num_online_cpus() point out that it's 811 + * only a snapshot so we need to be careful. It should be OK 812 + * for us to use, though. It's important for us not to miss 813 + * if we're the last CPU going down so it would only be a 814 + * problem if a CPU went offline right after we did the check 815 + * AND that CPU was not idle AND that CPU was the last non-idle 816 + * CPU. That can't happen. CPUs would have to come out of idle 817 + * before the CPU could go offline. 818 + */ 819 + if (cpus_in_pm < num_online_cpus()) 820 + return NOTIFY_OK; 812 821 break; 813 822 case CPU_PM_ENTER_FAILED: 814 823 case CPU_PM_EXIT: 815 - cpumask_clear_cpu(smp_processor_id(), &drv->cpus_entered_pm); 816 - goto exit; 824 + atomic_dec(&drv->cpus_in_pm); 825 + return NOTIFY_OK; 817 826 default: 818 - ret = NOTIFY_DONE; 819 - goto exit; 827 + return NOTIFY_DONE; 820 828 } 821 829 822 - ret = rpmh_rsc_ctrlr_is_busy(drv); 823 - if (ret) { 824 - ret = NOTIFY_BAD; 825 - goto exit; 830 + /* 831 + * It's likely we're on the last CPU. Grab the drv->lock and write 832 + * out the sleep/wake commands to RPMH hardware. Grabbing the lock 833 + * means that if we race with another CPU coming up we are still 834 + * guaranteed to be safe. If another CPU came up just after we checked 835 + * and has grabbed the lock or started an active transfer then we'll 836 + * notice we're busy and abort. If another CPU comes up after we start 837 + * flushing it will be blocked from starting an active transfer until 838 + * we're done flushing. If another CPU starts an active transfer after 839 + * we release the lock we're still OK because we're no longer the last 840 + * CPU. 841 + */ 842 + if (spin_trylock(&drv->lock)) { 843 + if (rpmh_rsc_ctrlr_is_busy(drv) || rpmh_flush(&drv->client)) 844 + ret = NOTIFY_BAD; 845 + spin_unlock(&drv->lock); 846 + } else { 847 + /* Another CPU must be up */ 848 + return NOTIFY_OK; 826 849 } 827 850 828 - ret = rpmh_flush(&drv->client); 829 - if (ret) 830 - ret = NOTIFY_BAD; 831 - else 832 - ret = NOTIFY_OK; 851 + if (ret == NOTIFY_BAD) { 852 + /* Double-check if we're here because someone else is up */ 853 + if (cpus_in_pm < num_online_cpus()) 854 + ret = NOTIFY_OK; 855 + else 856 + /* We won't be called w/ CPU_PM_ENTER_FAILED */ 857 + atomic_dec(&drv->cpus_in_pm); 858 + } 833 859 834 - exit: 835 - if (ret == NOTIFY_BAD) 836 - /* We won't be called w/ CPU_PM_ENTER_FAILED */ 837 - cpumask_clear_cpu(smp_processor_id(), &drv->cpus_entered_pm); 838 - 839 - spin_unlock(&drv->pm_lock); 840 860 return ret; 841 861 } 842 862 ··· 998 980 solver_config = solver_config >> DRV_HW_SOLVER_SHIFT; 999 981 if (!solver_config) { 1000 982 drv->rsc_pm.notifier_call = rpmh_rsc_cpu_pm_callback; 1001 - spin_lock_init(&drv->pm_lock); 1002 983 cpu_pm_register_notifier(&drv->rsc_pm); 1003 984 } 1004 985
+16 -9
drivers/soc/qcom/rpmh.c
··· 435 435 * 436 436 * @ctrlr: Controller making request to flush cached data 437 437 * 438 - * This function is called from sleep code on the last CPU 439 - * (thus no spinlock needed). 440 - * 441 438 * Return: 442 439 * * 0 - Success 443 440 * * Error code - Otherwise ··· 442 445 int rpmh_flush(struct rpmh_ctrlr *ctrlr) 443 446 { 444 447 struct cache_req *p; 445 - int ret; 448 + int ret = 0; 446 449 447 450 lockdep_assert_irqs_disabled(); 448 451 452 + /* 453 + * Currently rpmh_flush() is only called when we think we're running 454 + * on the last processor. If the lock is busy it means another 455 + * processor is up and it's better to abort than spin. 456 + */ 457 + if (!spin_trylock(&ctrlr->cache_lock)) 458 + return -EBUSY; 459 + 449 460 if (!ctrlr->dirty) { 450 461 pr_debug("Skipping flush, TCS has latest data.\n"); 451 - return 0; 462 + goto exit; 452 463 } 453 464 454 465 /* Invalidate the TCSes first to avoid stale data */ ··· 465 460 /* First flush the cached batch requests */ 466 461 ret = flush_batch(ctrlr); 467 462 if (ret) 468 - return ret; 463 + goto exit; 469 464 470 465 list_for_each_entry(p, &ctrlr->cache, list) { 471 466 if (!is_req_valid(p)) { ··· 476 471 ret = send_single(ctrlr, RPMH_SLEEP_STATE, p->addr, 477 472 p->sleep_val); 478 473 if (ret) 479 - return ret; 474 + goto exit; 480 475 ret = send_single(ctrlr, RPMH_WAKE_ONLY_STATE, p->addr, 481 476 p->wake_val); 482 477 if (ret) 483 - return ret; 478 + goto exit; 484 479 } 485 480 486 481 ctrlr->dirty = false; 487 482 488 - return 0; 483 + exit: 484 + spin_unlock(&ctrlr->cache_lock); 485 + return ret; 489 486 } 490 487 491 488 /**