SCSI fixes on 20240606
The core change is to detect unusually large number of VPD pages (caused by device manufacturers having an endiannes issue) and reject them rather than trying to parse a huge non-existent array. The remaining fixes are in drivers the most user visible of which is the ALUA state transition recognition (leads to intermittent I/O errors in some situations otherwise). Signed-off-by: James E.J. Bottomley <James.Bottomley@HansenPartnership.com> -----BEGIN PGP SIGNATURE----- iJwEABMIAEQWIQTnYEDbdso9F2cI+arnQslM7pishQUCZmIHRiYcamFtZXMuYm90 dG9tbGV5QGhhbnNlbnBhcnRuZXJzaGlwLmNvbQAKCRDnQslM7pishcwfAQCS1dL9 j9tLAj5mol+WwsfcValZlpb/h5pKXsYrHZcxOQEA3A5DARsEgVEJMQpGuO4N+hcV 0lSmRV+iC85h5F08COI= =1+e/ -----END PGP SIGNATURE----- Merge tag 'scsi-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi Pull SCSI fixes from James Bottomley: "The core change is to detect unusually large number of VPD pages (caused by device manufacturers having an endiannes issue) and reject them rather than trying to parse a huge non-existent array. The remaining fixes are in drivers the most user visible of which is the ALUA state transition recognition (leads to intermittent I/O errors in some situations otherwise)" * tag 'scsi-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi: scsi: ufs: mcq: Fix error output and clean up ufshcd_mcq_abort() scsi: core: Handle devices which return an unusually large VPD page count scsi: mpt3sas: Add missing kerneldoc parameter descriptions scsi: qedf: Set qed_slowpath_params to zero before use scsi: qedf: Wait for stag work during unload scsi: qedf: Don't process stag work during unload and recovery scsi: sr: Fix unintentional arithmetic wraparound scsi: core: alua: I/O errors for ALUA state transitions scsi: mpi3mr: Use proper format specifier in mpi3mr_sas_port_add()
This commit is contained in:
commit
8a92980606
@ -217,7 +217,7 @@ current *struct* is::
|
||||
int (*media_changed)(struct cdrom_device_info *, int);
|
||||
int (*tray_move)(struct cdrom_device_info *, int);
|
||||
int (*lock_door)(struct cdrom_device_info *, int);
|
||||
int (*select_speed)(struct cdrom_device_info *, int);
|
||||
int (*select_speed)(struct cdrom_device_info *, unsigned long);
|
||||
int (*get_last_session) (struct cdrom_device_info *,
|
||||
struct cdrom_multisession *);
|
||||
int (*get_mcn)(struct cdrom_device_info *, struct cdrom_mcn *);
|
||||
@ -396,7 +396,7 @@ action need be taken, and the return value should be 0.
|
||||
|
||||
::
|
||||
|
||||
int select_speed(struct cdrom_device_info *cdi, int speed)
|
||||
int select_speed(struct cdrom_device_info *cdi, unsigned long speed)
|
||||
|
||||
Some CD-ROM drives are capable of changing their head-speed. There
|
||||
are several reasons for changing the speed of a CD-ROM drive. Badly
|
||||
|
@ -414,28 +414,40 @@ static char print_alua_state(unsigned char state)
|
||||
}
|
||||
}
|
||||
|
||||
static enum scsi_disposition alua_check_sense(struct scsi_device *sdev,
|
||||
struct scsi_sense_hdr *sense_hdr)
|
||||
static void alua_handle_state_transition(struct scsi_device *sdev)
|
||||
{
|
||||
struct alua_dh_data *h = sdev->handler_data;
|
||||
struct alua_port_group *pg;
|
||||
|
||||
rcu_read_lock();
|
||||
pg = rcu_dereference(h->pg);
|
||||
if (pg)
|
||||
pg->state = SCSI_ACCESS_STATE_TRANSITIONING;
|
||||
rcu_read_unlock();
|
||||
alua_check(sdev, false);
|
||||
}
|
||||
|
||||
static enum scsi_disposition alua_check_sense(struct scsi_device *sdev,
|
||||
struct scsi_sense_hdr *sense_hdr)
|
||||
{
|
||||
switch (sense_hdr->sense_key) {
|
||||
case NOT_READY:
|
||||
if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0a) {
|
||||
/*
|
||||
* LUN Not Accessible - ALUA state transition
|
||||
*/
|
||||
rcu_read_lock();
|
||||
pg = rcu_dereference(h->pg);
|
||||
if (pg)
|
||||
pg->state = SCSI_ACCESS_STATE_TRANSITIONING;
|
||||
rcu_read_unlock();
|
||||
alua_check(sdev, false);
|
||||
alua_handle_state_transition(sdev);
|
||||
return NEEDS_RETRY;
|
||||
}
|
||||
break;
|
||||
case UNIT_ATTENTION:
|
||||
if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0a) {
|
||||
/*
|
||||
* LUN Not Accessible - ALUA state transition
|
||||
*/
|
||||
alua_handle_state_transition(sdev);
|
||||
return NEEDS_RETRY;
|
||||
}
|
||||
if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00) {
|
||||
/*
|
||||
* Power On, Reset, or Bus Device Reset.
|
||||
@ -502,7 +514,8 @@ static int alua_tur(struct scsi_device *sdev)
|
||||
|
||||
retval = scsi_test_unit_ready(sdev, ALUA_FAILOVER_TIMEOUT * HZ,
|
||||
ALUA_FAILOVER_RETRIES, &sense_hdr);
|
||||
if (sense_hdr.sense_key == NOT_READY &&
|
||||
if ((sense_hdr.sense_key == NOT_READY ||
|
||||
sense_hdr.sense_key == UNIT_ATTENTION) &&
|
||||
sense_hdr.asc == 0x04 && sense_hdr.ascq == 0x0a)
|
||||
return SCSI_DH_RETRY;
|
||||
else if (retval)
|
||||
|
@ -1364,7 +1364,7 @@ static struct mpi3mr_sas_port *mpi3mr_sas_port_add(struct mpi3mr_ioc *mrioc,
|
||||
continue;
|
||||
|
||||
if (i > sizeof(mr_sas_port->phy_mask) * 8) {
|
||||
ioc_warn(mrioc, "skipping port %u, max allowed value is %lu\n",
|
||||
ioc_warn(mrioc, "skipping port %u, max allowed value is %zu\n",
|
||||
i, sizeof(mr_sas_port->phy_mask) * 8);
|
||||
goto out_fail;
|
||||
}
|
||||
|
@ -302,8 +302,8 @@ struct _scsi_io_transfer {
|
||||
|
||||
/**
|
||||
* _scsih_set_debug_level - global setting of ioc->logging_level.
|
||||
* @val: ?
|
||||
* @kp: ?
|
||||
* @val: value of the parameter to be set
|
||||
* @kp: pointer to kernel_param structure
|
||||
*
|
||||
* Note: The logging levels are defined in mpt3sas_debug.h.
|
||||
*/
|
||||
|
@ -363,6 +363,7 @@ struct qedf_ctx {
|
||||
#define QEDF_IN_RECOVERY 5
|
||||
#define QEDF_DBG_STOP_IO 6
|
||||
#define QEDF_PROBING 8
|
||||
#define QEDF_STAG_IN_PROGRESS 9
|
||||
unsigned long flags; /* Miscellaneous state flags */
|
||||
int fipvlan_retries;
|
||||
u8 num_queues;
|
||||
|
@ -318,11 +318,18 @@ static struct fc_seq *qedf_elsct_send(struct fc_lport *lport, u32 did,
|
||||
*/
|
||||
if (resp == fc_lport_flogi_resp) {
|
||||
qedf->flogi_cnt++;
|
||||
qedf->flogi_pending++;
|
||||
|
||||
if (test_bit(QEDF_UNLOADING, &qedf->flags)) {
|
||||
QEDF_ERR(&qedf->dbg_ctx, "Driver unloading\n");
|
||||
qedf->flogi_pending = 0;
|
||||
}
|
||||
|
||||
if (qedf->flogi_pending >= QEDF_FLOGI_RETRY_CNT) {
|
||||
schedule_delayed_work(&qedf->stag_work, 2);
|
||||
return NULL;
|
||||
}
|
||||
qedf->flogi_pending++;
|
||||
|
||||
return fc_elsct_send(lport, did, fp, op, qedf_flogi_resp,
|
||||
arg, timeout);
|
||||
}
|
||||
@ -912,13 +919,14 @@ void qedf_ctx_soft_reset(struct fc_lport *lport)
|
||||
struct qedf_ctx *qedf;
|
||||
struct qed_link_output if_link;
|
||||
|
||||
qedf = lport_priv(lport);
|
||||
|
||||
if (lport->vport) {
|
||||
clear_bit(QEDF_STAG_IN_PROGRESS, &qedf->flags);
|
||||
printk_ratelimited("Cannot issue host reset on NPIV port.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
qedf = lport_priv(lport);
|
||||
|
||||
qedf->flogi_pending = 0;
|
||||
/* For host reset, essentially do a soft link up/down */
|
||||
atomic_set(&qedf->link_state, QEDF_LINK_DOWN);
|
||||
@ -938,6 +946,7 @@ void qedf_ctx_soft_reset(struct fc_lport *lport)
|
||||
if (!if_link.link_up) {
|
||||
QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC,
|
||||
"Physical link is not up.\n");
|
||||
clear_bit(QEDF_STAG_IN_PROGRESS, &qedf->flags);
|
||||
return;
|
||||
}
|
||||
/* Flush and wait to make sure link down is processed */
|
||||
@ -950,6 +959,7 @@ void qedf_ctx_soft_reset(struct fc_lport *lport)
|
||||
"Queue link up work.\n");
|
||||
queue_delayed_work(qedf->link_update_wq, &qedf->link_update,
|
||||
0);
|
||||
clear_bit(QEDF_STAG_IN_PROGRESS, &qedf->flags);
|
||||
}
|
||||
|
||||
/* Reset the host by gracefully logging out and then logging back in */
|
||||
@ -3463,6 +3473,7 @@ retry_probe:
|
||||
}
|
||||
|
||||
/* Start the Slowpath-process */
|
||||
memset(&slowpath_params, 0, sizeof(struct qed_slowpath_params));
|
||||
slowpath_params.int_mode = QED_INT_MODE_MSIX;
|
||||
slowpath_params.drv_major = QEDF_DRIVER_MAJOR_VER;
|
||||
slowpath_params.drv_minor = QEDF_DRIVER_MINOR_VER;
|
||||
@ -3721,6 +3732,7 @@ static void __qedf_remove(struct pci_dev *pdev, int mode)
|
||||
{
|
||||
struct qedf_ctx *qedf;
|
||||
int rc;
|
||||
int cnt = 0;
|
||||
|
||||
if (!pdev) {
|
||||
QEDF_ERR(NULL, "pdev is NULL.\n");
|
||||
@ -3738,6 +3750,17 @@ static void __qedf_remove(struct pci_dev *pdev, int mode)
|
||||
return;
|
||||
}
|
||||
|
||||
stag_in_prog:
|
||||
if (test_bit(QEDF_STAG_IN_PROGRESS, &qedf->flags)) {
|
||||
QEDF_ERR(&qedf->dbg_ctx, "Stag in progress, cnt=%d.\n", cnt);
|
||||
cnt++;
|
||||
|
||||
if (cnt < 5) {
|
||||
msleep(500);
|
||||
goto stag_in_prog;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode != QEDF_MODE_RECOVERY)
|
||||
set_bit(QEDF_UNLOADING, &qedf->flags);
|
||||
|
||||
@ -3997,6 +4020,24 @@ void qedf_stag_change_work(struct work_struct *work)
|
||||
struct qedf_ctx *qedf =
|
||||
container_of(work, struct qedf_ctx, stag_work.work);
|
||||
|
||||
if (!qedf) {
|
||||
QEDF_ERR(&qedf->dbg_ctx, "qedf is NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
if (test_bit(QEDF_IN_RECOVERY, &qedf->flags)) {
|
||||
QEDF_ERR(&qedf->dbg_ctx,
|
||||
"Already is in recovery, hence not calling software context reset.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (test_bit(QEDF_UNLOADING, &qedf->flags)) {
|
||||
QEDF_ERR(&qedf->dbg_ctx, "Driver unloading\n");
|
||||
return;
|
||||
}
|
||||
|
||||
set_bit(QEDF_STAG_IN_PROGRESS, &qedf->flags);
|
||||
|
||||
printk_ratelimited("[%s]:[%s:%d]:%d: Performing software context reset.",
|
||||
dev_name(&qedf->pdev->dev), __func__, __LINE__,
|
||||
qedf->dbg_ctx.host_no);
|
||||
|
@ -350,6 +350,13 @@ static int scsi_get_vpd_size(struct scsi_device *sdev, u8 page)
|
||||
if (result < SCSI_VPD_HEADER_SIZE)
|
||||
return 0;
|
||||
|
||||
if (result > sizeof(vpd)) {
|
||||
dev_warn_once(&sdev->sdev_gendev,
|
||||
"%s: long VPD page 0 length: %d bytes\n",
|
||||
__func__, result);
|
||||
result = sizeof(vpd);
|
||||
}
|
||||
|
||||
result -= SCSI_VPD_HEADER_SIZE;
|
||||
if (!memchr(&vpd[SCSI_VPD_HEADER_SIZE], page, result))
|
||||
return 0;
|
||||
|
@ -65,7 +65,7 @@ int sr_disk_status(struct cdrom_device_info *);
|
||||
int sr_get_last_session(struct cdrom_device_info *, struct cdrom_multisession *);
|
||||
int sr_get_mcn(struct cdrom_device_info *, struct cdrom_mcn *);
|
||||
int sr_reset(struct cdrom_device_info *);
|
||||
int sr_select_speed(struct cdrom_device_info *cdi, int speed);
|
||||
int sr_select_speed(struct cdrom_device_info *cdi, unsigned long speed);
|
||||
int sr_audio_ioctl(struct cdrom_device_info *, unsigned int, void *);
|
||||
|
||||
int sr_is_xa(Scsi_CD *);
|
||||
|
@ -425,11 +425,14 @@ int sr_reset(struct cdrom_device_info *cdi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sr_select_speed(struct cdrom_device_info *cdi, int speed)
|
||||
int sr_select_speed(struct cdrom_device_info *cdi, unsigned long speed)
|
||||
{
|
||||
Scsi_CD *cd = cdi->handle;
|
||||
struct packet_command cgc;
|
||||
|
||||
/* avoid exceeding the max speed or overflowing integer bounds */
|
||||
speed = clamp(0, speed, 0xffff / 177);
|
||||
|
||||
if (speed == 0)
|
||||
speed = 0xffff; /* set to max */
|
||||
else
|
||||
|
@ -634,20 +634,20 @@ int ufshcd_mcq_abort(struct scsi_cmnd *cmd)
|
||||
struct ufshcd_lrb *lrbp = &hba->lrb[tag];
|
||||
struct ufs_hw_queue *hwq;
|
||||
unsigned long flags;
|
||||
int err = FAILED;
|
||||
int err;
|
||||
|
||||
if (!ufshcd_cmd_inflight(lrbp->cmd)) {
|
||||
dev_err(hba->dev,
|
||||
"%s: skip abort. cmd at tag %d already completed.\n",
|
||||
__func__, tag);
|
||||
goto out;
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
/* Skip task abort in case previous aborts failed and report failure */
|
||||
if (lrbp->req_abort_skip) {
|
||||
dev_err(hba->dev, "%s: skip abort. tag %d failed earlier\n",
|
||||
__func__, tag);
|
||||
goto out;
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
hwq = ufshcd_mcq_req_to_hwq(hba, scsi_cmd_to_rq(cmd));
|
||||
@ -659,7 +659,7 @@ int ufshcd_mcq_abort(struct scsi_cmnd *cmd)
|
||||
*/
|
||||
dev_err(hba->dev, "%s: cmd found in sq. hwq=%d, tag=%d\n",
|
||||
__func__, hwq->id, tag);
|
||||
goto out;
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -667,18 +667,17 @@ int ufshcd_mcq_abort(struct scsi_cmnd *cmd)
|
||||
* in the completion queue either. Query the device to see if
|
||||
* the command is being processed in the device.
|
||||
*/
|
||||
if (ufshcd_try_to_abort_task(hba, tag)) {
|
||||
err = ufshcd_try_to_abort_task(hba, tag);
|
||||
if (err) {
|
||||
dev_err(hba->dev, "%s: device abort failed %d\n", __func__, err);
|
||||
lrbp->req_abort_skip = true;
|
||||
goto out;
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
err = SUCCESS;
|
||||
spin_lock_irqsave(&hwq->cq_lock, flags);
|
||||
if (ufshcd_cmd_inflight(lrbp->cmd))
|
||||
ufshcd_release_scsi_cmd(hba, lrbp);
|
||||
spin_unlock_irqrestore(&hwq->cq_lock, flags);
|
||||
|
||||
out:
|
||||
return err;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ struct cdrom_device_ops {
|
||||
unsigned int clearing, int slot);
|
||||
int (*tray_move) (struct cdrom_device_info *, int);
|
||||
int (*lock_door) (struct cdrom_device_info *, int);
|
||||
int (*select_speed) (struct cdrom_device_info *, int);
|
||||
int (*select_speed) (struct cdrom_device_info *, unsigned long);
|
||||
int (*get_last_session) (struct cdrom_device_info *,
|
||||
struct cdrom_multisession *);
|
||||
int (*get_mcn) (struct cdrom_device_info *,
|
||||
|
Loading…
x
Reference in New Issue
Block a user