Skip to content

Commit 555701a

Browse files
diandersandersson
authored andcommitted
soc: qcom: rpmh-rsc: Simplify locking by eliminating the per-TCS lock
The rpmh-rsc code had both a driver-level lock (sometimes referred to in comments as drv->lock) and a lock per-TCS. The idea was supposed to be that there would be times where you could get by with just locking a TCS lock and therefor other RPMH users wouldn't be blocked. The above didn't work out so well. Looking at tcs_write() the bigger drv->lock was held for most of the function anyway. Only the __tcs_buffer_write() and __tcs_set_trigger() calls were called without holding the drv->lock. It actually turns out that in tcs_write() we don't need to hold the drv->lock for those function calls anyway even if the per-TCS lock isn't there anymore. From the newly added comments in the code, this is because: - We marked "tcs_in_use" under lock. - Once "tcs_in_use" has been marked nobody else could be writing to these registers until the interrupt goes off. - The interrupt can't go off until we trigger w/ the last line of __tcs_set_trigger(). Thus, from a tcs_write() point of view, the per-TCS lock was useless. Looking at rpmh_rsc_write_ctrl_data(), only the per-TCS lock was held. It turns out, though, that this function already needs to be called with the equivalent of the drv->lock held anyway (we either need to hold drv->lock as we will in a future patch or we need to know no other CPUs could be running as happens today). Specifically rpmh_rsc_write_ctrl_data() might be writing to a TCS that has been borrowed for writing an active transation but it never checks this. Let's eliminate this extra overhead and avoid possible AB BA locking headaches. Suggested-by: Maulik Shah <[email protected]> Signed-off-by: Douglas Anderson <[email protected]> Reviewed-by: Stephen Boyd <[email protected]> Link: https://lore.kernel.org/r/20200504104917.v6.4.Ib8dccfdb10bf6b1fb1d600ca1c21d9c0db1ef746@changeid Signed-off-by: Bjorn Andersson <[email protected]>
1 parent b594521 commit 555701a

File tree

2 files changed

+28
-40
lines changed

2 files changed

+28
-40
lines changed

drivers/soc/qcom/rpmh-internal.h

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ struct rsc_drv;
2828
* @offset: Start of the TCS group relative to the TCSes in the RSC.
2929
* @num_tcs: Number of TCSes in this type.
3030
* @ncpt: Number of commands in each TCS.
31-
* @lock: Lock for synchronizing this TCS writes.
3231
* @req: Requests that are sent from the TCS; only used for ACTIVE_ONLY
3332
* transfers (could be on a wake/sleep TCS if we are borrowing for
3433
* an ACTIVE_ONLY transfer).
@@ -48,7 +47,6 @@ struct tcs_group {
4847
u32 offset;
4948
int num_tcs;
5049
int ncpt;
51-
spinlock_t lock;
5250
const struct tcs_request *req[MAX_TCS_PER_TYPE];
5351
DECLARE_BITMAP(slots, MAX_TCS_SLOTS);
5452
};
@@ -103,14 +101,9 @@ struct rpmh_ctrlr {
103101
* @tcs_in_use: S/W state of the TCS; only set for ACTIVE_ONLY
104102
* transfers, but might show a sleep/wake TCS in use if
105103
* it was borrowed for an active_only transfer. You
106-
* must hold both the lock in this struct and the
107-
* tcs_lock for the TCS in order to mark a TCS as
108-
* in-use, but you only need the lock in this structure
109-
* (aka the drv->lock) to mark one freed.
110-
* @lock: Synchronize state of the controller. If you will be
111-
* grabbing this lock and a tcs_lock at the same time,
112-
* grab the tcs_lock first so we always have a
113-
* consistent lock ordering.
104+
* must hold the lock in this struct (AKA drv->lock) in
105+
* order to update this.
106+
* @lock: Synchronize state of the controller.
114107
* @pm_lock: Synchronize during PM notifications.
115108
* Used when solver mode is not present.
116109
* @client: Handle to the DRV's client.

drivers/soc/qcom/rpmh-rsc.c

Lines changed: 25 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -192,11 +192,7 @@ static void write_tcs_reg_sync(const struct rsc_drv *drv, int reg, int tcs_id,
192192
*
193193
* Returns true if nobody has claimed this TCS (by setting tcs_in_use).
194194
*
195-
* Context: Must be called with the drv->lock held or the tcs_lock for the TCS
196-
* being tested. If only the tcs_lock is held then it is possible that
197-
* this function will return that a tcs is still busy when it has been
198-
* recently been freed but it will never return free when a TCS is
199-
* actually in use.
195+
* Context: Must be called with the drv->lock held.
200196
*
201197
* Return: true if the given TCS is free.
202198
*/
@@ -255,8 +251,6 @@ void rpmh_rsc_invalidate(struct rsc_drv *drv)
255251
* This is normally pretty straightforward except if we are trying to send
256252
* an ACTIVE_ONLY message but don't have any active_only TCSes.
257253
*
258-
* Called without drv->lock held and with no tcs_lock locks held.
259-
*
260254
* Return: A pointer to a tcs_group or an ERR_PTR.
261255
*/
262256
static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv,
@@ -594,24 +588,19 @@ static int tcs_write(struct rsc_drv *drv, const struct tcs_request *msg)
594588
if (IS_ERR(tcs))
595589
return PTR_ERR(tcs);
596590

597-
spin_lock_irqsave(&tcs->lock, flags);
598-
spin_lock(&drv->lock);
591+
spin_lock_irqsave(&drv->lock, flags);
599592
/*
600593
* The h/w does not like if we send a request to the same address,
601594
* when one is already in-flight or being processed.
602595
*/
603596
ret = check_for_req_inflight(drv, tcs, msg);
604-
if (ret) {
605-
spin_unlock(&drv->lock);
606-
goto done_write;
607-
}
597+
if (ret)
598+
goto unlock;
608599

609-
tcs_id = find_free_tcs(tcs);
610-
if (tcs_id < 0) {
611-
ret = tcs_id;
612-
spin_unlock(&drv->lock);
613-
goto done_write;
614-
}
600+
ret = find_free_tcs(tcs);
601+
if (ret < 0)
602+
goto unlock;
603+
tcs_id = ret;
615604

616605
tcs->req[tcs_id - tcs->offset] = msg;
617606
set_bit(tcs_id, drv->tcs_in_use);
@@ -625,13 +614,22 @@ static int tcs_write(struct rsc_drv *drv, const struct tcs_request *msg)
625614
write_tcs_reg_sync(drv, RSC_DRV_CMD_WAIT_FOR_CMPL, tcs_id, 0);
626615
enable_tcs_irq(drv, tcs_id, true);
627616
}
628-
spin_unlock(&drv->lock);
617+
spin_unlock_irqrestore(&drv->lock, flags);
629618

619+
/*
620+
* These two can be done after the lock is released because:
621+
* - We marked "tcs_in_use" under lock.
622+
* - Once "tcs_in_use" has been marked nobody else could be writing
623+
* to these registers until the interrupt goes off.
624+
* - The interrupt can't go off until we trigger w/ the last line
625+
* of __tcs_set_trigger() below.
626+
*/
630627
__tcs_buffer_write(drv, tcs_id, 0, msg);
631628
__tcs_set_trigger(drv, tcs_id, true);
632629

633-
done_write:
634-
spin_unlock_irqrestore(&tcs->lock, flags);
630+
return 0;
631+
unlock:
632+
spin_unlock_irqrestore(&drv->lock, flags);
635633
return ret;
636634
}
637635

