mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 15:22:21 -04:00
scsi: ufs: core: Avoid possible memory reclaim deadlock in TX EQTR context
TX EQTR may run while devfreq gear scaling has quiesced the UFS
tagset. In that context, functions ufshcd_tx_eqtr(), __ufshcd_tx_eqtr()
and ufs_qcom_get_rx_fom() allocate memory with GFP_KERNEL. If direct
reclaim is triggered, reclaim/writeback can depend on I/O to UFS
device. Because the queue is quiesced, this can cause deadlock.
Use memalloc_noio_save/restore() in ufshcd_tx_eqtr() to cover all
allocations in the TX EQTR call tree, including:
- params->eqtr_record in ufshcd_tx_eqtr()
- eqtr_data in __ufshcd_tx_eqtr()
- params in ufs_qcom_get_rx_fom()
This is preferred over tagging individual call sites with GFP_NOIO, as it
automatically covers any future allocations added anywhere in the call tree
without requiring each caller to be aware of this constraint.
[mkp: fix label as suggested by Bart]
Fixes: 03e5d38e2f ("scsi: ufs: core: Add support for TX Equalization")
Closes: https://sashiko.dev/#/patchset/20260615132834.2985346-1-can.guo@oss.qualcomm.com?part=2
Signed-off-by: Can Guo <can.guo@oss.qualcomm.com>
Reviewed-by: Ziqi Chen <ziqi.chen@oss.qualcomm.com>
Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
Link: https://patch.msgid.link/20260618140941.902000-1-can.guo@oss.qualcomm.com
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
committed by
Martin K. Petersen
parent
e700a7c94f
commit
760fc6f0e2
@@ -10,6 +10,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched/mm.h>
|
||||
#include <ufs/ufshcd.h>
|
||||
#include <ufs/unipro.h>
|
||||
#include "ufshcd-priv.h"
|
||||
@@ -1216,14 +1217,25 @@ static int ufshcd_tx_eqtr(struct ufs_hba *hba,
|
||||
struct ufs_pa_layer_attr *pwr_mode)
|
||||
{
|
||||
struct ufs_pa_layer_attr old_pwr_info;
|
||||
unsigned int noio_flag;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* ufshcd_tx_eqtr() is called from a power-mode-change context where
|
||||
* I/O is suspended. Use memalloc_noio_save() to propagate GFP_NOIO
|
||||
* to all allocations in the call tree instead of tagging each call
|
||||
* site individually.
|
||||
*/
|
||||
noio_flag = memalloc_noio_save();
|
||||
|
||||
if (!params->eqtr_record) {
|
||||
params->eqtr_record = devm_kzalloc(hba->dev,
|
||||
sizeof(*params->eqtr_record),
|
||||
GFP_KERNEL);
|
||||
if (!params->eqtr_record)
|
||||
return -ENOMEM;
|
||||
if (!params->eqtr_record) {
|
||||
ret = -ENOMEM;
|
||||
goto out_noio_restore;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(&old_pwr_info, &hba->pwr_info, sizeof(struct ufs_pa_layer_attr));
|
||||
@@ -1231,23 +1243,26 @@ static int ufshcd_tx_eqtr(struct ufs_hba *hba,
|
||||
ret = ufshcd_tx_eqtr_prepare(hba, pwr_mode);
|
||||
if (ret) {
|
||||
dev_err(hba->dev, "Failed to prepare TX EQTR: %d\n", ret);
|
||||
goto out;
|
||||
goto out_unprepare;
|
||||
}
|
||||
|
||||
ret = ufshcd_vops_tx_eqtr_notify(hba, PRE_CHANGE, pwr_mode);
|
||||
if (ret)
|
||||
goto out;
|
||||
goto out_unprepare;
|
||||
|
||||
ret = __ufshcd_tx_eqtr(hba, params, pwr_mode);
|
||||
if (ret)
|
||||
goto out;
|
||||
goto out_unprepare;
|
||||
|
||||
ret = ufshcd_vops_tx_eqtr_notify(hba, POST_CHANGE, pwr_mode);
|
||||
|
||||
out:
|
||||
out_unprepare:
|
||||
if (ret)
|
||||
ufshcd_tx_eqtr_unprepare(hba, &old_pwr_info);
|
||||
|
||||
out_noio_restore:
|
||||
memalloc_noio_restore(noio_flag);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user