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

Pull SCSI fixes from James Bottomley:
 "The biggest core change is the reliable wake fix for scsi_schedule_eh
  which is used by both libata and libsas which could otherwise cause
  error handler hangs due to rare races.

  All other fixes are in drivers (well except the export symbol removal)
  the next biggest being the target PR-OUT transportid parsing fix"

* tag 'scsi-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi:
  scsi: hpsa: Fix DMA mapping leak on IOACCEL2 reset path
  scsi: elx: efct: Fix refcount leak in efct_hw_io_abort()
  scsi: elx: efct: Fix I/O leak on unsupported additional CDB
  scsi: core: wake eh reliably when using scsi_schedule_eh
  scsi: target: core: Fix iSCSI ISID use-after-free in REGISTER AND MOVE
  scsi: target: Bound PR-OUT TransportID parsing to the received buffer
  scsi: lpfc: Fix memory leak in lpfc_sli4_driver_resource_setup()
  scsi: sg: Report request-table problems when any status is set
  scsi: ufs: core: tracing: Do not dereference pointers in TP_printk()
  scsi: bfa: Reduce kernel stack usage in bfa_fcs_lport_fdmi_build_portattr_block()
  scsi: xen: scsiback: Free the command tag on the TMR submit-failure path
  scsi: xen: scsiback: Free unsubmitted command instead of double-putting it
  scsi: core: Remove export for scsi_device_from_queue()
This commit is contained in:
Linus Torvalds
2026-07-18 12:36:19 -07:00
16 changed files with 141 additions and 50 deletions

View File

