Skip to content

Commit ba81043

Browse files
jhovoldmartinkpetersen
authored andcommitted
scsi: ufs: core: Fix devfreq deadlocks
There is a lock inversion and rwsem read-lock recursion in the devfreq target callback which can lead to deadlocks. Specifically, ufshcd_devfreq_scale() already holds a clk_scaling_lock read lock when toggling the write booster, which involves taking the dev_cmd mutex before taking another clk_scaling_lock read lock. This can lead to a deadlock if another thread: 1) tries to acquire the dev_cmd and clk_scaling locks in the correct order, or 2) takes a clk_scaling write lock before the attempt to take the clk_scaling read lock a second time. Fix this by dropping the clk_scaling_lock before toggling the write booster as was done before commit 0e9d4ca ("scsi: ufs: Protect some contexts from unexpected clock scaling"). While the devfreq callbacks are already serialised, add a second serialising mutex to handle the unlikely case where a callback triggered through the devfreq sysfs interface is racing with a request to disable clock scaling through the UFS controller 'clkscale_enable' sysfs attribute. This could otherwise lead to the write booster being left disabled after having disabled clock scaling. Also take the new mutex in ufshcd_clk_scaling_allow() to make sure that any pending write booster update has completed on return. Note that this currently only affects Qualcomm platforms since commit 87bd050 ("scsi: ufs: core: Allow host driver to disable wb toggling during clock scaling"). The lock inversion (i.e. 1 above) was reported by lockdep as: ====================================================== WARNING: possible circular locking dependency detected 6.1.0-next-20221216 #211 Not tainted ------------------------------------------------------ kworker/u16:2/71 is trying to acquire lock: ffff076280ba98a0 (&hba->dev_cmd.lock){+.+.}-{3:3}, at: ufshcd_query_flag+0x50/0x1c0 but task is already holding lock: ffff076280ba9cf0 (&hba->clk_scaling_lock){++++}-{3:3}, at: ufshcd_devfreq_scale+0x2b8/0x380 which lock already depends on the new lock. [ +0.011606] the existing dependency chain (in reverse order) is: -> #1 (&hba->clk_scaling_lock){++++}-{3:3}: lock_acquire+0x68/0x90 down_read+0x58/0x80 ufshcd_exec_dev_cmd+0x70/0x2c0 ufshcd_verify_dev_init+0x68/0x170 ufshcd_probe_hba+0x398/0x1180 ufshcd_async_scan+0x30/0x320 async_run_entry_fn+0x34/0x150 process_one_work+0x288/0x6c0 worker_thread+0x74/0x450 kthread+0x118/0x120 ret_from_fork+0x10/0x20 -> #0 (&hba->dev_cmd.lock){+.+.}-{3:3}: __lock_acquire+0x12a0/0x2240 lock_acquire.part.0+0xcc/0x220 lock_acquire+0x68/0x90 __mutex_lock+0x98/0x430 mutex_lock_nested+0x2c/0x40 ufshcd_query_flag+0x50/0x1c0 ufshcd_query_flag_retry+0x64/0x100 ufshcd_wb_toggle+0x5c/0x120 ufshcd_devfreq_scale+0x2c4/0x380 ufshcd_devfreq_target+0xf4/0x230 devfreq_set_target+0x84/0x2f0 devfreq_update_target+0xc4/0xf0 devfreq_monitor+0x38/0x1f0 process_one_work+0x288/0x6c0 worker_thread+0x74/0x450 kthread+0x118/0x120 ret_from_fork+0x10/0x20 other info that might help us debug this: Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(&hba->clk_scaling_lock); lock(&hba->dev_cmd.lock); lock(&hba->clk_scaling_lock); lock(&hba->dev_cmd.lock); *** DEADLOCK *** Fixes: 0e9d4ca ("scsi: ufs: Protect some contexts from unexpected clock scaling") Cc: [email protected] # 5.12 Cc: Can Guo <[email protected]> Tested-by: Andrew Halaney <[email protected]> Signed-off-by: Johan Hovold <[email protected]> Reviewed-by: Bart Van Assche <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Martin K. Petersen <[email protected]>
1 parent bbbd254 commit ba81043

File tree

2 files changed

+17
-14
lines changed

2 files changed

+17
-14
lines changed

drivers/ufs/core/ufshcd.c

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,12 +1234,14 @@ static int ufshcd_clock_scaling_prepare(struct ufs_hba *hba)
12341234
* clock scaling is in progress
12351235
*/
12361236
ufshcd_scsi_block_requests(hba);
1237+
mutex_lock(&hba->wb_mutex);
12371238
down_write(&hba->clk_scaling_lock);
12381239

