diff --git a/drivers/scsi/BusLogic.c b/drivers/scsi/BusLogic.c index 27b446c0c5d0..4179f29b3066 100644 --- a/drivers/scsi/BusLogic.c +++ b/drivers/scsi/BusLogic.c @@ -276,8 +276,10 @@ static void blogic_create_addlccbs(struct blogic_adapter *adapter, Lock should already have been acquired by the caller. */ -static struct blogic_ccb *blogic_alloc_ccb(struct blogic_adapter *adapter) +static struct blogic_ccb *blogic_alloc_ccb(struct Scsi_Host *host) + __must_hold(&host->host_lock) { + struct blogic_adapter *adapter = (void *)host->hostdata; static unsigned long serial; struct blogic_ccb *ccb; ccb = adapter->free_ccbs; @@ -307,7 +309,9 @@ static struct blogic_ccb *blogic_alloc_ccb(struct blogic_adapter *adapter) caller. */ -static void blogic_dealloc_ccb(struct blogic_ccb *ccb, int dma_unmap) +static void blogic_dealloc_ccb(struct Scsi_Host *host, struct blogic_ccb *ccb, + int dma_unmap) + __must_hold(&host->host_lock) { struct blogic_adapter *adapter = ccb->adapter; @@ -2596,8 +2600,11 @@ static void blogic_scan_inbox(struct blogic_adapter *adapter) should already have been acquired by the caller. */ -static void blogic_process_ccbs(struct blogic_adapter *adapter) +static void blogic_process_ccbs(struct Scsi_Host *host) + __must_hold(&host->host_lock) { + struct blogic_adapter *adapter = (void *)host->hostdata; + if (adapter->processing_ccbs) return; adapter->processing_ccbs = true; @@ -2621,7 +2628,7 @@ static void blogic_process_ccbs(struct blogic_adapter *adapter) /* Place CCB back on the Host Adapter's free list. */ - blogic_dealloc_ccb(ccb, 1); + blogic_dealloc_ccb(host, ccb, 1); #if 0 /* this needs to be redone different for new EH */ /* Bus Device Reset CCBs have the command field @@ -2650,7 +2657,7 @@ static void blogic_process_ccbs(struct blogic_adapter *adapter) if (ccb->status == BLOGIC_CCB_RESET && ccb->tgt_id == tgt_id) { command = ccb->command; - blogic_dealloc_ccb(ccb, 1); + blogic_dealloc_ccb(host, ccb, 1); adapter->active_cmds[tgt_id]--; command->result = DID_RESET << 16; scsi_done(command); @@ -2721,7 +2728,7 @@ static void blogic_process_ccbs(struct blogic_adapter *adapter) /* Place CCB back on the Host Adapter's free list. */ - blogic_dealloc_ccb(ccb, 1); + blogic_dealloc_ccb(host, ccb, 1); /* Call the SCSI Command Completion Routine. */ @@ -2740,11 +2747,12 @@ static void blogic_process_ccbs(struct blogic_adapter *adapter) static irqreturn_t blogic_inthandler(int irq_ch, void *devid) { struct blogic_adapter *adapter = (struct blogic_adapter *) devid; + struct Scsi_Host *host = adapter->scsi_host; unsigned long processor_flag; /* Acquire exclusive access to Host Adapter. */ - spin_lock_irqsave(&adapter->scsi_host->host_lock, processor_flag); + spin_lock_irqsave(&host->host_lock, processor_flag); /* Handle Interrupts appropriately for each Host Adapter type. */ @@ -2794,25 +2802,25 @@ static irqreturn_t blogic_inthandler(int irq_ch, void *devid) Process any completed CCBs. */ if (adapter->firstccb != NULL) - blogic_process_ccbs(adapter); + blogic_process_ccbs(host); /* Reset the Host Adapter if requested. */ if (adapter->adapter_extreset) { blogic_warn("Resetting %s due to External SCSI Bus Reset\n", adapter, adapter->full_model); blogic_inc_count(&adapter->ext_resets); - blogic_resetadapter(adapter, false); + blogic_resetadapter(host, false); adapter->adapter_extreset = false; } else if (adapter->adapter_intern_err) { blogic_warn("Resetting %s due to Host Adapter Internal Error\n", adapter, adapter->full_model); blogic_inc_count(&adapter->adapter_intern_errors); - blogic_resetadapter(adapter, true); + blogic_resetadapter(host, true); adapter->adapter_intern_err = false; } /* Release exclusive access to Host Adapter. */ - spin_unlock_irqrestore(&adapter->scsi_host->host_lock, processor_flag); + spin_unlock_irqrestore(&host->host_lock, processor_flag); return IRQ_HANDLED; } @@ -2823,9 +2831,11 @@ static irqreturn_t blogic_inthandler(int irq_ch, void *devid) already have been acquired by the caller. */ -static bool blogic_write_outbox(struct blogic_adapter *adapter, +static bool blogic_write_outbox(struct Scsi_Host *host, enum blogic_action action, struct blogic_ccb *ccb) + __must_hold(&host->host_lock) { + struct blogic_adapter *adapter = (void *)host->hostdata; struct blogic_outbox *next_outbox; next_outbox = adapter->next_outbox; @@ -2859,17 +2869,16 @@ static int blogic_hostreset(struct scsi_cmnd *SCpnt) { struct blogic_adapter *adapter = (struct blogic_adapter *) SCpnt->device->host->hostdata; - + struct Scsi_Host *shost = adapter->scsi_host; unsigned int id = SCpnt->device->id; struct blogic_tgt_stats *stats = &adapter->tgt_stats[id]; int rc; - spin_lock_irq(&SCpnt->device->host->host_lock); - + spin_lock_irq(&shost->host_lock); blogic_inc_count(&stats->adapter_reset_req); + rc = blogic_resetadapter(shost, false); + spin_unlock_irq(&shost->host_lock); - rc = blogic_resetadapter(adapter, false); - spin_unlock_irq(&SCpnt->device->host->host_lock); return rc; } @@ -2878,11 +2887,12 @@ static int blogic_hostreset(struct scsi_cmnd *SCpnt) Outgoing Mailbox for execution by the associated Host Adapter. */ -static enum scsi_qc_status blogic_qcmd_lck(struct scsi_cmnd *command) +static enum scsi_qc_status blogic_qcmd_lck(struct Scsi_Host *shost, struct scsi_cmnd *command) + __must_hold(&shost->host_lock) { void (*comp_cb)(struct scsi_cmnd *) = scsi_done; struct blogic_adapter *adapter = - (struct blogic_adapter *) command->device->host->hostdata; + (struct blogic_adapter *) shost->hostdata; struct blogic_tgt_flags *tgt_flags = &adapter->tgt_flags[command->device->id]; struct blogic_tgt_stats *tgt_stats = adapter->tgt_stats; @@ -2913,12 +2923,12 @@ static enum scsi_qc_status blogic_qcmd_lck(struct scsi_cmnd *command) probably hung so signal an error as a Host Adapter Hard Reset should be initiated soon. */ - ccb = blogic_alloc_ccb(adapter); + ccb = blogic_alloc_ccb(shost); if (ccb == NULL) { - spin_unlock_irq(&adapter->scsi_host->host_lock); + spin_unlock_irq(&shost->host_lock); blogic_delay(1); - spin_lock_irq(&adapter->scsi_host->host_lock); - ccb = blogic_alloc_ccb(adapter); + spin_lock_irq(&shost->host_lock); + ccb = blogic_alloc_ccb(shost); if (ccb == NULL) { command->result = DID_ERROR << 16; comp_cb(command); @@ -3046,7 +3056,7 @@ static enum scsi_qc_status blogic_qcmd_lck(struct scsi_cmnd *command) if (dma_mapping_error(&adapter->pci_device->dev, sense_buf)) { blogic_err("DMA mapping for sense data buffer failed\n", adapter); - blogic_dealloc_ccb(ccb, 0); + blogic_dealloc_ccb(shost, ccb, 0); return SCSI_MLQUEUE_HOST_BUSY; } ccb->sensedata = sense_buf; @@ -3061,15 +3071,15 @@ static enum scsi_qc_status blogic_qcmd_lck(struct scsi_cmnd *command) so signal an error as a Host Adapter Hard Reset should be initiated soon. */ - if (!blogic_write_outbox(adapter, BLOGIC_MBOX_START, ccb)) { - spin_unlock_irq(&adapter->scsi_host->host_lock); + if (!blogic_write_outbox(shost, BLOGIC_MBOX_START, ccb)) { + spin_unlock_irq(&shost->host_lock); blogic_warn("Unable to write Outgoing Mailbox - Pausing for 1 second\n", adapter); blogic_delay(1); - spin_lock_irq(&adapter->scsi_host->host_lock); - if (!blogic_write_outbox(adapter, BLOGIC_MBOX_START, + spin_lock_irq(&shost->host_lock); + if (!blogic_write_outbox(shost, BLOGIC_MBOX_START, ccb)) { blogic_warn("Still unable to write Outgoing Mailbox - Host Adapter Dead?\n", adapter); - blogic_dealloc_ccb(ccb, 1); + blogic_dealloc_ccb(shost, ccb, 1); command->result = DID_ERROR << 16; scsi_done(command); } @@ -3089,12 +3099,20 @@ static enum scsi_qc_status blogic_qcmd_lck(struct scsi_cmnd *command) pending. */ if (ccb->status == BLOGIC_CCB_COMPLETE) - blogic_process_ccbs(adapter); + blogic_process_ccbs(shost); } return 0; } -static DEF_SCSI_QCMD(blogic_qcmd) +static enum scsi_qc_status blogic_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *cmd) +{ + enum scsi_qc_status rc; + + spin_lock_irq(&shost->host_lock); + rc = blogic_qcmd_lck(shost, cmd); + spin_unlock_irq(&shost->host_lock); + return rc; +} #if 0 /* @@ -3103,9 +3121,8 @@ static DEF_SCSI_QCMD(blogic_qcmd) static int blogic_abort(struct scsi_cmnd *command) { - struct blogic_adapter *adapter = - (struct blogic_adapter *) command->device->host->hostdata; - + struct Scsi_Host *const shost = command->device->host; + struct blogic_adapter *adapter = (void *) shost->hostdata; int tgt_id = command->device->id; struct blogic_ccb *ccb; blogic_inc_count(&adapter->tgt_stats[tgt_id].aborts_request); @@ -3144,7 +3161,7 @@ static int blogic_abort(struct scsi_cmnd *command) adapter->fw_ver[0] < '5') { blogic_warn("Unable to Abort CCB #%ld to Target %d - Abort Tag Not Supported\n", adapter, ccb->serial, tgt_id); return FAILURE; - } else if (blogic_write_outbox(adapter, BLOGIC_MBOX_ABORT, + } else if (blogic_write_outbox(shost, BLOGIC_MBOX_ABORT, ccb)) { blogic_warn("Aborting CCB #%ld to Target %d\n", adapter, ccb->serial, tgt_id); @@ -3169,7 +3186,7 @@ static int blogic_abort(struct scsi_cmnd *command) may still be pending. */ if (ccb->status == BLOGIC_CCB_COMPLETE) - blogic_process_ccbs(adapter); + blogic_process_ccbs(shost); return SUCCESS; } return SUCCESS; @@ -3181,8 +3198,10 @@ static int blogic_abort(struct scsi_cmnd *command) currently executing SCSI Commands as having been Reset. */ -static int blogic_resetadapter(struct blogic_adapter *adapter, bool hard_reset) +static int blogic_resetadapter(struct Scsi_Host *host, bool hard_reset) + __must_hold(&host->host_lock) { + struct blogic_adapter *adapter = (void *)host->hostdata; struct blogic_ccb *ccb; int tgt_id; @@ -3203,7 +3222,7 @@ static int blogic_resetadapter(struct blogic_adapter *adapter, bool hard_reset) for (ccb = adapter->all_ccbs; ccb != NULL; ccb = ccb->next_all) if (ccb->status == BLOGIC_CCB_ACTIVE) - blogic_dealloc_ccb(ccb, 1); + blogic_dealloc_ccb(host, ccb, 1); /* * Wait a few seconds between the Host Adapter Hard Reset which * initiates a SCSI Bus Reset and issuing any SCSI Commands. Some @@ -3212,9 +3231,9 @@ static int blogic_resetadapter(struct blogic_adapter *adapter, bool hard_reset) */ if (hard_reset) { - spin_unlock_irq(&adapter->scsi_host->host_lock); + spin_unlock_irq(&host->host_lock); blogic_delay(adapter->bus_settle_time); - spin_lock_irq(&adapter->scsi_host->host_lock); + spin_lock_irq(&host->host_lock); } for (tgt_id = 0; tgt_id < adapter->maxdev; tgt_id++) { diff --git a/drivers/scsi/BusLogic.h b/drivers/scsi/BusLogic.h index 24697a5bedc8..455d442525f6 100644 --- a/drivers/scsi/BusLogic.h +++ b/drivers/scsi/BusLogic.h @@ -1279,7 +1279,8 @@ static int blogic_sdev_configure(struct scsi_device *, struct queue_limits *lim); static void blogic_qcompleted_ccb(struct blogic_ccb *); static irqreturn_t blogic_inthandler(int, void *); -static int blogic_resetadapter(struct blogic_adapter *, bool hard_reset); +static int blogic_resetadapter(struct Scsi_Host *host, bool hard_reset) + __must_hold(&host->host_lock); static void blogic_msg(enum blogic_msglevel, char *, struct blogic_adapter *, ...); static int __init blogic_setup(char *); diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 1eec66195cf4..90b65bebec70 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -569,7 +569,8 @@ config SCSI_MYRS config VMWARE_PVSCSI tristate "VMware PVSCSI driver support" - depends on PCI && SCSI && X86 + depends on PCI && SCSI + depends on X86 || (ARM64 && !CPU_BIG_ENDIAN && PAGE_SIZE_4KB) help This driver supports VMware's para virtualized SCSI HBA. To compile this driver as a module, choose M here: the diff --git a/drivers/scsi/aha1542.c b/drivers/scsi/aha1542.c index 7ea38d509072..8191d258f162 100644 --- a/drivers/scsi/aha1542.c +++ b/drivers/scsi/aha1542.c @@ -271,8 +271,6 @@ static void aha1542_free_cmd(struct scsi_cmnd *cmd) buf += bv.bv_len; } } - - scsi_dma_unmap(cmd); } static irqreturn_t aha1542_interrupt(int irq, void *dev_id) diff --git a/drivers/scsi/aic7xxx/aic79xx_core.c b/drivers/scsi/aic7xxx/aic79xx_core.c index 3602a1fc40e4..4abc7948187f 100644 --- a/drivers/scsi/aic7xxx/aic79xx_core.c +++ b/drivers/scsi/aic7xxx/aic79xx_core.c @@ -8942,8 +8942,10 @@ ahd_handle_scsi_status(struct ahd_softc *ahd, struct scb *scb) } #endif - if (ahd_perform_autosense(scb) == 0) + if (ahd_perform_autosense(scb) == 0) { + ahd_done(ahd, scb); break; + } ahd_compile_devinfo(&devinfo, SCB_GET_OUR_ID(scb), SCB_GET_TARGET(ahd, scb), diff --git a/drivers/scsi/aic7xxx/aic79xx_osm.h b/drivers/scsi/aic7xxx/aic79xx_osm.h index c6eb2b97e929..1facfd238f89 100644 --- a/drivers/scsi/aic7xxx/aic79xx_osm.h +++ b/drivers/scsi/aic7xxx/aic79xx_osm.h @@ -501,6 +501,8 @@ int ahd_linux_show_info(struct seq_file *,struct Scsi_Host *); static inline void ahd_cmd_set_transaction_status(struct scsi_cmnd *cmd, uint32_t status) { + if (!cmd) + return; cmd->result &= ~(CAM_STATUS_MASK << 16); cmd->result |= status << 16; } @@ -514,6 +516,8 @@ void ahd_set_transaction_status(struct scb *scb, uint32_t status) static inline void ahd_cmd_set_scsi_status(struct scsi_cmnd *cmd, uint32_t status) { + if (!cmd) + return; cmd->result &= ~0xFFFF; cmd->result |= status; } @@ -527,6 +531,8 @@ void ahd_set_scsi_status(struct scb *scb, uint32_t status) static inline uint32_t ahd_cmd_get_transaction_status(struct scsi_cmnd *cmd) { + if (!cmd) + return CAM_REQ_INPROG; return ((cmd->result >> 16) & CAM_STATUS_MASK); } @@ -539,6 +545,8 @@ uint32_t ahd_get_transaction_status(struct scb *scb) static inline uint32_t ahd_cmd_get_scsi_status(struct scsi_cmnd *cmd) { + if (!cmd) + return SAM_STAT_GOOD; return (cmd->result & 0xFFFF); } @@ -566,13 +574,16 @@ u_long ahd_get_transfer_length(struct scb *scb) static inline int ahd_get_transfer_dir(struct scb *scb) { + if (!scb->io_ctx) + return DMA_NONE; return (scb->io_ctx->sc_data_direction); } static inline void ahd_set_residual(struct scb *scb, u_long resid) { - scsi_set_resid(scb->io_ctx, resid); + if (scb->io_ctx) + scsi_set_resid(scb->io_ctx, resid); } static inline @@ -584,6 +595,8 @@ void ahd_set_sense_residual(struct scb *scb, u_long resid) static inline u_long ahd_get_residual(struct scb *scb) { + if (!scb->io_ctx) + return 0; return scsi_get_resid(scb->io_ctx); } @@ -597,11 +610,12 @@ static inline int ahd_perform_autosense(struct scb *scb) { /* - * We always perform autosense in Linux. - * On other platforms this is set on a - * per-transaction basis. + * We always perform autosense in Linux, except for the device + * reset SCB, which has no command to return sense data to. */ - return (1); + if (!scb->io_ctx) + return 0; + return 1; } static inline uint32_t @@ -631,6 +645,8 @@ void ahd_platform_freeze_devq(struct ahd_softc *ahd, struct scb *scb); static inline void ahd_freeze_scb(struct scb *scb) { + if (!scb->io_ctx) + return; if ((scb->io_ctx->result & (CAM_DEV_QFRZN << 16)) == 0) { scb->io_ctx->result |= CAM_DEV_QFRZN << 16; scb->platform_data->dev->qfrozen++; diff --git a/drivers/scsi/aic7xxx/aic7xxx_core.c b/drivers/scsi/aic7xxx/aic7xxx_core.c index b9761f9f0bbc..af6115bfb280 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_core.c +++ b/drivers/scsi/aic7xxx/aic7xxx_core.c @@ -1097,7 +1097,7 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat) sc->byte2 = SCB_GET_LUN(scb) << 5; sc->unused[0] = 0; sc->unused[1] = 0; - sc->length = sg->len; + sc->length = ahc_get_sense_bufsize(ahc, scb); sc->control = 0; /* @@ -5266,9 +5266,13 @@ ahc_init(struct ahc_softc *ahc) /* * Assume we have a board at this stage and it has been reset. + * PCI controllers keep the host ID ahc_pci_config() chose. */ - if ((ahc->flags & AHC_USEDEFAULTS) != 0) - ahc->our_id = ahc->our_id_b = 7; + if ((ahc->flags & AHC_USEDEFAULTS) != 0) { + if ((ahc->chip & AHC_PCI) == 0) + ahc->our_id = 7; + ahc->our_id_b = 7; + } /* * Default to allowing initiator operations. diff --git a/drivers/scsi/aic7xxx/aic7xxx_pci.c b/drivers/scsi/aic7xxx/aic7xxx_pci.c index 2d4c85426dc3..5a04c7d55e4b 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_pci.c +++ b/drivers/scsi/aic7xxx/aic7xxx_pci.c @@ -701,6 +701,21 @@ ahc_find_pci_device(ahc_dev_softc_t pci) return (NULL); } +/* + * The SGI O2 firmware runs its onboard controllers, in slots 1 and 2 of the + * root bus, without the Adaptec BIOS and at host ID 0. It leaves no settings + * in scratch RAM, which may still hold power-on contents with bad parity. + */ +static bool +ahc_pci_sgi_o2_onboard(struct ahc_softc *ahc) +{ + int slot = ahc_get_pci_slot(ahc->dev_softc); + + return IS_ENABLED(CONFIG_SGI_IP32) && + ahc_get_pci_bus(ahc->dev_softc) == 0 && + (slot == 1 || slot == 2); +} + int ahc_pci_config(struct ahc_softc *ahc, const struct ahc_pci_identity *entry) { @@ -782,6 +797,9 @@ ahc_pci_config(struct ahc_softc *ahc, const struct ahc_pci_identity *entry) ahc->bus_intr = ahc_pci_intr; ahc->bus_chip_init = ahc_pci_chip_init; + if (ahc_pci_sgi_o2_onboard(ahc)) + ahc->flags |= AHC_NO_BIOS_INIT; + /* Remember how the card was setup in case there is no SEEPROM */ if ((ahc_inb(ahc, HCNTRL) & POWRDN) == 0) { ahc_pause(ahc); @@ -898,7 +916,7 @@ ahc_pci_config(struct ahc_softc *ahc, const struct ahc_pci_identity *entry) * Assume only one connector and always turn * on termination. */ - our_id = 0x07; + our_id = ahc_pci_sgi_o2_onboard(ahc) ? 0 : 0x07; sxfrctl1 = STPWEN; } ahc_outb(ahc, SCSICONF, our_id|ENSPCHK|RESET_SCSI); diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c index 742534c99386..0986a6e2a6ae 100644 --- a/drivers/scsi/device_handler/scsi_dh_rdac.c +++ b/drivers/scsi/device_handler/scsi_dh_rdac.c @@ -450,14 +450,14 @@ static int initialize_controller(struct scsi_device *sdev, spin_lock(&list_lock); h->ctlr = get_controller(index, array_name, array_id, sdev); - if (!h->ctlr) + if (!h->ctlr) { err = SCSI_DH_RES_TEMP_UNAVAIL; - else { + } else { h->sdev = sdev; list_add_rcu(&h->node, &h->ctlr->dh_list); + err = SCSI_DH_OK; } spin_unlock(&list_lock); - err = SCSI_DH_OK; } return err; } diff --git a/drivers/scsi/leapraid/leapraid_os.c b/drivers/scsi/leapraid/leapraid_os.c index da597eb99377..749e50366dbc 100644 --- a/drivers/scsi/leapraid/leapraid_os.c +++ b/drivers/scsi/leapraid/leapraid_os.c @@ -935,22 +935,16 @@ static int leapraid_init_cmd_priv(struct Scsi_Host *shost, struct scsi_cmnd *scmd) { struct leapraid_adapter *adapter = shost_priv(shost); - struct leapraid_io_req_tracker *io_tracker; - io_tracker = scsi_cmd_priv(scmd); - leapraid_internal_init_cmd_priv(adapter, io_tracker); - - return 0; + return leapraid_internal_init_cmd_priv(adapter, scsi_cmd_priv(scmd)); } static int leapraid_exit_cmd_priv(struct Scsi_Host *shost, struct scsi_cmnd *scmd) { struct leapraid_adapter *adapter = shost_priv(shost); - struct leapraid_io_req_tracker *io_tracker; - io_tracker = scsi_cmd_priv(scmd); - leapraid_internal_exit_cmd_priv(adapter, io_tracker); + leapraid_internal_exit_cmd_priv(adapter, scsi_cmd_priv(scmd)); return 0; } diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 2340790c9f6b..8f5f1ef6f619 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -372,6 +372,7 @@ static int local_ata_check_ready(struct ata_link *link) } } +__printf(3, 4) static int sas_ata_printk(const char *level, const struct domain_device *ddev, const char *fmt, ...) { diff --git a/drivers/scsi/mpi3mr/mpi3mr.h b/drivers/scsi/mpi3mr/mpi3mr.h index d6e16707fd97..e78383df619c 100644 --- a/drivers/scsi/mpi3mr/mpi3mr.h +++ b/drivers/scsi/mpi3mr/mpi3mr.h @@ -83,6 +83,7 @@ extern atomic64_t event_counter; #define MPI3MR_ADMIN_REPLY_Q_SIZE (8 * MPI3MR_PAGE_SIZE_4K) #define MPI3MR_ADMIN_REQ_FRAME_SZ 128 #define MPI3MR_ADMIN_REPLY_FRAME_SZ 16 +#define MPI3MR_MIN_REPLY_FRAME_SZ_DWORDS 16 /* 64 bytes */ /* Operational queue management definitions */ #define MPI3MR_OP_REQ_Q_QD 512 diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c index f0d3cd398dd0..60a92366e590 100644 --- a/drivers/scsi/mpi3mr/mpi3mr_fw.c +++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c @@ -3261,6 +3261,7 @@ static int mpi3mr_issue_iocfacts(struct mpi3mr_ioc *mrioc, struct mpi3_ioc_facts_data *facts_data) { struct mpi3_ioc_facts_request iocfacts_req; + u16 reply_frame_size; void *data = NULL; dma_addr_t data_dma; u32 data_len = sizeof(*facts_data); @@ -3318,6 +3319,23 @@ static int mpi3mr_issue_iocfacts(struct mpi3mr_ioc *mrioc, goto out_unlock; } memcpy(facts_data, (u8 *)data, data_len); + + reply_frame_size = le16_to_cpu(facts_data->reply_frame_size); + /* + * Validate reply_frame_size: + * - Must be at least 16 dwords (64 bytes) to accommodate fixed reply + * structures and avoid underflow/divide-by-zero in logdata sizing. + * - Must not exceed U16_MAX / 4 to prevent integer overflow. + */ + if (reply_frame_size < MPI3MR_MIN_REPLY_FRAME_SZ_DWORDS || + reply_frame_size > (U16_MAX / 4)) { + ioc_err(mrioc, "Invalid reply_frame_size %u (must be %u..%u dwords)\n", + reply_frame_size, MPI3MR_MIN_REPLY_FRAME_SZ_DWORDS, + (unsigned int)(U16_MAX / 4)); + retval = -EINVAL; + goto out_unlock; + } + mpi3mr_process_factsdata(mrioc, facts_data); out_unlock: mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED; diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 085d4672db85..a77bbc3d2b66 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -7700,7 +7700,9 @@ _scsih_determine_disposition(struct MPT3SAS_ADAPTER *ioc, u16 scode = scsi_sense_code(sense_info.asc, sense_info.ascq); - if (scode == + if (scode == LU_NOT_READY_SANITIZE_IN_PROGRESS) + rc = DEVICE_READY; + else if (scode == LU_NOT_READY_MANUAL_INTERVENTION_REQUIRED || scode == LU_NOT_ACCESSIBLE_TARGET_PORT_IN_STANDBY_STATE || diff --git a/drivers/scsi/scsi_bsg.c b/drivers/scsi/scsi_bsg.c index 5eec248a77a6..3cf1caad02f7 100644 --- a/drivers/scsi/scsi_bsg.c +++ b/drivers/scsi/scsi_bsg.c @@ -41,11 +41,19 @@ static void scsi_bsg_uring_task_cb(struct io_tw_req tw_req, io_tw_token_t tw) u8 sense_len_wr = 0; if (pdu->bio) - blk_rq_unmap_user(pdu->bio); + ret = blk_rq_unmap_user(pdu->bio); + + /* + * If the request is being canceled, e.g. because the submitting task + * has exited, this may run from a kworker or in a different task, so + * don't copy the sense data to the submitter's address. + */ + if (unlikely(tw.cancel)) + ret = -ECANCELED; if (scsi_status_is_check_condition(scmd->result)) { driver_status = DRIVER_SENSE; - if (pdu->response_addr) + if (pdu->response_addr && !tw.cancel) sense_len_wr = min_t(unsigned int, pdu->max_response_len, scmd->sense_len); } @@ -199,7 +207,7 @@ static int scsi_bsg_sg_io_fn(struct request_queue *q, struct sg_io_v4 *hdr, struct scsi_cmnd *scmd; struct request *rq; struct bio *bio; - int ret; + int ret, unmap_ret; if (hdr->protocol != BSG_PROTOCOL_SCSI || hdr->subprotocol != BSG_SUB_PROTOCOL_SCSI_CMD) @@ -273,7 +281,9 @@ static int scsi_bsg_sg_io_fn(struct request_queue *q, struct sg_io_v4 *hdr, else hdr->dout_resid = scmd->resid_len; - blk_rq_unmap_user(bio); + unmap_ret = blk_rq_unmap_user(bio); + if (!ret) + ret = unmap_ret; out_put_request: blk_mq_free_request(rq); diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index a922c8318a2f..c5c4312e23aa 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -9053,6 +9053,7 @@ static void __exit scsi_debug_exit(void) sdebug_erase_all_stores(false); xa_destroy(per_store_ap); + async_synchronize_full_domain(&sdebug_async_domain); debugfs_remove(sdebug_debugfs_root); } diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 75442d60c30e..b8d3c6f2ad33 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -57,6 +58,21 @@ #define BUS_RESET_SETTLE_TIME (10) #define HOST_RESET_SETTLE_TIME (10) +/* + * Number of times a command is retried if it receives the same UNIT ATTENTION + * repeatedly before the error handler is invoked to clear that condition. + */ +#define SCSI_UA_RETRIES 2 +/* + * Maximum number of error handler invocations for a command with unlimited + * retries that keeps receiving the same UNIT ATTENTION. + */ +#define SCSI_UA_EH_ROUNDS 3 +/* Maximum number of TEST UNIT READY commands sent to clear a UNIT ATTENTION. */ +#define SCSI_EH_CLEAR_UA_TURS 4 +/* Delay in milliseconds before a TEST UNIT READY command is retried. */ +#define SCSI_EH_CLEAR_UA_RETRY_DELAY_MS 100 + static int scsi_eh_try_stu(struct scsi_cmnd *scmd); static enum scsi_disposition scsi_try_to_abort_cmd(const struct scsi_host_template *, struct scsi_cmnd *); @@ -559,17 +575,15 @@ static inline void set_scsi_ml_byte(struct scsi_cmnd *cmd, u8 status) } /** - * scsi_check_sense - Examine scsi cmd sense + * __scsi_check_sense - Examine scsi cmd sense * @scmd: Cmd to have sense checked. + * @escalate_ua: Whether to invoke the SCSI error handler for UNIT ATTENTION + * conditions that persist after retries. * - * Return value: - * SUCCESS or FAILED or NEEDS_RETRY or ADD_TO_MLQUEUE - * - * Notes: - * When a deferred error is detected the current command has - * not been executed and needs retrying. + * Return: SUCCESS, FAILED, NEEDS_RETRY or ADD_TO_MLQUEUE. */ -enum scsi_disposition scsi_check_sense(struct scsi_cmnd *scmd) +static enum scsi_disposition __scsi_check_sense(struct scsi_cmnd *scmd, + bool escalate_ua) { struct request *req = scsi_cmd_to_rq(scmd); struct scsi_device *sdev = scmd->device; @@ -716,6 +730,40 @@ enum scsi_disposition scsi_check_sense(struct scsi_cmnd *scmd) sshdr.sense_code == LU_NOT_READY_INITIALIZING_COMMAND_REQUIRED) return FAILED; + /* + * Retry a UNIT ATTENTION that is reported repeatedly with the + * same additional sense code SCSI_UA_RETRIES times on the + * normal completion path for non-removable block requests, + * then invoke the error handler so TEST UNIT READY can clear a + * stuck UNIT ATTENTION. Use a dedicated counter instead of + * scmd->retries since the latter is also incremented for + * other retry reasons and by the error handler. Comparing the + * additional sense code prevents escalation if a target + * reports multiple queued UNIT ATTENTION conditions. + */ + if (escalate_ua && sshdr.sense_key == UNIT_ATTENTION && + !sdev->removable && !scsi_noretry_cmd(scmd)) { + u8 asc = scsi_sense_asc(&sshdr); + + if (scmd->ua_asc != asc) { + scmd->ua_asc = asc; + scmd->ua_count = 0; + } + if (scmd->ua_count < U8_MAX) + scmd->ua_count++; + if (scmd->ua_count <= SCSI_UA_RETRIES) + return NEEDS_RETRY; + /* + * scsi_eh_flush_done_q() limits the number of error + * handler invocations unless retries are unlimited. + * Pass the UNIT ATTENTION upwards in the latter case + * once SCSI_UA_EH_ROUNDS attempts to clear it failed. + */ + if (scmd->allowed == SCSI_CMD_RETRIES_NO_LIMIT && + scmd->ua_count > SCSI_UA_RETRIES + SCSI_UA_EH_ROUNDS) + return SUCCESS; + return FAILED; + } /* * Pass the UA upwards for a determination in the completion * functions. @@ -790,6 +838,27 @@ enum scsi_disposition scsi_check_sense(struct scsi_cmnd *scmd) return SUCCESS; } } + +/** + * scsi_check_sense - Examine scsi cmd sense + * @scmd: Cmd to have sense checked. + * + * Return value: + * SUCCESS or FAILED or NEEDS_RETRY or ADD_TO_MLQUEUE + * + * Notes: + * When a deferred error is detected the current command has + * not been executed and needs retrying. + * + * Unlike the sense data evaluation by scsi_decide_disposition(), this + * function never invokes the SCSI error handler for UNIT ATTENTION + * conditions that persist after retries. Callers with their own error + * handler, e.g. libata, rely on this. + */ +enum scsi_disposition scsi_check_sense(struct scsi_cmnd *scmd) +{ + return __scsi_check_sense(scmd, false); +} EXPORT_SYMBOL_GPL(scsi_check_sense); static void scsi_handle_queue_ramp_up(struct scsi_device *sdev) @@ -1582,6 +1651,33 @@ static int scsi_eh_try_stu(struct scsi_cmnd *scmd) return 1; } +/* + * Return true if @scmd completed with CHECK CONDITION and current (not + * deferred) sense data and store the normalized sense data in @sshdr. + */ +static bool scsi_cmd_current_sense(const struct scsi_cmnd *scmd, + struct scsi_sense_hdr *sshdr) +{ + if ((scmd->eh_eflags & SCSI_EH_ABORT_SCHEDULED) || + host_byte(scmd->result) != DID_OK || + !scsi_status_is_check_condition(scmd->result)) + return false; + + return scsi_command_normalize_sense(scmd, sshdr) && + !scsi_sense_is_deferred(sshdr); +} + +static bool scsi_cmd_needs_stu(const struct scsi_cmnd *scmd) +{ + struct scsi_sense_hdr sshdr; + + return scmd->device->allow_restart && + scsi_cmd_current_sense(scmd, &sshdr) && + (sshdr.sense_key == NOT_READY || + sshdr.sense_key == UNIT_ATTENTION) && + sshdr.sense_code == LU_NOT_READY_INITIALIZING_COMMAND_REQUIRED; +} + /** * scsi_eh_stu - send START_UNIT if needed * @shost: &scsi host being recovered. @@ -1610,8 +1706,7 @@ static int scsi_eh_stu(struct Scsi_Host *shost, } stu_scmd = NULL; list_for_each_entry(scmd, work_q, eh_entry) - if (scmd->device == sdev && SCSI_SENSE_VALID(scmd) && - scsi_check_sense(scmd) == FAILED ) { + if (scmd->device == sdev && scsi_cmd_needs_stu(scmd)) { stu_scmd = scmd; break; } @@ -1645,6 +1740,130 @@ static int scsi_eh_stu(struct Scsi_Host *shost, return list_empty(work_q); } +static bool scsi_cmd_has_ua(const struct scsi_cmnd *scmd) +{ + struct scsi_sense_hdr sshdr; + + return scsi_cmd_current_sense(scmd, &sshdr) && + sshdr.sense_key == UNIT_ATTENTION; +} + +static bool scsi_eh_send_tur_clear_ua(struct scsi_cmnd *scmd) +{ + static unsigned char tur_command[6] = { TEST_UNIT_READY }; + struct Scsi_Host *shost = scmd->device->host; + u8 sense_buf[SCSI_SENSE_BUFFERSIZE]; + struct scsi_sense_hdr sshdr; + enum scsi_disposition rtn; + bool cleared = false; + int i; + + memcpy(sense_buf, scmd->sense_buffer, sizeof(sense_buf)); + + /* + * The first TEST UNIT READY command consumes the pending UNIT + * ATTENTION and the second TEST UNIT READY command verifies that the + * condition has been cleared. Allow additional attempts in case + * multiple UNIT ATTENTION conditions have been queued or in case the + * device is temporarily busy or becoming ready. + */ + for (i = 0; i < SCSI_EH_CLEAR_UA_TURS; i++) { + if (scsi_host_eh_past_deadline(shost)) + break; + rtn = scsi_send_eh_cmnd(scmd, tur_command, sizeof(tur_command), + scmd->device->eh_timeout, 0); + if (rtn == NEEDS_RETRY) { + if (i + 1 < SCSI_EH_CLEAR_UA_TURS) + msleep(SCSI_EH_CLEAR_UA_RETRY_DELAY_MS); + continue; + } + if (rtn != SUCCESS) + break; + if (!scsi_command_normalize_sense(scmd, &sshdr) || + sshdr.sense_key == NO_SENSE || + sshdr.sense_key == RECOVERED_ERROR) { + cleared = true; + break; + } + if (sshdr.sense_key != UNIT_ATTENTION) + break; + } + + memcpy(scmd->sense_buffer, sense_buf, sizeof(sense_buf)); + return cleared; +} + +/** + * scsi_eh_clear_ua - clear pending UNIT ATTENTION conditions if needed + * @shost: SCSI host being recovered. + * @work_q: &list_head for pending commands. + * @done_q: &list_head for processed commands. + * + * If commands fail repeatedly with UNIT ATTENTION because a target does not + * clear the condition for those commands (for example, WRITE), send + * TEST UNIT READY to clear the UNIT ATTENTION before escalating to a reset. + * + * Return: 1 if @work_q is empty, 0 otherwise. + */ +static int scsi_eh_clear_ua(struct Scsi_Host *shost, + struct list_head *work_q, + struct list_head *done_q) +{ + static DEFINE_RATELIMIT_STATE(rs, DEFAULT_RATELIMIT_INTERVAL, + DEFAULT_RATELIMIT_BURST); + struct scsi_cmnd *scmd, *ua_scmd, *next; + struct scsi_device *sdev; + + shost_for_each_device(sdev, shost) { + if (scsi_host_eh_past_deadline(shost)) { + SCSI_LOG_ERROR_RECOVERY(3, + sdev_printk(KERN_INFO, sdev, + "%s: skip clear UA, past eh deadline\n", + current->comm)); + scsi_device_put(sdev); + break; + } + + ua_scmd = NULL; + list_for_each_entry(scmd, work_q, eh_entry) { + if (scmd->device == sdev && scsi_cmd_has_ua(scmd)) { + ua_scmd = scmd; + break; + } + } + + if (!ua_scmd) + continue; + + SCSI_LOG_ERROR_RECOVERY(3, + sdev_printk(KERN_INFO, sdev, + "%s: Sending TEST_UNIT_READY to clear UA\n", + current->comm)); + + if (scsi_device_online(sdev)) { + if (!scsi_eh_send_tur_clear_ua(ua_scmd)) { + SCSI_LOG_ERROR_RECOVERY(3, + sdev_printk(KERN_INFO, sdev, + "%s: Failed to clear UA\n", + current->comm)); + continue; + } + if (__ratelimit(&rs)) + sdev_printk(KERN_INFO, sdev, + "Cleared persistent UNIT ATTENTION\n"); + } + + sdev->expecting_cc_ua = 0; + list_for_each_entry_safe(scmd, next, work_q, eh_entry) { + if (scmd->device == sdev && + scsi_cmd_has_ua(scmd) && + scsi_eh_action(scmd, SUCCESS) == SUCCESS) + scsi_eh_finish_cmd(scmd, done_q); + } + } + + return list_empty(work_q); +} /** * scsi_eh_bus_device_reset - send bdr if needed @@ -2115,7 +2334,7 @@ enum scsi_disposition scsi_decide_disposition(struct scsi_cmnd *scmd) case SAM_STAT_TASK_ABORTED: goto maybe_retry; case SAM_STAT_CHECK_CONDITION: - rtn = scsi_check_sense(scmd); + rtn = __scsi_check_sense(scmd, true); if (rtn == NEEDS_RETRY) goto maybe_retry; /* if rtn == FAILED, we have no sense information; @@ -2275,13 +2494,13 @@ void scsi_eh_ready_devs(struct Scsi_Host *shost, struct list_head *work_q, struct list_head *done_q) { - if (!scsi_eh_stu(shost, work_q, done_q)) - if (!scsi_eh_bus_device_reset(shost, work_q, done_q)) - if (!scsi_eh_target_reset(shost, work_q, done_q)) - if (!scsi_eh_bus_reset(shost, work_q, done_q)) - if (!scsi_eh_host_reset(shost, work_q, done_q)) - scsi_eh_offline_sdevs(work_q, - done_q); + if (!scsi_eh_stu(shost, work_q, done_q) && + !scsi_eh_clear_ua(shost, work_q, done_q) && + !scsi_eh_bus_device_reset(shost, work_q, done_q) && + !scsi_eh_target_reset(shost, work_q, done_q) && + !scsi_eh_bus_reset(shost, work_q, done_q) && + !scsi_eh_host_reset(shost, work_q, done_q)) + scsi_eh_offline_sdevs(work_q, done_q); } EXPORT_SYMBOL_GPL(scsi_eh_ready_devs); diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index ab39514cc0c6..b52b4c5daa42 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -849,6 +849,9 @@ static void scsi_io_completion_action(struct scsi_cmnd *cmd, int result) */ cmd->device->changed = 1; action = ACTION_FAIL; + } else if (cmd->allowed != SCSI_CMD_RETRIES_NO_LIMIT && + cmd->retries > max(cmd->allowed, 3)) { + action = ACTION_FAIL; } else { /* Must have been a power glitch, or a * bus reset. Could not have been a @@ -1272,6 +1275,8 @@ static void scsi_initialize_rq(struct request *rq) init_rcu_head(&cmd->rcu); cmd->jiffies_at_alloc = jiffies; cmd->retries = 0; + cmd->ua_asc = 0; + cmd->ua_count = 0; } /** diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 8aae12fd38f3..52f3b525d2fd 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -2154,14 +2154,13 @@ struct scsi_device *scsi_get_pseudo_sdev(struct Scsi_Host *shost) goto out; sdev = scsi_alloc_sdev(starget, U64_MAX, NULL); - if (!sdev) { - scsi_target_reap(starget); + if (!sdev) goto put_target; - } sdev->borken = 0; put_target: + scsi_target_reap(starget); /* See also the get_device(dev) call in scsi_alloc_target(). */ put_device(&starget->dev); diff --git a/drivers/scsi/sd_zbc.c b/drivers/scsi/sd_zbc.c index 8cbf171e6fab..06f4e3fcda1d 100644 --- a/drivers/scsi/sd_zbc.c +++ b/drivers/scsi/sd_zbc.c @@ -222,7 +222,7 @@ int sd_zbc_report_zones(struct gendisk *disk, sector_t sector, { struct scsi_disk *sdkp = scsi_disk(disk); sector_t lba = sectors_to_logical(sdkp->device, sector); - unsigned int nr, i; + unsigned int nr, i, max_in_buf; unsigned char *buf; u64 zone_length, start_lba; size_t offset, buflen = 0; @@ -241,13 +241,18 @@ int sd_zbc_report_zones(struct gendisk *disk, sector_t sector, if (!buf) return -ENOMEM; + max_in_buf = (buflen - ZBC_REPORT_ZONES_HEADER_SIZE) / + ZBC_ZONE_DESCRIPTOR_SIZE; + while (zone_idx < nr_zones && lba < sdkp->capacity) { ret = sd_zbc_do_report_zones(sdkp, buf, buflen, lba, true); if (ret) goto out; offset = 0; - nr = min(nr_zones, get_unaligned_be32(&buf[0]) / 64); + nr = min3(nr_zones - zone_idx, + get_unaligned_be32(&buf[0]) / ZBC_ZONE_DESCRIPTOR_SIZE, + max_in_buf); if (!nr) break; diff --git a/drivers/scsi/vmw_pvscsi.c b/drivers/scsi/vmw_pvscsi.c index 151cac9f9c2a..3271045a7b3d 100644 --- a/drivers/scsi/vmw_pvscsi.c +++ b/drivers/scsi/vmw_pvscsi.c @@ -201,6 +201,9 @@ static u64 pvscsi_map_context(const struct pvscsi_adapter *adapter, static struct pvscsi_ctx * pvscsi_get_context(const struct pvscsi_adapter *adapter, u64 context) { + if (unlikely(context == 0 || context > adapter->req_depth)) + return NULL; + return &adapter->cmd_map[context - 1]; } @@ -548,8 +551,15 @@ static void pvscsi_complete_request(struct pvscsi_adapter *adapter, struct completion *abort_cmp; u32 btstat = e->hostStatus; u32 sdstat = e->scsiStatus; + u64 data_len = e->dataLen; + u32 data_trans = 0; ctx = pvscsi_get_context(adapter, e->context); + if (ctx == NULL || ctx->cmd == NULL) { + dev_err_ratelimited(&adapter->dev->dev, + "Can not find mapping for context %llu", e->context); + return; + } cmd = ctx->cmd; abort_cmp = ctx->abort_cmp; pvscsi_unmap_buffers(adapter, ctx); @@ -590,15 +600,16 @@ static void pvscsi_complete_request(struct pvscsi_adapter *adapter, * returns zero dataLen with non zero data - do not set * residual count in that case. */ - if (e->dataLen && (e->dataLen < scsi_bufflen(cmd))) - scsi_set_resid(cmd, scsi_bufflen(cmd) - e->dataLen); + if (data_len && (data_len < scsi_bufflen(cmd))) + scsi_set_resid(cmd, scsi_bufflen(cmd) - data_len); cmd->result = (DID_OK << 16); break; case BTSTAT_DATARUN: case BTSTAT_DATA_UNDERRUN: /* Report residual data in underruns */ - scsi_set_resid(cmd, scsi_bufflen(cmd) - e->dataLen); + data_trans = min_t(u64, data_len, scsi_bufflen(cmd)); + scsi_set_resid(cmd, scsi_bufflen(cmd) - data_trans); cmd->result = (DID_ERROR << 16); break; @@ -650,12 +661,18 @@ static void pvscsi_complete_request(struct pvscsi_adapter *adapter, } /* - * barrier usage : Since the PVSCSI device is emulated, there could be cases - * where we may want to serialize some accesses between the driver and the - * emulation layer. We use compiler barriers instead of the more expensive - * memory barriers because PVSCSI is only supported on X86 which has strong - * memory access ordering. + * The completion consumer index must be incremented only after the completion + * entry is read. The read memory barrier combined with READ/WRITE_ONCE + * ensures that all reads from the entry happen before the increment. */ +static void pvscsi_increment_cons_idx(u32 *cons_idx) +{ + u32 new_cons_idx; + dma_rmb(); + new_cons_idx = READ_ONCE(*cons_idx) + 1; + WRITE_ONCE(*cons_idx, new_cons_idx); +} + static void pvscsi_process_completion_ring(struct pvscsi_adapter *adapter) { struct PVSCSIRingsState *s = adapter->rings_state; @@ -666,21 +683,14 @@ static void pvscsi_process_completion_ring(struct pvscsi_adapter *adapter) struct PVSCSIRingCmpDesc *e = ring + (s->cmpConsIdx & MASK(cmp_entries)); /* - * This barrier() ensures that *e is not dereferenced while + * This barrier ensures that *e is not dereferenced while * the device emulation still writes data into the slot. * Since the device emulation advances s->cmpProdIdx only after * updating the slot we want to check it first. */ - barrier(); + dma_rmb(); pvscsi_complete_request(adapter, e); - /* - * This barrier() ensures that compiler doesn't reorder write - * to s->cmpConsIdx before the read of (*e) inside - * pvscsi_complete_request. Otherwise, device emulation may - * overwrite *e before we had a chance to read it. - */ - barrier(); - s->cmpConsIdx++; + pvscsi_increment_cons_idx(&s->cmpConsIdx); } } @@ -764,7 +774,11 @@ static int pvscsi_queue_ring(struct pvscsi_adapter *adapter, e->context = pvscsi_map_context(adapter, ctx); - barrier(); + /* + * Ensure writes to the request entry are done before + * transferring ownership. + */ + dma_wmb(); s->reqProdIdx++; @@ -1105,10 +1119,9 @@ static void pvscsi_process_msg_ring(const struct pvscsi_adapter *adapter) struct PVSCSIRingMsgDesc *e = ring + (s->msgConsIdx & MASK(msg_entries)); - barrier(); + dma_rmb(); pvscsi_process_msg(adapter, e); - barrier(); - s->msgConsIdx++; + pvscsi_increment_cons_idx(&s->msgConsIdx); } } diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 124ff269b8e7..03d618cd7672 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -1518,25 +1518,6 @@ __iscsit_check_dataout_hdr(struct iscsit_conn *conn, void *buf, if (hdr->flags & ISCSI_FLAG_CMD_FINAL) iscsit_stop_dataout_timer(cmd); - return iscsit_dump_data_payload(conn, payload_length, 1); - } - } else { - /* - * For the normal solicited data path: - * - * Check for a delayed TASK_ABORTED status and dump any - * incoming data out payload if one exists. Also, when the - * ISCSI_FLAG_CMD_FINAL is set to denote the end of the current - * data out sequence, we decrement outstanding_r2ts. Once - * outstanding_r2ts reaches zero, go ahead and send the delayed - * TASK_ABORTED status. - */ - if (se_cmd->transport_state & CMD_T_ABORTED) { - if (hdr->flags & ISCSI_FLAG_CMD_FINAL && - --cmd->outstanding_r2ts < 1) { - iscsit_stop_dataout_timer(cmd); - target_complete_cmd(se_cmd, SAM_STAT_TASK_ABORTED); - } return iscsit_dump_data_payload(conn, payload_length, 1); } diff --git a/drivers/target/iscsi/iscsi_target_erl1.c b/drivers/target/iscsi/iscsi_target_erl1.c index d8ca06e697d6..44b851e4a602 100644 --- a/drivers/target/iscsi/iscsi_target_erl1.c +++ b/drivers/target/iscsi/iscsi_target_erl1.c @@ -908,8 +908,6 @@ int iscsit_execute_cmd(struct iscsit_cmd *cmd, int ooo) return 0; } spin_unlock_bh(&cmd->istate_lock); - if (cmd->se_cmd.transport_state & CMD_T_ABORTED) - return 0; return transport_send_check_condition_and_sense(se_cmd, cmd->sense_reason, 0); } @@ -927,9 +925,6 @@ int iscsit_execute_cmd(struct iscsit_cmd *cmd, int ooo) if (!(cmd->cmd_flags & ICF_NON_IMMEDIATE_UNSOLICITED_DATA)) { - if (cmd->se_cmd.transport_state & CMD_T_ABORTED) - return 0; - iscsit_set_dataout_sequence_values(cmd); conn->conn_transport->iscsit_get_dataout(conn, cmd, false); } @@ -942,9 +937,6 @@ int iscsit_execute_cmd(struct iscsit_cmd *cmd, int ooo) if ((cmd->data_direction == DMA_TO_DEVICE) && !(cmd->cmd_flags & ICF_NON_IMMEDIATE_UNSOLICITED_DATA)) { - if (cmd->se_cmd.transport_state & CMD_T_ABORTED) - return 0; - iscsit_set_unsolicited_dataout(cmd); } return target_submit(&cmd->se_cmd); diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c index b03ed154ca34..ccd468e26fb9 100644 --- a/drivers/target/iscsi/iscsi_target_nego.c +++ b/drivers/target/iscsi/iscsi_target_nego.c @@ -982,9 +982,6 @@ static int iscsi_target_handle_csg_one(struct iscsit_conn *conn, struct iscsi_lo if (iscsi_target_check_first_request(conn, login) < 0) return -1; - if (iscsi_target_check_for_existing_instances(conn, login) < 0) - return -1; - ret = iscsi_encode_text_output( PHASE_OPERATIONAL|PHASE_DECLARATIVE, SENDER_TARGET, @@ -1008,6 +1005,9 @@ static int iscsi_target_handle_csg_one(struct iscsit_conn *conn, struct iscsi_lo return -1; } + if (iscsi_target_check_for_existing_instances(conn, login) < 0) + return -1; + if (!iscsi_check_negotiated_keys(conn->param_list)) if ((login_req->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE3) && (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT)) diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c index 140154d93c43..17386c96de13 100644 --- a/drivers/target/target_core_alua.c +++ b/drivers/target/target_core_alua.c @@ -1081,8 +1081,15 @@ int core_alua_do_port_transition( return -EINVAL; local_lu_gp_mem = l_dev->dev_alua_lu_gp_mem; + if (!local_lu_gp_mem) + return -ENODEV; + spin_lock(&local_lu_gp_mem->lu_gp_mem_lock); lu_gp = local_lu_gp_mem->lu_gp; + if (!lu_gp) { + spin_unlock(&local_lu_gp_mem->lu_gp_mem_lock); + return -ENODEV; + } atomic_inc(&lu_gp->lu_gp_ref_cnt); spin_unlock(&local_lu_gp_mem->lu_gp_mem_lock); /* diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index 2b19a956007b..385b84bcbfb2 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -2126,8 +2126,8 @@ static match_table_t tokens = { {Opt_initiator_sid, "initiator_sid=%s"}, {Opt_sa_res_key, "sa_res_key=%s"}, {Opt_res_holder, "res_holder=%d"}, - {Opt_res_type, "res_type=%d"}, - {Opt_res_scope, "res_scope=%d"}, + {Opt_res_type, "res_type=%x"}, + {Opt_res_scope, "res_scope=%x"}, {Opt_res_all_tg_pt, "res_all_tg_pt=%d"}, {Opt_mapped_lun, "mapped_lun=%u"}, {Opt_target_fabric, "target_fabric=%s"}, @@ -2229,13 +2229,13 @@ static ssize_t target_pr_res_aptpl_metadata_store(struct config_item *item, res_holder = arg; break; case Opt_res_type: - ret = match_int(args, &arg); + ret = match_hex(args, &arg); if (ret) goto out; type = (u8)arg; break; case Opt_res_scope: - ret = match_int(args, &arg); + ret = match_hex(args, &arg); if (ret) goto out; break; @@ -2547,6 +2547,14 @@ static ssize_t target_dev_alua_lu_gp_store(struct config_item *item, spin_lock(&lu_gp_mem->lu_gp_mem_lock); lu_gp = lu_gp_mem->lu_gp; + if (!lu_gp && !lu_gp_new) { + /* + * The LU group association has already been cleared, so + * there is nothing left to do. + */ + spin_unlock(&lu_gp_mem->lu_gp_mem_lock); + return count; + } if (lu_gp) { /* * Clearing an existing lu_gp association, and replacing diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 9db2201aa553..c5d5d6a91fee 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -349,6 +349,19 @@ int core_enable_device_list_for_node( new->attach_count++; mutex_lock(&nacl->lun_entry_mutex); + /* + * Do not attach a new dev entry to an se_lun that is being torn down. + * lun_shutdown is set by core_tpg_remove_lun() before its + * core_clear_lun_from_tpg() sweep; checking it here under the same + * nacl->lun_entry_mutex the sweep takes guarantees that once the sweep + * has passed this ACL no dev entry can be left dangling on the + * se_lun->lun_deve_list that is about to be freed. + */ + if (lun->lun_shutdown) { + mutex_unlock(&nacl->lun_entry_mutex); + ret = -ENODEV; + goto free_stats; + } orig = target_nacl_find_deve(nacl, mapped_lun); if (orig && orig->se_lun) { struct se_lun *orig_lun = orig->se_lun; @@ -375,12 +388,12 @@ int core_enable_device_list_for_node( new->se_lun_acl = lun_acl; hlist_del_rcu(&orig->link); hlist_add_head_rcu(&new->link, &nacl->lun_entry_hlist); - mutex_unlock(&nacl->lun_entry_mutex); spin_lock(&lun->lun_deve_lock); list_del(&orig->lun_link); list_add_tail(&new->lun_link, &lun->lun_deve_list); spin_unlock(&lun->lun_deve_lock); + mutex_unlock(&nacl->lun_entry_mutex); kref_put(&orig->pr_kref, target_pr_kref_release); wait_for_completion(&orig->pr_comp); @@ -393,11 +406,10 @@ int core_enable_device_list_for_node( new->se_lun = lun; new->se_lun_acl = lun_acl; hlist_add_head_rcu(&new->link, &nacl->lun_entry_hlist); - mutex_unlock(&nacl->lun_entry_mutex); - spin_lock(&lun->lun_deve_lock); list_add_tail(&new->lun_link, &lun->lun_deve_list); spin_unlock(&lun->lun_deve_lock); + mutex_unlock(&nacl->lun_entry_mutex); target_luns_data_has_changed(nacl, new, true); return 0; diff --git a/drivers/target/target_core_fabric_lib.c b/drivers/target/target_core_fabric_lib.c index 2853b95b2c59..ad024009df56 100644 --- a/drivers/target/target_core_fabric_lib.c +++ b/drivers/target/target_core_fabric_lib.c @@ -371,6 +371,16 @@ static bool iscsi_parse_pr_out_transport_id( pr_err("Missing ISID in iSCSI Initiator port TransportID\n"); return false; } + /* + * The registration code reads the ISID as a C string, + * and its first 8 bytes with get_unaligned_be64(), so + * require 8 bytes after the separator and a NUL inside + * the rest of the descriptor. + */ + if (buf + tid_len - p < 8 || !memchr(p, '\0', buf + tid_len - p)) { + pr_err("Incomplete or unterminated ISID in iSCSI Initiator port TransportID\n"); + return false; + } *port_nexus_ptr = p; /* * Go ahead and do the lower case conversion of the received diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 25b1bcacc0c8..ca1a2802aa22 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -3226,10 +3226,11 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key, transport_kunmap_data_sg(cmd); buf = NULL; - if ((tid_len + 24) != cmd->data_length) { - pr_err("SPC-3 PR: Illegal tid_len: %u + 24 byte header" - " does not equal CDB data_length: %u\n", tid_len, - cmd->data_length); + /* TransportID is at least 24 bytes. buf[24] is its first byte. */ + if (tid_len < 24 || + tid_len != cmd->data_length - 24) { + pr_err("SPC-3 PR: Illegal tid_len: %u for data_length: %u\n", + tid_len, cmd->data_length); ret = TCM_INVALID_PARAMETER_LIST; goto out_put_pr_reg; } diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c index ccff9f6e4826..e615e5e30427 100644 --- a/drivers/target/target_core_tpg.c +++ b/drivers/target/target_core_tpg.c @@ -266,6 +266,15 @@ struct se_node_acl *core_tpg_check_initiator_node_acl( kref_get(&acl->acl_kref); acl->dynamic_node_acl = 1; + /* + * Publish the dynamic NodeACL on tpg->acl_node_list before creating + * its demo-mode MappedLUN dev entries below, so that a concurrent LUN + * teardown sweep (core_clear_lun_from_tpg(), which walks acl_node_list) + * can observe and disable those dev entries. Otherwise a dev entry can + * be left on an se_lun->lun_deve_list that is about to be freed. + */ + target_add_node_acl(acl); + /* * Here we only create demo-mode MappedLUNs from the active * TPG LUNs if the fabric is not explicitly asking for @@ -275,7 +284,6 @@ struct se_node_acl *core_tpg_check_initiator_node_acl( (tpg->se_tpg_tfo->tpg_check_demo_mode_login_only(tpg) != 1)) core_tpg_add_node_to_devs(acl, tpg, NULL); - target_add_node_acl(acl); return acl; } EXPORT_SYMBOL(core_tpg_check_initiator_node_acl); diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index 425ad1246a75..07b514678176 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -2524,6 +2524,11 @@ static int tcmu_set_data_pages_per_blk(struct tcmu_dev *udev, substring_t *arg) return ret; } + if (val <= 0) { + pr_err("Invalid data_pages_per_blk %d.\n", val); + return -EINVAL; + } + if (val > TCMU_MBS_TO_PAGES(udev->data_area_mb)) { pr_err("Invalid data_pages_per_blk %d: greater than max_data_area_mb %d -> %zd pages).\n", val, udev->data_area_mb, diff --git a/drivers/ufs/core/ufs-debugfs.c b/drivers/ufs/core/ufs-debugfs.c index 9f83628f3e2c..44a1a9692324 100644 --- a/drivers/ufs/core/ufs-debugfs.c +++ b/drivers/ufs/core/ufs-debugfs.c @@ -70,12 +70,18 @@ token_context_lock(ufs_debugfs); static int ufs_debugfs_get_user_access(struct ufs_hba *hba) __cond_acquires(0, ufs_debugfs) { + int err; + down(&hba->host_sem); if (!ufshcd_is_user_access_allowed(hba)) { up(&hba->host_sem); return -EBUSY; } - ufshcd_rpm_get_sync(hba); + err = ufshcd_rpm_resume_and_get(hba); + if (err) { + up(&hba->host_sem); + return err; + } __acquire(ufs_debugfs); return 0; } diff --git a/drivers/ufs/core/ufs-hwmon.c b/drivers/ufs/core/ufs-hwmon.c index aa0b60a0ee8a..2b9673d92573 100644 --- a/drivers/ufs/core/ufs-hwmon.c +++ b/drivers/ufs/core/ufs-hwmon.c @@ -61,7 +61,11 @@ static int ufs_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 return -EBUSY; } - ufshcd_rpm_get_sync(hba); + err = ufshcd_rpm_resume_and_get(hba); + if (err) { + up(&hba->host_sem); + return err; + } switch (attr) { case hwmon_temp_enable: @@ -113,7 +117,11 @@ static int ufs_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 return -EBUSY; } - ufshcd_rpm_get_sync(hba); + err = ufshcd_rpm_resume_and_get(hba); + if (err) { + up(&hba->host_sem); + return err; + } if (val == 1) err = ufshcd_update_ee_usr_mask(hba, MASK_EE_URGENT_TEMP, 0); diff --git a/drivers/ufs/core/ufs-mcq.c b/drivers/ufs/core/ufs-mcq.c index 25e392f8ede2..005a70462473 100644 --- a/drivers/ufs/core/ufs-mcq.c +++ b/drivers/ufs/core/ufs-mcq.c @@ -554,6 +554,23 @@ static int ufshcd_mcq_sq_start(struct ufs_hba *hba, struct ufs_hw_queue *hwq) return err; } +/* + * Reap the CQ directly so that progress does not depend on interrupt + * delivery. ufshcd_compl_one_cqe() updates the OCS under cq_lock before + * calling scsi_done(). Holding cq_lock while reading the OCS makes sure that + * the completion has finished once the updated OCS is observed, even if the + * CQE was reaped by the interrupt handler. + */ +static enum utp_ocs ufshcd_mcq_poll_ocs(struct ufs_hba *hba, + struct ufs_hw_queue *hwq, + struct ufshcd_lrb *lrbp) +{ + ufshcd_mcq_poll_cqe_lock(hba, hwq); + + guard(spinlock_irqsave)(&hwq->cq_lock); + return lrbp->utr_descriptor_ptr->header.ocs; +} + /** * ufshcd_mcq_sq_cleanup - Clean up submission queue resources * associated with the pending command. @@ -613,6 +630,23 @@ int ufshcd_mcq_sq_cleanup(struct ufs_hba *hba, int task_tag) __func__, id, FIELD_GET(SQ_ICU_ERR_CODE_MASK, readl(reg))); + /* + * For device management commands, there is no order guaranteed between + * OCS update and CUS update, which is why we need to wait here. SCSI + * commands are excluded because the OCS in their UTRD is not updated + * from the CQE. + */ + if (!err && blk_mq_is_reserved_rq(rq)) { + enum utp_ocs ocs; + + err = read_poll_timeout(ufshcd_mcq_poll_ocs, ocs, + ocs != OCS_INVALID_COMMAND_STATUS, 20, + MCQ_POLL_US, false, hba, hwq, lrbp); + if (err) + dev_err(hba->dev, "%s: OCS not updated. hwq=%d, tag=%d err=%d\n", + __func__, id, task_tag, err); + } + if (ufshcd_mcq_sq_start(hba, hwq)) err = -ETIMEDOUT; diff --git a/drivers/ufs/core/ufs-sysfs.c b/drivers/ufs/core/ufs-sysfs.c index cf49ad0258e1..19b62cf52410 100644 --- a/drivers/ufs/core/ufs-sysfs.c +++ b/drivers/ufs/core/ufs-sysfs.c @@ -250,13 +250,19 @@ EXPORT_SYMBOL_GPL(ufshcd_us_to_ahit); static int ufshcd_read_hci_reg(struct ufs_hba *hba, u32 *val, unsigned int reg) { + int ret; + down(&hba->host_sem); if (!ufshcd_is_user_access_allowed(hba)) { up(&hba->host_sem); return -EBUSY; } - ufshcd_rpm_get_sync(hba); + ret = ufshcd_rpm_resume_and_get(hba); + if (ret) { + up(&hba->host_sem); + return ret; + } ufshcd_hold(hba); *val = ufshcd_readl(hba, reg); ufshcd_release(hba); @@ -350,7 +356,9 @@ static ssize_t wb_on_store(struct device *dev, struct device_attribute *attr, goto out; } - ufshcd_rpm_get_sync(hba); + res = ufshcd_rpm_resume_and_get(hba); + if (res) + goto out; res = ufshcd_wb_toggle(hba, wb_enable); ufshcd_rpm_put_sync(hba); out: @@ -421,7 +429,9 @@ static ssize_t enable_wb_buf_flush_store(struct device *dev, goto out; } - ufshcd_rpm_get_sync(hba); + res = ufshcd_rpm_resume_and_get(hba); + if (res) + goto out; res = ufshcd_wb_toggle_buf_flush(hba, enable_wb_buf_flush); ufshcd_rpm_put_sync(hba); @@ -490,7 +500,9 @@ static ssize_t wb_resize_enable_store(struct device *dev, goto out; } - ufshcd_rpm_get_sync(hba); + res = ufshcd_rpm_resume_and_get(hba); + if (res) + goto out; res = ufshcd_wb_set_resize_en(hba, mode); ufshcd_rpm_put_sync(hba); @@ -598,7 +610,9 @@ static ssize_t device_lvl_exception_id_show(struct device *dev, if (hba->dev_info.wspecversion < 0x410) return -EOPNOTSUPP; - ufshcd_rpm_get_sync(hba); + err = ufshcd_rpm_resume_and_get(hba); + if (err) + return err; err = ufshcd_query_attr_qword(hba, UPIU_QUERY_OPCODE_READ_ATTR, QUERY_ATTR_IDN_DEV_LVL_EXCEPTION_ID, 0, 0, &exception_id); @@ -1101,7 +1115,9 @@ static ssize_t ufs_sysfs_read_desc_param(struct ufs_hba *hba, goto out; } - ufshcd_rpm_get_sync(hba); + ret = ufshcd_rpm_resume_and_get(hba); + if (ret) + goto out; ret = ufshcd_read_desc_param(hba, desc_id, desc_index, param_offset, desc_buf, param_size); ufshcd_rpm_put_sync(hba); @@ -1482,7 +1498,12 @@ static ssize_t _name##_show(struct device *dev, \ up(&hba->host_sem); \ return -ENOMEM; \ } \ - ufshcd_rpm_get_sync(hba); \ + ret = ufshcd_rpm_resume_and_get(hba); \ + if (ret) { \ + kfree(desc_buf); \ + up(&hba->host_sem); \ + return ret; \ + } \ ret = ufshcd_query_descriptor_retry(hba, \ UPIU_QUERY_OPCODE_READ_DESC, QUERY_DESC_IDN_DEVICE, \ 0, 0, desc_buf, &desc_len); \ @@ -1548,7 +1569,9 @@ static ssize_t _name##_show(struct device *dev, \ } \ if (ufshcd_is_wb_flags(QUERY_FLAG_IDN##_uname)) \ index = ufshcd_wb_get_query_index(hba); \ - ufshcd_rpm_get_sync(hba); \ + ret = ufshcd_rpm_resume_and_get(hba); \ + if (ret) \ + goto out; \ ret = ufshcd_query_flag(hba, UPIU_QUERY_OPCODE_READ_FLAG, \ QUERY_FLAG_IDN##_uname, index, &flag); \ ufshcd_rpm_put_sync(hba); \ @@ -1608,7 +1631,9 @@ static ssize_t max_number_of_rtt_show(struct device *dev, return -EBUSY; } - ufshcd_rpm_get_sync(hba); + ret = ufshcd_rpm_resume_and_get(hba); + if (ret) + goto out; ret = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR, QUERY_ATTR_IDN_MAX_NUM_OF_RTT, 0, 0, &rtt); ufshcd_rpm_put_sync(hba); @@ -1648,7 +1673,9 @@ static ssize_t max_number_of_rtt_store(struct device *dev, goto out; } - ufshcd_rpm_get_sync(hba); + ret = ufshcd_rpm_resume_and_get(hba); + if (ret) + goto out; memflags = memalloc_noio_save(); shost_for_each_device(sdev, hba->host) @@ -1700,7 +1727,11 @@ static int wb_read_resize_attrs(struct ufs_hba *hba, } index = ufshcd_wb_get_query_index(hba); - ufshcd_rpm_get_sync(hba); + ret = ufshcd_rpm_resume_and_get(hba); + if (ret) { + up(&hba->host_sem); + return ret; + } ret = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR, idn, index, 0, attr_val); ufshcd_rpm_put_sync(hba); @@ -1760,7 +1791,9 @@ static ssize_t _name##_show(struct device *dev, \ } \ if (ufshcd_is_wb_attrs(QUERY_ATTR_IDN##_uname)) \ index = ufshcd_wb_get_query_index(hba); \ - ufshcd_rpm_get_sync(hba); \ + ret = ufshcd_rpm_resume_and_get(hba); \ + if (ret) \ + goto out; \ if (ufshcd_is_qword_attr(QUERY_ATTR_IDN##_uname)) \ ret = ufshcd_query_attr_qword(hba, \ UPIU_QUERY_OPCODE_READ_ATTR, \ @@ -1848,7 +1881,11 @@ static int hid_query_attr(struct ufs_hba *hba, enum query_opcode opcode, return -EBUSY; } - ufshcd_rpm_get_sync(hba); + ret = ufshcd_rpm_resume_and_get(hba); + if (ret) { + up(&hba->host_sem); + return ret; + } ret = ufshcd_query_attr(hba, opcode, idn, 0, 0, attr_val); ufshcd_rpm_put_sync(hba); @@ -2121,7 +2158,9 @@ static ssize_t dyn_cap_needed_attribute_show(struct device *dev, goto out; } - ufshcd_rpm_get_sync(hba); + ret = ufshcd_rpm_resume_and_get(hba); + if (ret) + goto out; ret = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR, QUERY_ATTR_IDN_DYN_CAP_NEEDED, lun, 0, &value); ufshcd_rpm_put_sync(hba); diff --git a/drivers/ufs/core/ufs_bsg.c b/drivers/ufs/core/ufs_bsg.c index baf6ae917d36..8df1a2c6abf6 100644 --- a/drivers/ufs/core/ufs_bsg.c +++ b/drivers/ufs/core/ufs_bsg.c @@ -150,7 +150,9 @@ static int ufs_bsg_request(struct bsg_job *job) goto out_complete; } - ufshcd_rpm_get_sync(hba); + ret = ufshcd_rpm_resume_and_get(hba); + if (ret) + goto out_complete; msgcode = bsg_request->msgcode; switch (msgcode) { diff --git a/drivers/ufs/core/ufshcd-priv.h b/drivers/ufs/core/ufshcd-priv.h index e55c2a02c1f5..99f654b79035 100644 --- a/drivers/ufs/core/ufshcd-priv.h +++ b/drivers/ufs/core/ufshcd-priv.h @@ -388,6 +388,11 @@ static inline int ufshcd_rpm_get_sync(struct ufs_hba *hba) return pm_runtime_get_sync(&hba->ufs_device_wlun->sdev_gendev); } +static inline int ufshcd_rpm_resume_and_get(struct ufs_hba *hba) +{ + return pm_runtime_resume_and_get(&hba->ufs_device_wlun->sdev_gendev); +} + static inline int ufshcd_rpm_get_if_active(struct ufs_hba *hba) { return pm_runtime_get_if_active(&hba->ufs_device_wlun->sdev_gendev); diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index ee643830a57a..ddc953b45ce9 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -1864,7 +1864,9 @@ static ssize_t ufshcd_clkscale_enable_store(struct device *dev, if (value == hba->clk_scaling.is_enabled) goto out; - ufshcd_rpm_get_sync(hba); + err = ufshcd_rpm_resume_and_get(hba); + if (err) + goto out; ufshcd_hold(hba); hba->clk_scaling.is_enabled = value; @@ -3390,7 +3392,7 @@ static int ufshcd_issue_dev_cmd(struct ufs_hba *hba, struct scsi_cmnd *cmd, struct request *rq = scsi_cmd_to_rq(cmd); blk_status_t sts; - rq->timeout = timeout; + rq->timeout = msecs_to_jiffies(timeout); sts = blk_execute_rq(rq, true); if (sts != BLK_STS_OK) return blk_status_to_errno(sts); @@ -4792,7 +4794,8 @@ void ufshcd_auto_hibern8_update(struct ufs_hba *hba, u32 ahit) WRITE_ONCE(hba->ahit, ahit); if (!pm_runtime_suspended(&hba->ufs_device_wlun->sdev_gendev)) { - ufshcd_rpm_get_sync(hba); + if (ufshcd_rpm_resume_and_get(hba)) + return; ufshcd_hold(hba); /* * Serialize with the host driver's AHIT control. Skip the @@ -5931,18 +5934,26 @@ static irqreturn_t ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status) { irqreturn_t retval = IRQ_NONE; struct uic_command *cmd; + bool ah8_err; guard(spinlock_irqsave)(&hba->host->host_lock); + + /* + * Auto-hibern8 is performed without a UIC command, so check for + * auto-hibern8 errors before checking for an active UIC command. + */ + ah8_err = ufshcd_is_auto_hibern8_error(hba, intr_status); + if (ah8_err) + hba->errors |= (UFSHCD_UIC_HIBERN8_MASK & intr_status); + cmd = hba->active_uic_cmd; if (!cmd) { - dev_err(hba->dev, - "No active UIC command. Maybe a timeout occurred?\n"); + if (!ah8_err) + dev_err(hba->dev, + "No active UIC command. Maybe a timeout occurred?\n"); return retval; } - if (ufshcd_is_auto_hibern8_error(hba, intr_status)) - hba->errors |= (UFSHCD_UIC_HIBERN8_MASK & intr_status); - if (intr_status & UIC_COMMAND_COMPL) { /* * Store the UIC command result in the lowest byte of @@ -8056,11 +8067,15 @@ static void ufshcd_set_req_abort_skip(struct ufs_hba *hba, unsigned long bitmap) * @hba: Pointer to adapter instance * @tag: Tag of the task to be aborted * - * Abort the pending command in device by sending UFS_ABORT_TASK task management - * command, and in host controller by clearing the door-bell register. There can - * be race between controller sending the command to the device while abort is - * issued. To avoid that, first issue UFS_QUERY_TASK to check if the command is - * really issued and then try to abort it. + * Abort the pending SCSI command in device by sending UFS_ABORT_TASK task + * management command, and in host controller by clearing the door-bell + * register. There can be race between controller sending the command to the + * device while abort is issued. To avoid that, first issue UFS_QUERY_TASK to + * check if the command is really issued and then try to abort it. + * + * Device management commands are not SCSI commands, so no task management + * function is issued for them; only the command is cleared in the host + * controller. * * Return: zero on success, non-zero on failure. */ @@ -8077,6 +8092,9 @@ int ufshcd_try_to_abort_task(struct ufs_hba *hba, int tag) lrbp = scsi_cmd_priv(cmd); + if (!ufshcd_is_scsi_cmd(cmd)) + goto clear_cmd; + for (poll_cnt = 100; poll_cnt; poll_cnt--) { err = ufshcd_issue_tm_cmd(hba, lrbp->lun, tag, UFS_QUERY_TASK, &resp); @@ -8122,6 +8140,7 @@ int ufshcd_try_to_abort_task(struct ufs_hba *hba, int tag) return err; } +clear_cmd: err = ufshcd_clear_cmd(hba, tag); if (err) dev_err(hba->dev, "%s: Failed clearing cmd at tag %d, err %d\n", @@ -8215,10 +8234,7 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) goto release; } - if (blk_mq_is_reserved_rq(rq)) - err = ufshcd_clear_cmd(hba, tag); - else - err = ufshcd_try_to_abort_task(hba, tag); + err = ufshcd_try_to_abort_task(hba, tag); if (err) { dev_err(hba->dev, "%s: failed with err %d\n", __func__, err); ufshcd_set_req_abort_skip(hba, hba->outstanding_reqs); @@ -10643,8 +10659,14 @@ static int __ufshcd_wl_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) out: if (ret) ufshcd_update_evt_hist(hba, UFS_EVT_WL_RES_ERR, (u32)ret); - hba->clk_gating.is_suspended = false; - ufshcd_release(hba); + /* + * A failed runtime resume leaves the W-LUN RPM_SUSPENDED, so keep + * is_suspended and the suspend hold for the next resume. + */ + if (!ret || pm_op != UFS_RUNTIME_PM) { + hba->clk_gating.is_suspended = false; + ufshcd_release(hba); + } hba->pm_op_in_progress = false; return ret; } diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c index 27c87e62f8e9..47fc785612ad 100644 --- a/drivers/ufs/host/ufs-qcom.c +++ b/drivers/ufs/host/ufs-qcom.c @@ -2413,7 +2413,9 @@ static void ufs_qcom_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg) { struct device *dev = msi_desc_to_dev(desc); struct ufs_hba *hba = dev_get_drvdata(dev); + struct ufs_qcom_host *host = ufshcd_get_variant(hba); + host->esi_msg = *msg; ufshcd_mcq_config_esi(hba, msg); } @@ -2441,8 +2443,10 @@ static int ufs_qcom_config_esi(struct ufs_hba *hba) struct ufs_qcom_irq *qi; int nr_irqs, ret; - if (host->esi_enabled) - return 0; + if (host->esi_enabled) { + ufshcd_mcq_config_esi(hba, &host->esi_msg); + goto enable; + } /* * 1. We only handle CQs as of now. @@ -2480,12 +2484,14 @@ static int ufs_qcom_config_esi(struct ufs_hba *hba) } } + host->esi_enabled = true; + +enable: if (host->hw_ver.major >= 6) { ufshcd_rmwl(hba, ESI_VEC_MASK, FIELD_PREP(ESI_VEC_MASK, MAX_ESI_VEC - 1), REG_UFS_CFG3); } ufshcd_mcq_enable_esi(hba); - host->esi_enabled = true; return 0; } diff --git a/drivers/ufs/host/ufs-qcom.h b/drivers/ufs/host/ufs-qcom.h index 944b2e532a09..d2647695abe3 100644 --- a/drivers/ufs/host/ufs-qcom.h +++ b/drivers/ufs/host/ufs-qcom.h @@ -5,6 +5,7 @@ #ifndef UFS_QCOM_H_ #define UFS_QCOM_H_ +#include #include #include #include @@ -360,6 +361,7 @@ struct ufs_qcom_host { u32 phy_gear; bool esi_enabled; + struct msi_msg esi_msg; u32 saved_tx_eq_g1_setting; u32 boot_spare_cfg; diff --git a/drivers/ufs/host/ufs-rockchip.c b/drivers/ufs/host/ufs-rockchip.c index bac68f238e1c..87308c9b1070 100644 --- a/drivers/ufs/host/ufs-rockchip.c +++ b/drivers/ufs/host/ufs-rockchip.c @@ -82,6 +82,7 @@ static int ufs_rockchip_rk3576_phy_init(struct ufs_hba *hba) ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_PVALUE2, SEL_RX_LANE0 + i), 0x02); ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_OPTION, SEL_RX_LANE0 + i), 0xf6); ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_POWER_SAVING_CTRL, SEL_RX_LANE0 + i), 0x69); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_SAVE_DET_CTRL, SEL_RX_LANE0 + i), 0x18); } /* disable the mphy DME_SET cfg */ diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h index 2f2364c96457..5a8f7a45dc6d 100644 --- a/include/scsi/scsi_cmnd.h +++ b/include/scsi/scsi_cmnd.h @@ -98,6 +98,13 @@ struct scsi_cmnd { enum scsi_cmnd_submitter submitter; unsigned short cmd_len; + /* + * Additional sense code of the most recent UNIT ATTENTION and the + * number of consecutive UNIT ATTENTIONs with that additional sense + * code. Only used by the SCSI core. + */ + u8 ua_asc; + u8 ua_count; enum dma_data_direction sc_data_direction; unsigned char cmnd[32]; /* SCSI CDB */ diff --git a/include/scsi/scsi_proto.h b/include/scsi/scsi_proto.h index 9c7d31aa6bae..0ee0cf3200d0 100644 --- a/include/scsi/scsi_proto.h +++ b/include/scsi/scsi_proto.h @@ -371,6 +371,9 @@ enum zbc_zone_reporting_options { #define ZBC_REPORT_ZONE_PARTIAL 0x80 +#define ZBC_REPORT_ZONES_HEADER_SIZE 64 +#define ZBC_ZONE_DESCRIPTOR_SIZE 64 + /* Zone types of REPORT ZONES zone descriptors */ enum zbc_zone_type { ZBC_ZONE_TYPE_CONV = 0x1,