@@ -2627,7 +2627,7 @@ bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_lport_fdmi_s *fdmi,
}
static void
static noinline_for_stack void
bfa_fcs_fdmi_get_portattr(struct bfa_fcs_lport_fdmi_s *fdmi,
struct bfa_fcs_fdmi_port_attr_s *port_attr)
{

View File

@@ -1997,6 +1997,8 @@ efct_hw_io_abort(struct efct_hw *hw, struct efct_hw_io *io_to_abort,
wqcb = efct_hw_reqtag_alloc(hw, efct_hw_wq_process_abort, io_to_abort);
if (!wqcb) {
efc_log_err(hw->os, "can't allocate request tag\n");
io_to_abort->abort_in_progress = false;
kref_put(&io_to_abort->ref, io_to_abort->release);
return -ENOSPC;
}

View File

@@ -385,6 +385,7 @@ efct_dispatch_fcp_cmd(struct efct_node *node, struct efc_hw_sequence *seq)
if (cmnd->fc_flags & FCP_CFL_LEN_MASK) {
efc_log_err(efct, "Additional CDB not supported\n");
efct_scsi_io_free(io);
return -EIO;
}
/*

View File

@@ -357,6 +357,7 @@ static void scsi_host_dev_release(struct device *dev)
/* Wait for functions invoked through call_rcu(&scmd->rcu, ...) */
rcu_barrier();
cancel_work_sync(&shost->eh_work);
if (shost->tmf_work_q)
destroy_workqueue(shost->tmf_work_q);
if (shost->ehandler)
@@ -422,6 +423,7 @@ struct Scsi_Host *scsi_host_alloc(const struct scsi_host_template *sht, int priv
INIT_LIST_HEAD(&shost->starved_list);
init_waitqueue_head(&shost->host_wait);
mutex_init(&shost->scan_mutex);
INIT_WORK(&shost->eh_work, scsi_rcu_eh_wakeup);
index = ida_alloc(&host_index_ida, GFP_KERNEL);
if (index < 0) {

View File

@@ -5017,6 +5017,10 @@ static int hpsa_scsi_ioaccel2_queue_command(struct ctlr_info *h,
if (phys_disk->in_reset) {
cmd->result = DID_RESET << 16;
atomic_dec(&phys_disk->ioaccel_cmds_out);
scsi_dma_unmap(cmd);
if (use_sg > h->ioaccel_maxsg)
hpsa_unmap_ioaccel2_sg_chain_block(h, cp);
return -1;
}

View File

@@ -8189,6 +8189,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
mempool_free(mboxq, phba->mbox_mem_pool);
goto out_free_bsmbx;
}
mempool_free(mboxq, phba->mbox_mem_pool);
/*
* 1 for cmd, 1 for rsp, NVME adds an extra one
@@ -8311,8 +8312,6 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
goto out_free_sg_dma_buf;
}
mempool_free(mboxq, phba->mbox_mem_pool);
/* Verify OAS is supported */
lpfc_sli4_oas_verify(phba);

View File

@@ -73,6 +73,26 @@ void scsi_eh_wakeup(struct Scsi_Host *shost, unsigned int busy)
}
}
void scsi_rcu_eh_wakeup(struct work_struct *work)
{
struct Scsi_Host *shost = container_of(work, struct Scsi_Host, eh_work);
unsigned long flags;
unsigned int busy;
/*
* Ensure any running scsi_dec_host_busy has completed its rcu section
* so changes to host state and host_eh_scheduled are visible to all
* future calls of scsi_dec_host_busy
*/
synchronize_rcu();
busy = scsi_host_busy(shost);
spin_lock_irqsave(shost->host_lock, flags);
scsi_eh_wakeup(shost, busy);
spin_unlock_irqrestore(shost->host_lock, flags);
}
/**
* scsi_schedule_eh - schedule EH for SCSI host
* @shost: SCSI host to invoke error handling on.
@@ -88,7 +108,7 @@ void scsi_schedule_eh(struct Scsi_Host *shost)
if (scsi_host_set_state(shost, SHOST_RECOVERY) == 0 ||
scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY) == 0) {
shost->host_eh_scheduled++;
scsi_eh_wakeup(shost, scsi_host_busy(shost));
queue_work(shost->tmf_work_q, &shost->eh_work);
}
spin_unlock_irqrestore(shost->host_lock, flags);

View File

@@ -2224,14 +2224,6 @@ struct scsi_device *scsi_device_from_queue(struct request_queue *q)
return sdev;
}
/*
* pktcdvd should have been integrated into the SCSI layers, but for historical
* reasons like the old IDE driver it isn't. This export allows it to safely
* probe if a given device is a SCSI one and only attach to that.
*/
#ifdef CONFIG_CDROM_PKTCDVD_MODULE
EXPORT_SYMBOL_GPL(scsi_device_from_queue);
#endif
/**
* scsi_block_requests - Utility function used by low-level drivers to prevent

View File

@@ -91,6 +91,7 @@ extern enum blk_eh_timer_return scsi_timeout(struct request *req);
extern int scsi_error_handler(void *host);
extern enum scsi_disposition scsi_decide_disposition(struct scsi_cmnd *cmd);
extern void scsi_eh_wakeup(struct Scsi_Host *shost, unsigned int busy);
extern void scsi_rcu_eh_wakeup(struct work_struct *work);
extern void scsi_eh_scmd_add(struct scsi_cmnd *);
void scsi_eh_ready_devs(struct Scsi_Host *shost,
struct list_head *work_q,
@@ -102,6 +103,7 @@ void scsi_eh_done(struct scsi_cmnd *scmd);
/* scsi_lib.c */
extern void scsi_device_unbusy(struct scsi_device *sdev, struct scsi_cmnd *cmd);
extern struct scsi_device *scsi_device_from_queue(struct request_queue *q);
extern void scsi_queue_insert(struct scsi_cmnd *cmd,
enum scsi_qc_status reason);
extern void scsi_io_completion(struct scsi_cmnd *, unsigned int);

View File

@@ -863,10 +863,9 @@ sg_fill_request_table(Sg_fd *sfp, sg_req_info_t *rinfo)
if (val >= SG_MAX_QUEUE)
break;
rinfo[val].req_state = srp->done + 1;
rinfo[val].problem =
srp->header.masked_status &
srp->header.host_status &
srp->header.driver_status;
rinfo[val].problem = srp->header.masked_status ||
srp->header.host_status ||
srp->header.driver_status;
if (srp->done)
rinfo[val].duration =
srp->header.duration;

View File

@@ -290,13 +290,24 @@ static void sbp_parse_pr_out_transport_id(char *buf, char *i_str)
static bool iscsi_parse_pr_out_transport_id(
struct se_portal_group *se_tpg,
char *buf,
u32 buf_len,
u32 *out_tid_len,
char **port_nexus_ptr,
char *i_str)
{
char *p;
u32 tid_len;
int i;
u8 format_code = (buf[0] & 0xc0);
u8 format_code;
/*
* The 4-byte iSCSI TransportID header (FORMAT CODE + 2-byte ADDITIONAL
* LENGTH) must be present before any of it can be parsed.
*/
if (buf_len < 4)
return false;
format_code = buf[0] & 0xc0;
/*
* Check for FORMAT CODE 00b or 01b from spc4r17, section 7.5.4.6:
*
@@ -316,15 +327,17 @@ static bool iscsi_parse_pr_out_transport_id(
return false;
}
/*
* If the caller wants the TransportID Length, we set that value for the
* entire iSCSI Tarnsport ID now.
* Reconstruct the self-described TransportID length from the ADDITIONAL
* LENGTH field plus the 4-byte header. Reject it if it is below the
* spc4r17 section 7.5.4.6 minimum (ADDITIONAL LENGTH shall be at least
* 20) or if it runs past the bytes actually received, so that every
* access below stays inside the TransportID.
*/
if (out_tid_len) {
/* The shift works thanks to integer promotion rules */
*out_tid_len = get_unaligned_be16(&buf[2]);
/* Add four bytes for iSCSI Transport ID header */
*out_tid_len += 4;
}
tid_len = get_unaligned_be16(&buf[2]) + 4;
if (tid_len < 24 || tid_len > buf_len)
return false;
if (out_tid_len)
*out_tid_len = tid_len;
/*
* Check for ',i,0x' separator between iSCSI Name and iSCSI Initiator
@@ -332,16 +345,32 @@ static bool iscsi_parse_pr_out_transport_id(
* format.
*/
if (format_code == 0x40) {
p = strstr(&buf[4], ",i,0x");
p = strnstr(&buf[4], ",i,0x", tid_len - 4);
if (!p) {
pr_err("Unable to locate \",i,0x\" separator"
" for Initiator port identifier: %s\n",
&buf[4]);
pr_err("Unable to locate \",i,0x\" separator in iSCSI TransportID\n");
return false;
}
/*
* The iSCSI name runs from &buf[4] up to the separator; reject it
* if it cannot fit in i_str[TRANSPORT_IQN_LEN].
*/
if (p - &buf[4] >= TRANSPORT_IQN_LEN) {
pr_err("iSCSI Initiator port name too long in TransportID\n");
return false;
}
*p = '\0'; /* Terminate iSCSI Name */
p += 5; /* Skip over ",i,0x" separator */
/*
* The ISID must follow the separator. A ",i,0x" sitting at the
* very end of the TransportID leaves no ISID and would point the
* port nexus at buf + tid_len, i.e. past the descriptor, which
* the registration code then reads as the ISID string.
*/
if (p >= buf + tid_len) {
pr_err("Missing ISID in iSCSI Initiator port TransportID\n");
return false;
}
*port_nexus_ptr = p;
/*
* Go ahead and do the lower case conversion of the received
@@ -349,7 +378,7 @@ static bool iscsi_parse_pr_out_transport_id(
* for comparison against the running iSCSI session's ISID from
* iscsi_target.c:lio_sess_get_initiator_sid()
*/
for (i = 0; i < 12; i++) {
for (i = 0; i < 12 && p < buf + tid_len; i++) {
/*
* The first ISCSI INITIATOR SESSION ID field byte
* containing an ASCII null character terminates the
@@ -367,10 +396,22 @@ static bool iscsi_parse_pr_out_transport_id(
*p = tolower(*p);
p++;
}
} else
strscpy(i_str, &buf[4], TRANSPORT_IQN_LEN);
} else {
*port_nexus_ptr = NULL;
strscpy(i_str, &buf[4], TRANSPORT_IQN_LEN);
/*
* FORMAT CODE 00b: the name occupies buf[4..tid_len-1]. The
* declared length tid_len - 4 must fit in i_str[TRANSPORT_IQN_LEN].
* (For 01b the same tid_len bound would be over-restrictive: the
* descriptor also carries the separator and ISID, so a legal
* <=223-byte name gives tid_len up to 244.)
*/
if (tid_len - 4 >= TRANSPORT_IQN_LEN) {
pr_err("iSCSI Initiator port name too long in TransportID\n");
return false;
}
strscpy(i_str, &buf[4], tid_len - 4);
}
return true;
}
@@ -420,8 +461,16 @@ int target_get_pr_transport_id(struct se_node_acl *nacl,
}
bool target_parse_pr_out_transport_id(struct se_portal_group *tpg,
char *buf, u32 *out_tid_len, char **port_nexus_ptr, char *i_str)
char *buf, u32 buf_len, u32 *out_tid_len,
char **port_nexus_ptr, char *i_str)
{
/*
* The fixed-length SAS/SRP/FCP/SBP TransportIDs are 24 bytes; the iSCSI
* format is variable and bounds itself against buf_len below.
*/
if (tpg->proto_id != SCSI_PROTOCOL_ISCSI && buf_len < 24)
return false;
switch (tpg->proto_id) {
case SCSI_PROTOCOL_SAS:
/*
@@ -440,8 +489,8 @@ bool target_parse_pr_out_transport_id(struct se_portal_group *tpg,
sbp_parse_pr_out_transport_id(buf, i_str);
break;
case SCSI_PROTOCOL_ISCSI:
return iscsi_parse_pr_out_transport_id(tpg, buf, out_tid_len,
port_nexus_ptr, i_str);
return iscsi_parse_pr_out_transport_id(tpg, buf, buf_len,
out_tid_len, port_nexus_ptr, i_str);
default:
pr_err("Unknown proto_id: 0x%02x\n", tpg->proto_id);
return false;

View File

@@ -104,7 +104,8 @@ int target_get_pr_transport_id(struct se_node_acl *nacl,
struct t10_pr_registration *pr_reg, int *format_code,
unsigned char *buf);
bool target_parse_pr_out_transport_id(struct se_portal_group *tpg,
char *buf, u32 *out_tid_len, char **port_nexus_ptr, char *i_str);
char *buf, u32 buf_len, u32 *out_tid_len,
char **port_nexus_ptr, char *i_str);
/* target_core_hba.c */
struct se_hba *core_alloc_hba(const char *, u32, u32);

View File

@@ -1573,7 +1573,7 @@ core_scsi3_decode_spec_i_port(
iport_ptr = NULL;
tid_found = target_parse_pr_out_transport_id(tmp_tpg,
ptr, &tid_len, &iport_ptr, i_str);
ptr, tpdl, &tid_len, &iport_ptr, i_str);
if (!tid_found)
continue;
/*
@@ -3285,7 +3285,7 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key,
goto out;
}
tid_found = target_parse_pr_out_transport_id(dest_se_tpg,
&buf[24], &tmp_tid_len, &iport_ptr, initiator_str);
&buf[24], tid_len, &tmp_tid_len, &iport_ptr, initiator_str);
if (!tid_found) {
pr_err("SPC-3 PR REGISTER_AND_MOVE: Unable to locate"
" initiator_str from Transport ID\n");
@@ -3293,9 +3293,6 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key,
goto out;
}
transport_kunmap_data_sg(cmd);
buf = NULL;
pr_debug("SPC-3 PR [%s] Extracted initiator %s identifier: %s"
" %s\n", dest_tf_ops->fabric_name, (iport_ptr != NULL) ?
"port" : "device", initiator_str, (iport_ptr != NULL) ?
@@ -3532,6 +3529,11 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key,
core_scsi3_update_and_write_aptpl(cmd->se_dev, aptpl);
core_scsi3_put_pr_reg(dest_pr_reg);
/*
* iport_ptr aliases the PR-OUT parameter list mapped above, so the
* buffer is unmapped only here on success (and at out: on error).
*/
transport_kunmap_data_sg(cmd);
return 0;
out:
if (buf)

View File

@@ -611,6 +611,25 @@ static void scsiback_disconnect(struct vscsibk_info *info)
xenbus_unmap_ring_vfree(info->dev, info->ring.sring);
}
/*
* Send the error response for a request that did not reach the target core
* and return its tag. Free the tag before the response drops the v2p
* reference that keeps the session alive, and snapshot what the response
* needs since returning the tag can let the slot be reused.
*/
static void scsiback_resp_and_free(struct vscsibk_pend *pending_req,
int32_t result)
{
struct vscsibk_info *info = pending_req->info;
struct v2p_entry *v2p = pending_req->v2p;
struct se_session *se_sess = v2p->tpg->tpg_nexus->tvn_se_sess;
u16 rqid = pending_req->rqid;
target_free_tag(se_sess, &pending_req->se_cmd);
scsiback_send_response(info, NULL, result, 0, rqid);
kref_put(&v2p->kref, scsiback_free_translation_entry);
}
static void scsiback_device_action(struct vscsibk_pend *pending_req,
enum tcm_tmreq_table act, int tag)
{
@@ -639,7 +658,7 @@ static void scsiback_device_action(struct vscsibk_pend *pending_req,
return;
err:
scsiback_do_resp_with_sense(NULL, err, 0, pending_req);
scsiback_resp_and_free(pending_req, err);
}
/*
@@ -792,9 +811,8 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info,
case VSCSIIF_ACT_SCSI_CDB:
if (scsiback_gnttab_data_map(&ring_req, pending_req)) {
scsiback_fast_flush_area(pending_req);
scsiback_do_resp_with_sense(NULL,
DID_ERROR << 16, 0, pending_req);
transport_generic_free_cmd(&pending_req->se_cmd, 0);
scsiback_resp_and_free(pending_req,
DID_ERROR << 16);
} else {
scsiback_cmd_exec(pending_req);
}
@@ -808,9 +826,7 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info,
break;
default:
pr_err_ratelimited("invalid request\n");
scsiback_do_resp_with_sense(NULL, DID_ERROR << 16, 0,
pending_req);
transport_generic_free_cmd(&pending_req->se_cmd, 0);
scsiback_resp_and_free(pending_req, DID_ERROR << 16);
break;
}

View File

@@ -408,7 +408,6 @@ void scsi_attach_vpd(struct scsi_device *sdev);
void scsi_cdl_check(struct scsi_device *sdev);
int scsi_cdl_enable(struct scsi_device *sdev, bool enable);
extern struct scsi_device *scsi_device_from_queue(struct request_queue *q);
extern int __must_check scsi_device_get(struct scsi_device *);
extern void scsi_device_put(struct scsi_device *);
extern struct scsi_device *scsi_device_lookup(struct Scsi_Host *,

View File

@@ -750,6 +750,9 @@ struct Scsi_Host {
*/
struct device *dma_dev;
/* Used for an rcu-synchronizing eh wakeup */
struct work_struct eh_work;
/* Delay for runtime autosuspend */
int rpm_autosuspend_delay;