Skip to content

Commit d2a8cfc

Browse files
diandersandersson
authored andcommitted
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 <[email protected]> Reviewed-by: Stephen Boyd <[email protected]> Link: https://lore.kernel.org/r/20200504104917.v6.5.I295cb72bc5334a2af80313cbe97cb5c9dcb1442c@changeid Signed-off-by: Bjorn Andersson <[email protected]>
1 parent 555701a commit d2a8cfc

File tree

3 files changed

+67
-44
lines changed

3 files changed

+67
-44
lines changed

drivers/soc/qcom/rpmh-internal.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,17 +95,17 @@ struct rpmh_ctrlr {
9595
* @num_tcs: Number of TCSes in this DRV.
9696
* @rsc_pm: CPU PM notifier for controller.
9797
* 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.
9999
* Used when solver mode is not present.
100100
* @tcs: TCS groups.
101101
* @tcs_in_use: S/W state of the TCS; only set for ACTIVE_ONLY
102102
* transfers, but might show a sleep/wake TCS in use if
103103
* it was borrowed for an active_only transfer. You
104104
* must hold the lock in this struct (AKA drv->lock) in
105105
* 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.
109109
* @client: Handle to the DRV's client.
110110
*/
111111
struct rsc_drv {
@@ -114,11 +114,10 @@ struct rsc_drv {
114114
int id;
115115
int num_tcs;
116116
struct notifier_block rsc_pm;
117-
struct cpumask cpus_entered_pm;
117+
atomic_t cpus_in_pm;
118118
struct tcs_group tcs[TCS_TYPE_NR];
119119
DECLARE_BITMAP(tcs_in_use, MAX_TCS_NR);
120120
spinlock_t lock;
121-
spinlock_t pm_lock;
122121
struct rpmh_ctrlr client;
123122
};
124123

drivers/soc/qcom/rpmh-rsc.c

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,8 @@ int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg)
750750
* SLEEP and WAKE sets. If AMCs are busy, controller can not enter
751751
* power collapse, so deny from the last cpu's pm notification.
752752
*
753+
* Context: Must be called with the drv->lock held.
754+
*
753755
* Return:
754756
* * False - AMCs are idle
755757
* * True - AMCs are busy
@@ -764,9 +766,6 @@ static bool rpmh_rsc_ctrlr_is_busy(struct rsc_drv *drv)
764766
* dedicated TCS for active state use, then re-purposed wake TCSes
765767
* should be checked for not busy, because we used wake TCSes for
766768
* active requests in this case.
767-
*
768-
* Since this is called from the last cpu, need not take drv->lock
769-
* before checking tcs_is_free().
770769
*/
771770
if (!tcs->num_tcs)
772771
tcs = &drv->tcs[WAKE_TCS];
@@ -801,43 +800,62 @@ static int rpmh_rsc_cpu_pm_callback(struct notifier_block *nfb,
801800
{
802801
struct rsc_drv *drv = container_of(nfb, struct rsc_drv, rsc_pm);
803802
int ret = NOTIFY_OK;
804-
805-
spin_lock(&drv->pm_lock);
803+
int cpus_in_pm;
806804

807805
switch (action) {
808806
case CPU_PM_ENTER:
809-
cpumask_set_cpu(smp_processor_id(), &drv->cpus_entered_pm);
810-
811-
if (!cpumask_equal(&drv->cpus_entered_pm, cpu_online_mask))
812-
goto exit;
807+
cpus_in_pm = atomic_inc_return(&drv->cpus_in_pm);
808+
/*
809+
* NOTE: comments for num_online_cpus() point out that it's
810+
* only a snapshot so we need to be careful. It should be OK
811+
* for us to use, though. It's important for us not to miss
812+
* if we're the last CPU going down so it would only be a
813+
* problem if a CPU went offline right after we did the check
814+
* AND that CPU was not idle AND that CPU was the last non-idle
815+
* CPU. That can't happen. CPUs would have to come out of idle
816+
* before the CPU could go offline.
817+
*/
818+
if (cpus_in_pm < num_online_cpus())
819+
return NOTIFY_OK;
813820
break;
814821
case CPU_PM_ENTER_FAILED:
815822
case CPU_PM_EXIT:
816-
cpumask_clear_cpu(smp_processor_id(), &drv->cpus_entered_pm);
817-
goto exit;
823+
atomic_dec(&drv->cpus_in_pm);
824+
return NOTIFY_OK;
818825
default:
819-
ret = NOTIFY_DONE;
820-
goto exit;
826+
return NOTIFY_DONE;
821827
}
822828

823-
ret = rpmh_rsc_ctrlr_is_busy(drv);
824-
if (ret) {
825-
ret = NOTIFY_BAD;
826-
goto exit;
829+
/*
830+
* It's likely we're on the last CPU. Grab the drv->lock and write
831+
* out the sleep/wake commands to RPMH hardware. Grabbing the lock
832+
* means that if we race with another CPU coming up we are still
833+
* guaranteed to be safe. If another CPU came up just after we checked
834+
* and has grabbed the lock or started an active transfer then we'll
835+
* notice we're busy and abort. If another CPU comes up after we start
836+
* flushing it will be blocked from starting an active transfer until
837+
* we're done flushing. If another CPU starts an active transfer after
838+
* we release the lock we're still OK because we're no longer the last
839+
* CPU.
840+
*/
841+
if (spin_trylock(&drv->lock)) {
842+
if (rpmh_rsc_ctrlr_is_busy(drv) || rpmh_flush(&drv->client))
843+
ret = NOTIFY_BAD;
844+
spin_unlock(&drv->lock);
845+
} else {
846+
/* Another CPU must be up */
847+
return NOTIFY_OK;
827848
}
828849

829-
ret = rpmh_flush(&drv->client);
830-
if (ret)
831-
ret = NOTIFY_BAD;
832-
else
833-
ret = NOTIFY_OK;
834-
835-
exit:
836-
if (ret == NOTIFY_BAD)
837-
/* We won't be called w/ CPU_PM_ENTER_FAILED */
838-
cpumask_clear_cpu(smp_processor_id(), &drv->cpus_entered_pm);
850+
if (ret == NOTIFY_BAD) {
851+
/* Double-check if we're here because someone else is up */
852+
if (cpus_in_pm < num_online_cpus())
853+
ret = NOTIFY_OK;
854+
else
855+
/* We won't be called w/ CPU_PM_ENTER_FAILED */
856+
atomic_dec(&drv->cpus_in_pm);
857+
}
839858

840-
spin_unlock(&drv->pm_lock);
841859
return ret;
842860
}
843861

@@ -980,7 +998,6 @@ static int rpmh_rsc_probe(struct platform_device *pdev)
980998
solver_config = solver_config >> DRV_HW_SOLVER_SHIFT;
981999
if (!solver_config) {
9821000
drv->rsc_pm.notifier_call = rpmh_rsc_cpu_pm_callback;
983-
spin_lock_init(&drv->pm_lock);
9841001
cpu_pm_register_notifier(&drv->rsc_pm);
9851002
}
9861003

drivers/soc/qcom/rpmh.c

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -435,23 +435,28 @@ static int send_single(struct rpmh_ctrlr *ctrlr, enum rpmh_state state,
435435
*
436436
* @ctrlr: Controller making request to flush cached data
437437
*
438-
* This function is called from sleep code on the last CPU
439-
* (thus no spinlock needed).
440-
*
441438
* Return:
442439
* * 0 - Success
443440
* * Error code - Otherwise
444441
*/
445442
int rpmh_flush(struct rpmh_ctrlr *ctrlr)
446443
{
447444
struct cache_req *p;
448-
int ret;
445+
int ret = 0;
449446

450447
lockdep_assert_irqs_disabled();
451448

449+
/*
450+
* Currently rpmh_flush() is only called when we think we're running
451+
* on the last processor. If the lock is busy it means another
452+
* processor is up and it's better to abort than spin.
453+
*/
454+
if (!spin_trylock(&ctrlr->cache_lock))
455+
return -EBUSY;
456+
452457
if (!ctrlr->dirty) {
453458
pr_debug("Skipping flush, TCS has latest data.\n");
454-
return 0;
459+
goto exit;
455460
}
456461

457462
/* Invalidate the TCSes first to avoid stale data */
@@ -460,7 +465,7 @@ int rpmh_flush(struct rpmh_ctrlr *ctrlr)
460465
/* First flush the cached batch requests */
461466
ret = flush_batch(ctrlr);
462467
if (ret)
463-
return ret;
468+
goto exit;
464469

465470
list_for_each_entry(p, &ctrlr->cache, list) {
466471
if (!is_req_valid(p)) {
@@ -471,16 +476,18 @@ int rpmh_flush(struct rpmh_ctrlr *ctrlr)
471476
ret = send_single(ctrlr, RPMH_SLEEP_STATE, p->addr,
472477
p->sleep_val);
473478
if (ret)
474-
return ret;
479+
goto exit;
475480
ret = send_single(ctrlr, RPMH_WAKE_ONLY_STATE, p->addr,
476481
p->wake_val);
477482
if (ret)
478-
return ret;
483+
goto exit;
479484
}
480485

481486
ctrlr->dirty = false;
482487

483-
return 0;
488+
exit:
489+
spin_unlock(&ctrlr->cache_lock);
490+
return ret;
484491
}
485492

486493
/**

0 commit comments

Comments
 (0)