SCSI misc on 20251214

The only core fix is in doc; all the others are in drivers, with the
 biggest impacts in libsas being the rollback on error handling and in
 ufs coming from a couple of error handling fixes, one causing a crash
 if it's activated before scanning and the other fixing W-LUN
 resumption.
 
 Signed-off-by: James E.J. Bottomley <James.Bottomley@HansenPartnership.com>
 -----BEGIN PGP SIGNATURE-----
 
 iLgEABMIAGAWIQTnYEDbdso9F2cI+arnQslM7pishQUCaT4RWBsUgAAAAAAEAA5t
 YW51MiwyLjUrMS4xMSwyLDImHGphbWVzLmJvdHRvbWxleUBoYW5zZW5wYXJ0bmVy
 c2hpcC5jb20ACgkQ50LJTO6YrIVfSgEA23oY0kPjLLkNj4KAjLoWVPmZ/5efKw49
 m6YUwfvzTS8BAIq4eFHVecwAM8FAe9NwuOKgO5d9u6epAy6wzBeP2jly
 =cpHq
 -----END PGP SIGNATURE-----

Merge tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi

Pull SCSI fixes from James Bottomley:
 "The only core fix is in doc; all the others are in drivers, with the
  biggest impacts in libsas being the rollback on error handling and in
  ufs coming from a couple of error handling fixes, one causing a crash
  if it's activated before scanning and the other fixing W-LUN
  resumption"

* tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi:
  scsi: ufs: qcom: Fix confusing cleanup.h syntax
  scsi: libsas: Add rollback handling when an error occurs
  scsi: device_handler: Return error pointer in scsi_dh_attached_handler_name()
  scsi: ufs: core: Fix a deadlock in the frequency scaling code
  scsi: ufs: core: Fix an error handler crash
  scsi: Revert "scsi: libsas: Fix exp-attached device scan after probe failure scanned in again after probe failed"
  scsi: ufs: core: Fix RPMB link error by reversing Kconfig dependencies
  scsi: qla4xxx: Use time conversion macros
  scsi: qla2xxx: Enable/disable IRQD_NO_BALANCING during reset
  scsi: ipr: Enable/disable IRQD_NO_BALANCING during reset
  scsi: imm: Fix use-after-free bug caused by unfinished delayed work
  scsi: target: sbp: Remove KMSG_COMPONENT macro
  scsi: core: Correct documentation for scsi_device_quiesce()
  scsi: mpi3mr: Prevent duplicate SAS/SATA device entries in channel 1
  scsi: target: Reset t_task_cdb pointer in error case
  scsi: ufs: core: Fix EH failure after W-LUN resume error
master
Linus Torvalds 2025-12-14 15:35:35 +12:00
commit 6a1636e066
18 changed files with 167 additions and 58 deletions

View File