@@ -686,8 +684,6 @@ int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg)
686684
* Only for use on sleep/wake TCSes since those are the only ones we maintain
687685
* tcs->slots for.
688686
*
689-
* Must be called with the tcs_lock for the group held.
690-
*
691687
* Return: -ENOMEM if there was no room, else 0.
692688
*/
693689
static int find_slots(struct tcs_group *tcs, const struct tcs_request *msg,
@@ -722,25 +718,25 @@ static int find_slots(struct tcs_group *tcs, const struct tcs_request *msg,
722718
* This should only be called for for sleep/wake state, never active-only
723719
* state.
724720
*
721+
* The caller must ensure that no other RPMH actions are happening and the
722+
* controller is idle when this function is called since it runs lockless.
723+
*
725724
* Return: 0 if no error; else -error.
726725
*/
727726
int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg)
728727
{
729728
struct tcs_group *tcs;
730729
int tcs_id = 0, cmd_id = 0;
731-
unsigned long flags;
732730
int ret;
733731

734732
tcs = get_tcs_for_msg(drv, msg);
735733
if (IS_ERR(tcs))
736734
return PTR_ERR(tcs);
737735

738-
spin_lock_irqsave(&tcs->lock, flags);
739736
/* find the TCS id and the command in the TCS to write to */
740737
ret = find_slots(tcs, msg, &tcs_id, &cmd_id);
741738
if (!ret)
742739
__tcs_buffer_write(drv, tcs_id, cmd_id, msg);
743-
spin_unlock_irqrestore(&tcs->lock, flags);
744740

745741
return ret;
746742
}
@@ -769,8 +765,8 @@ static bool rpmh_rsc_ctrlr_is_busy(struct rsc_drv *drv)
769765
* should be checked for not busy, because we used wake TCSes for
770766
* active requests in this case.
771767
*
772-
* Since this is called from the last cpu, need not take drv or tcs
773-
* lock before checking tcs_is_free().
768+
* Since this is called from the last cpu, need not take drv->lock
769+
* before checking tcs_is_free().
774770
*/
775771
if (!tcs->num_tcs)
776772
tcs = &drv->tcs[WAKE_TCS];
@@ -899,7 +895,6 @@ static int rpmh_probe_tcs_config(struct platform_device *pdev,
899895
tcs->type = tcs_cfg[i].type;
900896
tcs->num_tcs = tcs_cfg[i].n;
901897
tcs->ncpt = ncpt;
902-
spin_lock_init(&tcs->lock);
903898

904899
if (!tcs->num_tcs || tcs->type == CONTROL_TCS)
905900
continue;

0 commit comments

Comments
 (0)