summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-08-02 09:32:07 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-08-02 09:32:07 -0700
commita84c804215062d17c14141988ff3c69af961b49d (patch)
tree01089bbcca81c477de23ca7e0e6b9ee212cb715c /drivers
parent49c9f4657b2d584690b050169b646cbcee6e7958 (diff)
parent0279fd451a9971c0d5b959fc59f3e11b55e1694e (diff)
Merge tag 'scsi-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
Pull SCSI fixes from James Bottomley" "No core changes. The largest driver fix is the reversion of threaded interrupt handlers in UFS and the next is the resume deadlock fix in hisi_sas which extends into libsas" * tag 'scsi-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi: scsi: ufs: core: Initialize hba->rpmbs list in ufshcd scsi: mpi3mr: Fix potential deadlock in mpi3mr_fault_uevent_emit scsi: target: Clear cmd_cnt when initial counter enrollment fails scsi: zfcp: Fix memory leak during adapter release by destroying gid_pn_req scsi: ufs: core: Revert "Delegate the interrupt service routine to a threaded IRQ handler" scsi: ufs: core: Cancel RTC work in active-active suspend scsi: scsi_debug: Fix REPORT ZONES alloc_len underflow OOB write scsi: target: iblock: Fix wrong PR ops NULL check for PREEMPT/RELEASE scsi: ufs: dt-bindings: Add missing mcq reg for qcom,sa8255p-ufshc scsi: libsas: Fix HA resume deadlock and hisi_sas disk-wake race scsi: libiscsi_tcp: Bound SCSI Response data segment to the connection buffer scsi: libiscsi: Fix stale-data leak into the SCSI sense buffer
Diffstat (limited to 'drivers')
-rw-r--r--drivers/s390/scsi/zfcp_aux.c1
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas_v3_hw.c10
-rw-r--r--drivers/scsi/libiscsi.c2
-rw-r--r--drivers/scsi/libiscsi_tcp.c8
-rw-r--r--drivers/scsi/libsas/sas_init.c37
-rw-r--r--drivers/scsi/mpi3mr/mpi3mr_fw.c9
-rw-r--r--drivers/scsi/scsi_debug.c8
-rw-r--r--drivers/target/target_core_iblock.c6
-rw-r--r--drivers/target/target_core_transport.c12
-rw-r--r--drivers/ufs/core/ufs-rpmb.c2
-rw-r--r--drivers/ufs/core/ufshcd.c46
11 files changed, 56 insertions, 85 deletions
diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
index 8ff7db7921b5..fea573e00f9d 100644
--- a/drivers/s390/scsi/zfcp_aux.c
+++ b/drivers/s390/scsi/zfcp_aux.c
@@ -253,6 +253,7 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter)
{
mempool_destroy(adapter->pool.erp_req);
+ mempool_destroy(adapter->pool.gid_pn_req);
mempool_destroy(adapter->pool.scsi_req);
mempool_destroy(adapter->pool.scsi_abort);
mempool_destroy(adapter->pool.qtcb_pool);
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
index 213d5b5dea94..8a2500993e19 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
@@ -5261,15 +5261,7 @@ static int _resume_v3_hw(struct device *device)
return rc;
}
phys_init_v3_hw(hisi_hba);
-
- /*
- * If a directly-attached disk is removed during suspend, a deadlock
- * may occur, as the PHYE_RESUME_TIMEOUT processing will require the
- * hisi_hba->device to be active, which can only happen when resume
- * completes. So don't wait for the HA event workqueue to drain upon
- * resume.
- */
- sas_resume_ha_no_sync(sha);
+ sas_resume_ha(sha);
clear_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags);
dev_warn(dev, "end of resuming controller\n");
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index 160f02f2f51d..5cbc51899de0 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -918,7 +918,7 @@ invalid_datalen:
}
senselen = get_unaligned_be16(data);
- if (datalen < senselen)
+ if (datalen < senselen + 2)
goto invalid_datalen;
memcpy(sc->sense_buffer, data + 2,
diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c
index e90805ba868f..7223bb18b048 100644
--- a/drivers/scsi/libiscsi_tcp.c
+++ b/drivers/scsi/libiscsi_tcp.c
@@ -752,13 +752,6 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
rc = __iscsi_complete_pdu(conn, hdr, NULL, 0);
spin_unlock(&conn->session->back_lock);
break;
- case ISCSI_OP_SCSI_CMD_RSP:
- if (tcp_conn->in.datalen) {
- iscsi_tcp_data_recv_prep(tcp_conn);
- return 0;
- }
- rc = iscsi_complete_pdu(conn, hdr, NULL, 0);
- break;
case ISCSI_OP_R2T:
if (ahslen) {
rc = ISCSI_ERR_AHSLEN;
@@ -766,6 +759,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
}
rc = iscsi_tcp_r2t_rsp(conn, hdr);
break;
+ case ISCSI_OP_SCSI_CMD_RSP:
case ISCSI_OP_LOGIN_RSP:
case ISCSI_OP_TEXT_RSP:
case ISCSI_OP_REJECT:
diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c
index 0bec236f0fb5..c3f3d05b46de 100644
--- a/drivers/scsi/libsas/sas_init.c
+++ b/drivers/scsi/libsas/sas_init.c
@@ -410,7 +410,7 @@ static void sas_resume_insert_broadcast_ha(struct sas_ha_struct *ha)
}
}
-static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
+void sas_resume_ha(struct sas_ha_struct *ha)
{
const unsigned long tmo = msecs_to_jiffies(25000);
int i;
@@ -426,6 +426,23 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
dev_info(ha->dev, "waiting up to 25 seconds for %d phy%s to resume\n",
i, i > 1 ? "s" : "");
wait_event_timeout(ha->eh_wait_q, phys_suspended(ha) == 0, tmo);
+
+ /*
+ * All phys are back up or timed out. Turn on I/O and drain
+ * pending work.
+ */
+ scsi_unblock_requests(ha->shost);
+ sas_drain_work(ha);
+
+ /*
+ * Send PHYE_RESUME_TIMEOUT after sas_drain_work(). The handler
+ * calls sas_deform_port() -> sas_destruct_devices(), which removes
+ * SCSI devices and, for LLDDs using device_link() PM sync, waits
+ * for the host to be runtime-active. Sending it before the drain
+ * would deadlock: the drain waits for the handler, the handler
+ * waits for host resume, and host resume waits for the drain to
+ * finish.
+ */
for (i = 0; i < ha->num_phys; i++) {
struct asd_sas_phy *phy = ha->sas_phy[i];
@@ -436,12 +453,6 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
}
}
- /* all phys are back up or timed out, turn on i/o so we can
- * flush out disks that did not return
- */
- scsi_unblock_requests(ha->shost);
- if (drain)
- sas_drain_work(ha);
clear_bit(SAS_HA_RESUMING, &ha->state);
sas_queue_deferred_work(ha);
@@ -450,20 +461,8 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
*/
sas_resume_insert_broadcast_ha(ha);
}
-
-void sas_resume_ha(struct sas_ha_struct *ha)
-{
- _sas_resume_ha(ha, true);
-}
EXPORT_SYMBOL(sas_resume_ha);
-/* A no-sync variant, which does not call sas_drain_ha(). */
-void sas_resume_ha_no_sync(struct sas_ha_struct *ha)
-{
- _sas_resume_ha(ha, false);
-}
-EXPORT_SYMBOL(sas_resume_ha_no_sync);
-
void sas_suspend_ha(struct sas_ha_struct *ha)
{
int i;
diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c
index 31b19ed1528e..681868716ebd 100644
--- a/drivers/scsi/mpi3mr/mpi3mr_fw.c
+++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c
@@ -9,6 +9,7 @@
#include "mpi3mr.h"
#include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/sched/mm.h>
static int
mpi3mr_issue_reset(struct mpi3mr_ioc *mrioc, u16 reset_type, u16 reset_reason);
@@ -1287,11 +1288,14 @@ out_failed:
static void mpi3mr_fault_uevent_emit(struct mpi3mr_ioc *mrioc)
{
struct kobj_uevent_env *env;
+ unsigned int noio_flag;
int ret;
+ noio_flag = memalloc_noio_save();
+
env = kzalloc_obj(*env);
if (!env)
- return;
+ goto out_restore;
ret = add_uevent_var(env, "DRIVER=%s", mrioc->driver_name);
if (ret)
@@ -1326,7 +1330,8 @@ static void mpi3mr_fault_uevent_emit(struct mpi3mr_ioc *mrioc)
out_free:
kfree(env);
-
+out_restore:
+ memalloc_noio_restore(noio_flag);
}
/**
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 9d1c9c41d0f9..643051332132 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -5890,6 +5890,7 @@ static int resp_report_zones(struct scsi_cmnd *scp,
u32 alloc_len, rep_opts, rep_len;
bool partial;
u64 lba, zs_lba;
+ u64 arr_len;
u8 *arr = NULL, *desc;
u8 *cmd = scp->cmnd;
struct sdeb_zone_state *zsp = NULL;
@@ -5911,9 +5912,12 @@ static int resp_report_zones(struct scsi_cmnd *scp,
return check_condition_result;
}
- rep_max_zones = (alloc_len - 64) >> ilog2(RZONES_DESC_HD);
+ rep_max_zones = (ALIGN((u64)alloc_len, RZONES_DESC_HD) - RZONES_DESC_HD) >>
+ ilog2(RZONES_DESC_HD);
+ rep_max_zones = min_t(unsigned int, rep_max_zones, devip->nr_zones);
+ arr_len = (u64)RZONES_DESC_HD * (rep_max_zones + 1);
- arr = kzalloc(alloc_len, GFP_ATOMIC | __GFP_NOWARN);
+ arr = kzalloc(arr_len, GFP_ATOMIC | __GFP_NOWARN);
if (!arr) {
mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC,
INSUFF_RES_ASCQ);
diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c
index 1087d1d17c36..ea65d39fef88 100644
--- a/drivers/target/target_core_iblock.c
+++ b/drivers/target/target_core_iblock.c
@@ -906,7 +906,7 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key,
break;
case PRO_PREEMPT:
case PRO_PREEMPT_AND_ABORT:
- if (!ops->pr_clear) {
+ if (!ops->pr_preempt) {
pr_err("block_device does not support pr_preempt.\n");
return TCM_UNSUPPORTED_SCSI_OPCODE;
}
@@ -916,8 +916,8 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key,
sa == PRO_PREEMPT_AND_ABORT);
break;
case PRO_RELEASE:
- if (!ops->pr_clear) {
- pr_err("block_device does not support pr_pclear.\n");
+ if (!ops->pr_release) {
+ pr_err("block_device does not support pr_release.\n");
return TCM_UNSUPPORTED_SCSI_OPCODE;
}
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index fad03a15c969..dcfe94594916 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -1734,6 +1734,7 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess,
u32 data_length, int task_attr, int data_dir, int flags)
{
struct se_portal_group *se_tpg;
+ int ret;
se_tpg = se_sess->se_tpg;
BUG_ON(!se_tpg);
@@ -1763,7 +1764,11 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess,
* necessary for fabrics using TARGET_SCF_ACK_KREF that expect a second
* kref_put() to happen during fabric packet acknowledgement.
*/
- return target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF);
+ ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF);
+ if (ret)
+ se_cmd->cmd_cnt = NULL;
+
+ return ret;
}
EXPORT_SYMBOL_GPL(target_init_cmd);
@@ -2039,8 +2044,10 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess,
* allocation failure.
*/
ret = core_tmr_alloc_req(se_cmd, fabric_tmr_ptr, tm_type, gfp);
- if (ret < 0)
+ if (ret < 0) {
+ se_cmd->cmd_cnt = NULL;
return -ENOMEM;
+ }
if (tm_type == TMR_ABORT_TASK)
se_cmd->se_tmr_req->ref_task_tag = tag;
@@ -2048,6 +2055,7 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess,
/* See target_submit_cmd for commentary */
ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF);
if (ret) {
+ se_cmd->cmd_cnt = NULL;
core_tmr_release_req(se_cmd->se_tmr_req);
return ret;
}
diff --git a/drivers/ufs/core/ufs-rpmb.c b/drivers/ufs/core/ufs-rpmb.c
index ffad049872b9..62120dc2e9da 100644
--- a/drivers/ufs/core/ufs-rpmb.c
+++ b/drivers/ufs/core/ufs-rpmb.c
@@ -152,8 +152,6 @@ int ufs_rpmb_probe(struct ufs_hba *hba)
return -EINVAL;
}
- INIT_LIST_HEAD(&hba->rpmbs);
-
struct rpmb_descr descr = {
.type = RPMB_TYPE_UFS,
.route_frames = ufs_rpmb_route_frames,
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index d3044a3089b5..13e6357a8bbb 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -7357,7 +7357,7 @@ static irqreturn_t ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
}
/**
- * ufshcd_threaded_intr - Threaded interrupt service routine
+ * ufshcd_intr - Main interrupt service routine
* @irq: irq number
* @__hba: pointer to adapter instance
*
@@ -7365,7 +7365,7 @@ static irqreturn_t ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
* IRQ_HANDLED - If interrupt is valid
* IRQ_NONE - If invalid interrupt
*/
-static irqreturn_t ufshcd_threaded_intr(int irq, void *__hba)
+static irqreturn_t ufshcd_intr(int irq, void *__hba)
{
u32 last_intr_status, intr_status, enabled_intr_status = 0;
irqreturn_t retval = IRQ_NONE;
@@ -7404,38 +7404,6 @@ static irqreturn_t ufshcd_threaded_intr(int irq, void *__hba)
return retval;
}
-/**
- * ufshcd_intr - Main interrupt service routine
- * @irq: irq number
- * @__hba: pointer to adapter instance
- *
- * Return:
- * IRQ_HANDLED - If interrupt is valid
- * IRQ_WAKE_THREAD - If handling is moved to threaded handled
- * IRQ_NONE - If invalid interrupt
- */
-static irqreturn_t ufshcd_intr(int irq, void *__hba)
-{
- struct ufs_hba *hba = __hba;
- u32 intr_status, enabled_intr_status;
-
- /*
- * Handle interrupt in thread if MCQ or ESI is disabled,
- * and no active UIC command.
- */
- if ((!hba->mcq_enabled || !hba->mcq_esi_enabled) &&
- !hba->active_uic_cmd)
- return IRQ_WAKE_THREAD;
-
- intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS);
- enabled_intr_status = intr_status & ufshcd_readl(hba, REG_INTERRUPT_ENABLE);
-
- ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS);
-
- /* Directly handle interrupts since MCQ ESI handlers does the hard job */
- return ufshcd_sl_intr(hba, enabled_intr_status);
-}
-
static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag)
{
int err = 0;
@@ -10269,6 +10237,7 @@ static int __ufshcd_wl_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
req_link_state == UIC_LINK_ACTIVE_STATE) {
ufshcd_disable_auto_bkops(hba);
flush_work(&hba->eeh_work);
+ cancel_delayed_work_sync(&hba->ufs_rtc_update_work);
goto vops_suspend;
}
@@ -10478,10 +10447,11 @@ static int __ufshcd_wl_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
if (ret)
goto set_old_link_state;
ufshcd_set_timestamp_attr(hba);
- schedule_delayed_work(&hba->ufs_rtc_update_work,
- msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS));
}
+ schedule_delayed_work(&hba->ufs_rtc_update_work,
+ msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS));
+
if (ufshcd_keep_autobkops_enabled_except_suspend(hba))
ufshcd_enable_auto_bkops(hba);
else
@@ -10988,6 +10958,7 @@ int ufshcd_alloc_host(struct device *dev, struct ufs_hba **hba_handle)
hba->nop_out_timeout = NOP_OUT_TIMEOUT;
ufshcd_set_sg_entry_size(hba, sizeof(struct ufshcd_sg_entry));
INIT_LIST_HEAD(&hba->clk_list_head);
+ INIT_LIST_HEAD(&hba->rpmbs);
spin_lock_init(&hba->outstanding_lock);
*hba_handle = hba;
@@ -11236,8 +11207,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
ufshcd_readl(hba, REG_INTERRUPT_ENABLE);
/* IRQ registration */
- err = devm_request_threaded_irq(dev, irq, ufshcd_intr, ufshcd_threaded_intr,
- IRQF_ONESHOT | IRQF_SHARED, UFSHCD, hba);
+ err = devm_request_irq(dev, irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
if (err) {
dev_err(hba->dev, "request irq failed\n");
goto out_disable;