@ -950,6 +950,19 @@ static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps
q = bdev_get_queue(p->path.dev->bdev); q = bdev_get_queue(p->path.dev->bdev);
attached_handler_name = scsi_dh_attached_handler_name(q, GFP_KERNEL); attached_handler_name = scsi_dh_attached_handler_name(q, GFP_KERNEL);
if (IS_ERR(attached_handler_name)) {
if (PTR_ERR(attached_handler_name) == -ENODEV) {
if (m->hw_handler_name) {
DMERR("hardware handlers are only allowed for SCSI devices");
kfree(m->hw_handler_name);
m->hw_handler_name = NULL;
}
attached_handler_name = NULL;
} else {
r = PTR_ERR(attached_handler_name);
goto bad;
}
}
if (attached_handler_name || m->hw_handler_name) { if (attached_handler_name || m->hw_handler_name) {
INIT_DELAYED_WORK(&p->activate_path, activate_path_work); INIT_DELAYED_WORK(&p->activate_path, activate_path_work);
r = setup_scsi_dh(p->path.dev->bdev, m, &attached_handler_name, &ti->error); r = setup_scsi_dh(p->path.dev->bdev, m, &attached_handler_name, &ti->error);

View File

@ -106,7 +106,6 @@ config PHANTOM
config RPMB config RPMB
tristate "RPMB partition interface" tristate "RPMB partition interface"
depends on MMC || SCSI_UFSHCD
help help
Unified RPMB unit interface for RPMB capable devices such as eMMC and Unified RPMB unit interface for RPMB capable devices such as eMMC and
UFS. Provides interface for in-kernel security controllers to access UFS. Provides interface for in-kernel security controllers to access

View File

@ -1260,6 +1260,7 @@ static void imm_detach(struct parport *pb)
imm_struct *dev; imm_struct *dev;
list_for_each_entry(dev, &imm_hosts, list) { list_for_each_entry(dev, &imm_hosts, list) {
if (dev->dev->port == pb) { if (dev->dev->port == pb) {
disable_delayed_work_sync(&dev->imm_tq);
list_del_init(&dev->list); list_del_init(&dev->list);
scsi_remove_host(dev->host); scsi_remove_host(dev->host);
scsi_host_put(dev->host); scsi_host_put(dev->host);

View File

@ -61,8 +61,8 @@
#include <linux/hdreg.h> #include <linux/hdreg.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/stringify.h> #include <linux/stringify.h>
#include <linux/irq.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/irq.h>
#include <asm/processor.h> #include <asm/processor.h>
#include <scsi/scsi.h> #include <scsi/scsi.h>
#include <scsi/scsi_host.h> #include <scsi/scsi_host.h>
@ -7843,6 +7843,30 @@ static int ipr_dump_mailbox_wait(struct ipr_cmnd *ipr_cmd)
return IPR_RC_JOB_RETURN; return IPR_RC_JOB_RETURN;
} }
/**
* ipr_set_affinity_nobalance
* @ioa_cfg: ipr_ioa_cfg struct for an ipr device
* @flag: bool
* true: ensable "IRQ_NO_BALANCING" bit for msix interrupt
* false: disable "IRQ_NO_BALANCING" bit for msix interrupt
* Description: This function will be called to disable/enable
* "IRQ_NO_BALANCING" to avoid irqbalance daemon
* kicking in during adapter reset.
**/
static void ipr_set_affinity_nobalance(struct ipr_ioa_cfg *ioa_cfg, bool flag)
{
int irq, i;
for (i = 0; i < ioa_cfg->nvectors; i++) {
irq = pci_irq_vector(ioa_cfg->pdev, i);
if (flag)
irq_set_status_flags(irq, IRQ_NO_BALANCING);
else
irq_clear_status_flags(irq, IRQ_NO_BALANCING);
}
}
/** /**
* ipr_reset_restore_cfg_space - Restore PCI config space. * ipr_reset_restore_cfg_space - Restore PCI config space.
* @ipr_cmd: ipr command struct * @ipr_cmd: ipr command struct
@ -7866,6 +7890,7 @@ static int ipr_reset_restore_cfg_space(struct ipr_cmnd *ipr_cmd)
return IPR_RC_JOB_CONTINUE; return IPR_RC_JOB_CONTINUE;
} }
ipr_set_affinity_nobalance(ioa_cfg, false);
ipr_fail_all_ops(ioa_cfg); ipr_fail_all_ops(ioa_cfg);
if (ioa_cfg->sis64) { if (ioa_cfg->sis64) {
@ -7945,6 +7970,7 @@ static int ipr_reset_start_bist(struct ipr_cmnd *ipr_cmd)
rc = pci_write_config_byte(ioa_cfg->pdev, PCI_BIST, PCI_BIST_START); rc = pci_write_config_byte(ioa_cfg->pdev, PCI_BIST, PCI_BIST_START);
if (rc == PCIBIOS_SUCCESSFUL) { if (rc == PCIBIOS_SUCCESSFUL) {
ipr_set_affinity_nobalance(ioa_cfg, true);
ipr_cmd->job_step = ipr_reset_bist_done; ipr_cmd->job_step = ipr_reset_bist_done;
ipr_reset_start_timer(ipr_cmd, IPR_WAIT_FOR_BIST_TIMEOUT); ipr_reset_start_timer(ipr_cmd, IPR_WAIT_FOR_BIST_TIMEOUT);
rc = IPR_RC_JOB_RETURN; rc = IPR_RC_JOB_RETURN;

View File

@ -141,6 +141,7 @@ Undo_event_q:
Undo_ports: Undo_ports:
sas_unregister_ports(sas_ha); sas_unregister_ports(sas_ha);
Undo_phys: Undo_phys:
sas_unregister_phys(sas_ha);
return error; return error;
} }

View File

@ -54,6 +54,7 @@ void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev);
void sas_scsi_recover_host(struct Scsi_Host *shost); void sas_scsi_recover_host(struct Scsi_Host *shost);
int sas_register_phys(struct sas_ha_struct *sas_ha); int sas_register_phys(struct sas_ha_struct *sas_ha);
void sas_unregister_phys(struct sas_ha_struct *sas_ha);
struct asd_sas_event *sas_alloc_event(struct asd_sas_phy *phy, gfp_t gfp_flags); struct asd_sas_event *sas_alloc_event(struct asd_sas_phy *phy, gfp_t gfp_flags);
void sas_free_event(struct asd_sas_event *event); void sas_free_event(struct asd_sas_event *event);
@ -145,20 +146,6 @@ static inline void sas_fail_probe(struct domain_device *dev, const char *func, i
func, dev->parent ? "exp-attached" : func, dev->parent ? "exp-attached" :
"direct-attached", "direct-attached",
SAS_ADDR(dev->sas_addr), err); SAS_ADDR(dev->sas_addr), err);
/*
* If the device probe failed, the expander phy attached address
* needs to be reset so that the phy will not be treated as flutter
* in the next revalidation
*/
if (dev->parent && !dev_is_expander(dev->dev_type)) {
struct sas_phy *phy = dev->phy;
struct domain_device *parent = dev->parent;
struct ex_phy *ex_phy = &parent->ex_dev.ex_phy[phy->number];
memset(ex_phy->attached_sas_addr, 0, SAS_ADDR_SIZE);
}
sas_unregister_dev(dev->port, dev); sas_unregister_dev(dev->port, dev);
} }

View File

@ -116,6 +116,7 @@ static void sas_phye_shutdown(struct work_struct *work)
int sas_register_phys(struct sas_ha_struct *sas_ha) int sas_register_phys(struct sas_ha_struct *sas_ha)
{ {
int i; int i;
int err;
/* Now register the phys. */ /* Now register the phys. */
for (i = 0; i < sas_ha->num_phys; i++) { for (i = 0; i < sas_ha->num_phys; i++) {
@ -132,8 +133,10 @@ int sas_register_phys(struct sas_ha_struct *sas_ha)
phy->frame_rcvd_size = 0; phy->frame_rcvd_size = 0;
phy->phy = sas_phy_alloc(&sas_ha->shost->shost_gendev, i); phy->phy = sas_phy_alloc(&sas_ha->shost->shost_gendev, i);
if (!phy->phy) if (!phy->phy) {
return -ENOMEM; err = -ENOMEM;
goto rollback;
}
phy->phy->identify.initiator_port_protocols = phy->phy->identify.initiator_port_protocols =
phy->iproto; phy->iproto;
@ -146,10 +149,34 @@ int sas_register_phys(struct sas_ha_struct *sas_ha)
phy->phy->maximum_linkrate = SAS_LINK_RATE_UNKNOWN; phy->phy->maximum_linkrate = SAS_LINK_RATE_UNKNOWN;
phy->phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN; phy->phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN;
sas_phy_add(phy->phy); err = sas_phy_add(phy->phy);
if (err) {
sas_phy_free(phy->phy);
goto rollback;
}
} }
return 0; return 0;
rollback:
for (i-- ; i >= 0 ; i--) {
struct asd_sas_phy *phy = sas_ha->sas_phy[i];
sas_phy_delete(phy->phy);
sas_phy_free(phy->phy);
}
return err;
}
void sas_unregister_phys(struct sas_ha_struct *sas_ha)
{
int i;
for (i = 0 ; i < sas_ha->num_phys ; i++) {
struct asd_sas_phy *phy = sas_ha->sas_phy[i];
sas_phy_delete(phy->phy);
sas_phy_free(phy->phy);
}
} }
const work_func_t sas_phy_event_fns[PHY_NUM_EVENTS] = { const work_func_t sas_phy_event_fns[PHY_NUM_EVENTS] = {

View File

@ -56,8 +56,8 @@ extern struct list_head mrioc_list;
extern int prot_mask; extern int prot_mask;
extern atomic64_t event_counter; extern atomic64_t event_counter;
#define MPI3MR_DRIVER_VERSION "8.15.0.5.50" #define MPI3MR_DRIVER_VERSION "8.15.0.5.51"
#define MPI3MR_DRIVER_RELDATE "12-August-2025" #define MPI3MR_DRIVER_RELDATE "18-November-2025"
#define MPI3MR_DRIVER_NAME "mpi3mr" #define MPI3MR_DRIVER_NAME "mpi3mr"
#define MPI3MR_DRIVER_LICENSE "GPL" #define MPI3MR_DRIVER_LICENSE "GPL"

View File

@ -1184,6 +1184,8 @@ static void mpi3mr_update_tgtdev(struct mpi3mr_ioc *mrioc,
if (is_added == true) if (is_added == true)
tgtdev->io_throttle_enabled = tgtdev->io_throttle_enabled =
(flags & MPI3_DEVICE0_FLAGS_IO_THROTTLING_REQUIRED) ? 1 : 0; (flags & MPI3_DEVICE0_FLAGS_IO_THROTTLING_REQUIRED) ? 1 : 0;
if (!mrioc->sas_transport_enabled)
tgtdev->non_stl = 1;
switch (flags & MPI3_DEVICE0_FLAGS_MAX_WRITE_SAME_MASK) { switch (flags & MPI3_DEVICE0_FLAGS_MAX_WRITE_SAME_MASK) {
case MPI3_DEVICE0_FLAGS_MAX_WRITE_SAME_256_LB: case MPI3_DEVICE0_FLAGS_MAX_WRITE_SAME_256_LB:
@ -4844,7 +4846,7 @@ static int mpi3mr_target_alloc(struct scsi_target *starget)
spin_lock_irqsave(&mrioc->tgtdev_lock, flags); spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
if (starget->channel == mrioc->scsi_device_channel) { if (starget->channel == mrioc->scsi_device_channel) {
tgt_dev = __mpi3mr_get_tgtdev_by_perst_id(mrioc, starget->id); tgt_dev = __mpi3mr_get_tgtdev_by_perst_id(mrioc, starget->id);
if (tgt_dev && !tgt_dev->is_hidden) { if (tgt_dev && !tgt_dev->is_hidden && tgt_dev->non_stl) {
scsi_tgt_priv_data->starget = starget; scsi_tgt_priv_data->starget = starget;
scsi_tgt_priv_data->dev_handle = tgt_dev->dev_handle; scsi_tgt_priv_data->dev_handle = tgt_dev->dev_handle;
scsi_tgt_priv_data->perst_id = tgt_dev->perst_id; scsi_tgt_priv_data->perst_id = tgt_dev->perst_id;

View File

@ -17,6 +17,7 @@
#include <linux/crash_dump.h> #include <linux/crash_dump.h>
#include <linux/trace_events.h> #include <linux/trace_events.h>
#include <linux/trace.h> #include <linux/trace.h>
#include <linux/irq.h>
#include <scsi/scsi_tcq.h> #include <scsi/scsi_tcq.h>
#include <scsi/scsicam.h> #include <scsi/scsicam.h>
@ -7776,6 +7777,31 @@ static void qla_pci_error_cleanup(scsi_qla_host_t *vha)
} }
/**
* qla2xxx_set_affinity_nobalance
* @pdev: pci_dev struct for a qla2xxx device
* @flag: bool
* true: enable "IRQ_NO_BALANCING" bit for msix interrupt
* false: disable "IRQ_NO_BALANCING" bit for msix interrupt
* Description: This function will be called to disable/enable
* "IRQ_NO_BALANCING" to avoid irqbalance daemon
* kicking in during adapter reset.
**/
static void qla2xxx_set_affinity_nobalance(struct pci_dev *pdev, bool flag)
{
int irq, i;
for (i = 0; i < QLA_BASE_VECTORS; i++) {
irq = pci_irq_vector(pdev, i);
if (flag)
irq_set_status_flags(irq, IRQ_NO_BALANCING);
else
irq_clear_status_flags(irq, IRQ_NO_BALANCING);
}
}
static pci_ers_result_t static pci_ers_result_t
qla2xxx_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) qla2xxx_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
{ {
@ -7794,6 +7820,8 @@ qla2xxx_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
goto out; goto out;
} }
qla2xxx_set_affinity_nobalance(pdev, false);
switch (state) { switch (state) {
case pci_channel_io_normal: case pci_channel_io_normal:
qla_pci_set_eeh_busy(vha); qla_pci_set_eeh_busy(vha);
@ -7930,6 +7958,8 @@ exit_slot_reset:
ql_dbg(ql_dbg_aer, base_vha, 0x900e, ql_dbg(ql_dbg_aer, base_vha, 0x900e,
"Slot Reset returning %x.\n", ret); "Slot Reset returning %x.\n", ret);
qla2xxx_set_affinity_nobalance(pdev, true);
return ret; return ret;
} }

View File

@ -1552,7 +1552,7 @@ static int qla4_82xx_cmdpeg_ready(struct scsi_qla_host *ha, int pegtune_val)
(val == PHAN_INITIALIZE_ACK)) (val == PHAN_INITIALIZE_ACK))
return 0; return 0;
set_current_state(TASK_UNINTERRUPTIBLE); set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(500); schedule_timeout(msecs_to_jiffies(500));
} while (--retries); } while (--retries);

View File

@ -353,7 +353,8 @@ EXPORT_SYMBOL_GPL(scsi_dh_attach);
* that may have a device handler attached * that may have a device handler attached
* @gfp - the GFP mask used in the kmalloc() call when allocating memory * @gfp - the GFP mask used in the kmalloc() call when allocating memory
* *
* Returns name of attached handler, NULL if no handler is attached. * Returns name of attached handler, NULL if no handler is attached, or
* and error pointer if an error occurred.
* Caller must take care to free the returned string. * Caller must take care to free the returned string.
*/ */
const char *scsi_dh_attached_handler_name(struct request_queue *q, gfp_t gfp) const char *scsi_dh_attached_handler_name(struct request_queue *q, gfp_t gfp)
@ -363,10 +364,11 @@ const char *scsi_dh_attached_handler_name(struct request_queue *q, gfp_t gfp)
sdev = scsi_device_from_queue(q); sdev = scsi_device_from_queue(q);
if (!sdev) if (!sdev)
return NULL; return ERR_PTR(-ENODEV);
if (sdev->handler) if (sdev->handler)
handler_name = kstrdup(sdev->handler->name, gfp); handler_name = kstrdup(sdev->handler->name, gfp) ? :
ERR_PTR(-ENOMEM);
put_device(&sdev->sdev_gendev); put_device(&sdev->sdev_gendev);
return handler_name; return handler_name;
} }

View File

@ -2801,7 +2801,7 @@ EXPORT_SYMBOL_GPL(sdev_evt_send_simple);
* *
* Must be called with user context, may sleep. * Must be called with user context, may sleep.
* *
* Returns zero if unsuccessful or an error if not. * Returns zero if successful or an error if not.
*/ */
int int
scsi_device_quiesce(struct scsi_device *sdev) scsi_device_quiesce(struct scsi_device *sdev)

View File

@ -5,8 +5,7 @@
* Copyright (C) 2011 Chris Boot <bootc@bootc.net> * Copyright (C) 2011 Chris Boot <bootc@bootc.net>
*/ */
#define KMSG_COMPONENT "sbp_target" #define pr_fmt(fmt) "sbp_target: " fmt
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>

View File

@ -1524,6 +1524,7 @@ target_cmd_init_cdb(struct se_cmd *cmd, unsigned char *cdb, gfp_t gfp)
if (scsi_command_size(cdb) > sizeof(cmd->__t_task_cdb)) { if (scsi_command_size(cdb) > sizeof(cmd->__t_task_cdb)) {
cmd->t_task_cdb = kzalloc(scsi_command_size(cdb), gfp); cmd->t_task_cdb = kzalloc(scsi_command_size(cdb), gfp);
if (!cmd->t_task_cdb) { if (!cmd->t_task_cdb) {
cmd->t_task_cdb = &cmd->__t_task_cdb[0];
pr_err("Unable to allocate cmd->t_task_cdb" pr_err("Unable to allocate cmd->t_task_cdb"
" %u > sizeof(cmd->__t_task_cdb): %lu ops\n", " %u > sizeof(cmd->__t_task_cdb): %lu ops\n",
scsi_command_size(cdb), scsi_command_size(cdb),

View File

@ -6,6 +6,7 @@
menuconfig SCSI_UFSHCD menuconfig SCSI_UFSHCD
tristate "Universal Flash Storage Controller" tristate "Universal Flash Storage Controller"
depends on SCSI && SCSI_DMA depends on SCSI && SCSI_DMA
depends on RPMB || !RPMB
select PM_DEVFREQ select PM_DEVFREQ
select DEVFREQ_GOV_SIMPLE_ONDEMAND select DEVFREQ_GOV_SIMPLE_ONDEMAND
select NLS select NLS

View File

@ -1455,15 +1455,14 @@ out:
static void ufshcd_clock_scaling_unprepare(struct ufs_hba *hba, int err) static void ufshcd_clock_scaling_unprepare(struct ufs_hba *hba, int err)
{ {
up_write(&hba->clk_scaling_lock); up_write(&hba->clk_scaling_lock);
mutex_unlock(&hba->wb_mutex);
blk_mq_unquiesce_tagset(&hba->host->tag_set);
mutex_unlock(&hba->host->scan_mutex);
/* Enable Write Booster if current gear requires it else disable it */ /* Enable Write Booster if current gear requires it else disable it */
if (ufshcd_enable_wb_if_scaling_up(hba) && !err) if (ufshcd_enable_wb_if_scaling_up(hba) && !err)
ufshcd_wb_toggle(hba, hba->pwr_info.gear_rx >= hba->clk_scaling.wb_gear); ufshcd_wb_toggle(hba, hba->pwr_info.gear_rx >= hba->clk_scaling.wb_gear);
mutex_unlock(&hba->wb_mutex);
blk_mq_unquiesce_tagset(&hba->host->tag_set);
mutex_unlock(&hba->host->scan_mutex);
ufshcd_release(hba); ufshcd_release(hba);
} }
@ -6504,6 +6503,11 @@ static void ufshcd_clk_scaling_suspend(struct ufs_hba *hba, bool suspend)
static void ufshcd_err_handling_prepare(struct ufs_hba *hba) static void ufshcd_err_handling_prepare(struct ufs_hba *hba)
{ {
/*
* A WLUN resume failure could potentially lead to the HBA being
* runtime suspended, so take an extra reference on hba->dev.
*/
pm_runtime_get_sync(hba->dev);
ufshcd_rpm_get_sync(hba); ufshcd_rpm_get_sync(hba);
if (pm_runtime_status_suspended(&hba->ufs_device_wlun->sdev_gendev) || if (pm_runtime_status_suspended(&hba->ufs_device_wlun->sdev_gendev) ||
hba->is_sys_suspended) { hba->is_sys_suspended) {
@ -6543,6 +6547,7 @@ static void ufshcd_err_handling_unprepare(struct ufs_hba *hba)
if (ufshcd_is_clkscaling_supported(hba)) if (ufshcd_is_clkscaling_supported(hba))
ufshcd_clk_scaling_suspend(hba, false); ufshcd_clk_scaling_suspend(hba, false);
ufshcd_rpm_put(hba); ufshcd_rpm_put(hba);
pm_runtime_put(hba->dev);
} }
static inline bool ufshcd_err_handling_should_stop(struct ufs_hba *hba) static inline bool ufshcd_err_handling_should_stop(struct ufs_hba *hba)
@ -6557,28 +6562,42 @@ static inline bool ufshcd_err_handling_should_stop(struct ufs_hba *hba)
#ifdef CONFIG_PM #ifdef CONFIG_PM
static void ufshcd_recover_pm_error(struct ufs_hba *hba) static void ufshcd_recover_pm_error(struct ufs_hba *hba)
{ {
struct scsi_target *starget = hba->ufs_device_wlun->sdev_target;
struct Scsi_Host *shost = hba->host; struct Scsi_Host *shost = hba->host;
struct scsi_device *sdev; struct scsi_device *sdev;
struct request_queue *q; struct request_queue *q;
int ret; bool resume_sdev_queues = false;
hba->is_sys_suspended = false; hba->is_sys_suspended = false;
/*
* Set RPM status of wlun device to RPM_ACTIVE,
* this also clears its runtime error.
*/
ret = pm_runtime_set_active(&hba->ufs_device_wlun->sdev_gendev);
/* hba device might have a runtime error otherwise */ /*
if (ret) * Ensure the parent's error status is cleared before proceeding
ret = pm_runtime_set_active(hba->dev); * to the child, as the parent must be active to activate the child.
*/
if (hba->dev->power.runtime_error) {
/* hba->dev has no functional parent thus simplily set RPM_ACTIVE */
pm_runtime_set_active(hba->dev);
resume_sdev_queues = true;
}
if (hba->ufs_device_wlun->sdev_gendev.power.runtime_error) {
/*
* starget, parent of wlun, might be suspended if wlun resume failed.
* Make sure parent is resumed before set child (wlun) active.
*/
pm_runtime_get_sync(&starget->dev);
pm_runtime_set_active(&hba->ufs_device_wlun->sdev_gendev);
pm_runtime_put_sync(&starget->dev);
resume_sdev_queues = true;
}
/* /*
* If wlun device had runtime error, we also need to resume those * If wlun device had runtime error, we also need to resume those
* consumer scsi devices in case any of them has failed to be * consumer scsi devices in case any of them has failed to be
* resumed due to supplier runtime resume failure. This is to unblock * resumed due to supplier runtime resume failure. This is to unblock
* blk_queue_enter in case there are bios waiting inside it. * blk_queue_enter in case there are bios waiting inside it.
*/ */
if (!ret) { if (resume_sdev_queues) {
shost_for_each_device(sdev, shost) { shost_for_each_device(sdev, shost) {
q = sdev->request_queue; q = sdev->request_queue;
if (q->dev && (q->rpm_status == RPM_SUSPENDED || if (q->dev && (q->rpm_status == RPM_SUSPENDED ||
@ -6679,19 +6698,22 @@ static void ufshcd_err_handler(struct work_struct *work)
hba->saved_uic_err, hba->force_reset, hba->saved_uic_err, hba->force_reset,
ufshcd_is_link_broken(hba) ? "; link is broken" : ""); ufshcd_is_link_broken(hba) ? "; link is broken" : "");
/* if (hba->ufs_device_wlun) {
* Use ufshcd_rpm_get_noresume() here to safely perform link recovery /*
* even if an error occurs during runtime suspend or runtime resume. * Use ufshcd_rpm_get_noresume() here to safely perform link
* This avoids potential deadlocks that could happen if we tried to * recovery even if an error occurs during runtime suspend or
* resume the device while a PM operation is already in progress. * runtime resume. This avoids potential deadlocks that could
*/ * happen if we tried to resume the device while a PM operation
ufshcd_rpm_get_noresume(hba); * is already in progress.
if (hba->pm_op_in_progress) { */
ufshcd_link_recovery(hba); ufshcd_rpm_get_noresume(hba);
if (hba->pm_op_in_progress) {
ufshcd_link_recovery(hba);
ufshcd_rpm_put(hba);
return;
}
ufshcd_rpm_put(hba); ufshcd_rpm_put(hba);
return;
} }
ufshcd_rpm_put(hba);
down(&hba->host_sem); down(&hba->host_sem);
spin_lock_irqsave(hba->host->host_lock, flags); spin_lock_irqsave(hba->host->host_lock, flags);

View File

@ -1769,10 +1769,9 @@ static void ufs_qcom_dump_testbus(struct ufs_hba *hba)
{ {
struct ufs_qcom_host *host = ufshcd_get_variant(hba); struct ufs_qcom_host *host = ufshcd_get_variant(hba);
int i, j, nminor = 0, testbus_len = 0; int i, j, nminor = 0, testbus_len = 0;
u32 *testbus __free(kfree) = NULL;
char *prefix; char *prefix;
testbus = kmalloc_array(256, sizeof(u32), GFP_KERNEL); u32 *testbus __free(kfree) = kmalloc_array(256, sizeof(u32), GFP_KERNEL);
if (!testbus) if (!testbus)
return; return;
@ -1794,13 +1793,12 @@ static void ufs_qcom_dump_testbus(struct ufs_hba *hba)
static int ufs_qcom_dump_regs(struct ufs_hba *hba, size_t offset, size_t len, static int ufs_qcom_dump_regs(struct ufs_hba *hba, size_t offset, size_t len,
const char *prefix, void __iomem *base) const char *prefix, void __iomem *base)
{ {
u32 *regs __free(kfree) = NULL;
size_t pos; size_t pos;
if (offset % 4 != 0 || len % 4 != 0) if (offset % 4 != 0 || len % 4 != 0)
return -EINVAL; return -EINVAL;
regs = kzalloc(len, GFP_ATOMIC); u32 *regs __free(kfree) = kzalloc(len, GFP_ATOMIC);
if (!regs) if (!regs)
return -ENOMEM; return -ENOMEM;