12391240
if (!hba->clk_scaling.is_allowed ||
12401241
ufshcd_wait_for_doorbell_clr(hba, DOORBELL_CLR_TOUT_US)) {
12411242
ret = -EBUSY;
12421243
up_write(&hba->clk_scaling_lock);
1244+
mutex_unlock(&hba->wb_mutex);
12431245
ufshcd_scsi_unblock_requests(hba);
12441246
goto out;
12451247
}
@@ -1251,12 +1253,16 @@ static int ufshcd_clock_scaling_prepare(struct ufs_hba *hba)
12511253
return ret;
12521254
}
12531255

1254-
static void ufshcd_clock_scaling_unprepare(struct ufs_hba *hba, bool writelock)
1256+
static void ufshcd_clock_scaling_unprepare(struct ufs_hba *hba, int err, bool scale_up)
12551257
{
1256-
if (writelock)
1257-
up_write(&hba->clk_scaling_lock);
1258-
else
1259-
up_read(&hba->clk_scaling_lock);
1258+
up_write(&hba->clk_scaling_lock);
1259+
1260+
/* Enable Write Booster if we have scaled up else disable it */
1261+
if (ufshcd_enable_wb_if_scaling_up(hba) && !err)
1262+
ufshcd_wb_toggle(hba, scale_up);
1263+
1264+
mutex_unlock(&hba->wb_mutex);
1265+
12601266
ufshcd_scsi_unblock_requests(hba);
12611267
ufshcd_release(hba);
12621268
}
@@ -1273,7 +1279,6 @@ static void ufshcd_clock_scaling_unprepare(struct ufs_hba *hba, bool writelock)
12731279
static int ufshcd_devfreq_scale(struct ufs_hba *hba, bool scale_up)
12741280
{
12751281
int ret = 0;
1276-
bool is_writelock = true;
12771282

12781283
ret = ufshcd_clock_scaling_prepare(hba);
12791284
if (ret)
@@ -1302,15 +1307,8 @@ static int ufshcd_devfreq_scale(struct ufs_hba *hba, bool scale_up)
13021307
}
13031308
}
13041309

1305-
/* Enable Write Booster if we have scaled up else disable it */
1306-
if (ufshcd_enable_wb_if_scaling_up(hba)) {
1307-
downgrade_write(&hba->clk_scaling_lock);
1308-
is_writelock = false;
1309-
ufshcd_wb_toggle(hba, scale_up);
1310-
}
1311-
13121310
out_unprepare:
1313-
ufshcd_clock_scaling_unprepare(hba, is_writelock);
1311+
ufshcd_clock_scaling_unprepare(hba, ret, scale_up);
13141312
return ret;
13151313
}
13161314

@@ -6066,9 +6064,11 @@ static void ufshcd_force_error_recovery(struct ufs_hba *hba)
60666064

60676065
static void ufshcd_clk_scaling_allow(struct ufs_hba *hba, bool allow)
60686066
{
6067+
mutex_lock(&hba->wb_mutex);
60696068
down_write(&hba->clk_scaling_lock);
60706069
hba->clk_scaling.is_allowed = allow;
60716070
up_write(&hba->clk_scaling_lock);
6071+
mutex_unlock(&hba->wb_mutex);
60726072
}
60736073

60746074
static void ufshcd_clk_scaling_suspend(struct ufs_hba *hba, bool suspend)
@@ -9793,6 +9793,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
97939793
/* Initialize mutex for exception event control */
97949794
mutex_init(&hba->ee_ctrl_mutex);
97959795

9796+
mutex_init(&hba->wb_mutex);
97969797
init_rwsem(&hba->clk_scaling_lock);
97979798

97989799
ufshcd_init_clk_gating(hba);

include/ufs/ufshcd.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,7 @@ struct ufs_hba_monitor {
808808
* @urgent_bkops_lvl: keeps track of urgent bkops level for device
809809
* @is_urgent_bkops_lvl_checked: keeps track if the urgent bkops level for
810810
* device is known or not.
811+
* @wb_mutex: used to serialize devfreq and sysfs write booster toggling
811812
* @clk_scaling_lock: used to serialize device commands and clock scaling
812813
* @desc_size: descriptor sizes reported by device
813814
* @scsi_block_reqs_cnt: reference counting for scsi block requests
@@ -951,6 +952,7 @@ struct ufs_hba {
951952
enum bkops_status urgent_bkops_lvl;
952953
bool is_urgent_bkops_lvl_checked;
953954

955+
struct mutex wb_mutex;
954956
struct rw_semaphore clk_scaling_lock;
955957
unsigned char desc_size[QUERY_DESC_IDN_MAX];
956958
atomic_t scsi_block_reqs_cnt;

0 commit comments

Comments
 (0)