diff --git a/drivers/scsi/smartpqi/smartpqi.h b/drivers/scsi/smartpqi/smartpqi.h index 857d1bea6d06..94b92ae63f23 100644 --- a/drivers/scsi/smartpqi/smartpqi.h +++ b/drivers/scsi/smartpqi/smartpqi.h @@ -785,11 +785,11 @@ struct pqi_scsi_dev { u8 is_physical_device : 1; u8 is_external_raid_device : 1; u8 target_lun_valid : 1; - u8 aio_enabled : 1; /* only valid for physical disks */ u8 device_gone : 1; u8 new_device : 1; u8 keep_device : 1; u8 volume_offline : 1; + bool aio_enabled; /* only valid for physical disks */ bool in_reset; bool device_offline; u8 vendor[8]; /* bytes 8-15 of inquiry data */ @@ -911,7 +911,9 @@ struct pqi_io_request { void (*io_complete_callback)(struct pqi_io_request *io_request, void *context); void *context; + u8 raid_bypass : 1; int status; + struct pqi_queue_group *queue_group; struct scsi_cmnd *scmd; void *error_info; struct pqi_sg_descriptor *sg_chain_buffer; @@ -1019,6 +1021,10 @@ struct pqi_ctrl_info { atomic_t num_busy_threads; atomic_t num_blocked_threads; wait_queue_head_t block_requests_wait; + + struct list_head raid_bypass_retry_list; + spinlock_t raid_bypass_retry_list_lock; + struct work_struct raid_bypass_retry_work; }; enum pqi_ctrl_mode { diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index 15bb8c1602b4..57ff80fba3f5 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -63,6 +63,9 @@ static char *microsemi_branded_controller = "Microsemi Smart Family Controller"; static void pqi_perform_lockup_action(void); static void pqi_take_ctrl_offline(struct pqi_ctrl_info *ctrl_info); +static void pqi_complete_all_queued_raid_bypass_retries( + struct pqi_ctrl_info *ctrl_info, int result); +static void pqi_retry_raid_bypass_requests(struct pqi_ctrl_info *ctrl_info); static int pqi_scan_scsi_devices(struct pqi_ctrl_info *ctrl_info); static void pqi_scan_start(struct Scsi_Host *shost); static void pqi_start_io(struct pqi_ctrl_info *ctrl_info, @@ -74,7 +77,7 @@ static int pqi_submit_raid_request_synchronous(struct pqi_ctrl_info *ctrl_info, static int pqi_aio_submit_io(struct pqi_ctrl_info *ctrl_info, struct scsi_cmnd *scmd, u32 aio_handle, u8 *cdb, unsigned int cdb_length, struct pqi_queue_group *queue_group, - struct pqi_encryption_info *encryption_info); + struct pqi_encryption_info *encryption_info, bool raid_bypass); /* for flags argument to pqi_submit_raid_request_synchronous() */ #define PQI_SYNC_FLAGS_INTERRUPTABLE 0x1 @@ -227,6 +230,7 @@ static inline void pqi_ctrl_unblock_requests(struct pqi_ctrl_info *ctrl_info) { ctrl_info->block_requests = false; wake_up_all(&ctrl_info->block_requests_wait); + pqi_retry_raid_bypass_requests(ctrl_info); scsi_unblock_requests(ctrl_info->scsi_host); } @@ -445,6 +449,14 @@ static int pqi_build_raid_path_request(struct pqi_ctrl_info *ctrl_info, buffer, buffer_length, pci_dir); } +static inline void pqi_reinit_io_request(struct pqi_io_request *io_request) +{ + io_request->scmd = NULL; + io_request->status = 0; + io_request->error_info = NULL; + io_request->raid_bypass = false; +} + static struct pqi_io_request *pqi_alloc_io_request( struct pqi_ctrl_info *ctrl_info) { @@ -462,9 +474,7 @@ static struct pqi_io_request *pqi_alloc_io_request( /* benignly racy */ ctrl_info->next_io_request_slot = (i + 1) % ctrl_info->max_io_slots; - io_request->scmd = NULL; - io_request->status = 0; - io_request->error_info = NULL; + pqi_reinit_io_request(io_request); return io_request; } @@ -1678,8 +1688,8 @@ static bool pqi_is_supported_device(struct pqi_scsi_dev *device) /* * Only support the HBA controller itself as a RAID * controller. If it's a RAID controller other than - * the HBA itself (an external RAID controller, MSA500 - * or similar), we don't support it. + * the HBA itself (an external RAID controller, for + * example), we don't support it. */ if (pqi_is_hba_lunid(device->scsi3addr)) is_supported = true; @@ -2308,7 +2318,7 @@ static int pqi_raid_bypass_submit_scsi_cmd(struct pqi_ctrl_info *ctrl_info, } return pqi_aio_submit_io(ctrl_info, scmd, aio_handle, - cdb, cdb_length, queue_group, encryption_info_ptr); + cdb, cdb_length, queue_group, encryption_info_ptr, true); } #define PQI_STATUS_IDLE 0x0 @@ -2381,6 +2391,7 @@ static inline void pqi_aio_path_disabled(struct pqi_io_request *io_request) device = io_request->scmd->device->hostdata; device->offload_enabled = false; + device->aio_enabled = false; } static inline void pqi_take_device_offline(struct scsi_device *sdev, char *path) @@ -2500,9 +2511,11 @@ static void pqi_process_aio_io_error(struct pqi_io_request *io_request) break; case PQI_AIO_STATUS_NO_PATH_TO_DEVICE: case PQI_AIO_STATUS_INVALID_DEVICE: - device_offline = true; - pqi_take_device_offline(scmd->device, "AIO"); - host_byte = DID_NO_CONNECT; + if (!io_request->raid_bypass) { + device_offline = true; + pqi_take_device_offline(scmd->device, "AIO"); + host_byte = DID_NO_CONNECT; + } scsi_status = SAM_STAT_CHECK_CONDITION; break; case PQI_AIO_STATUS_IO_ERROR: @@ -2751,48 +2764,6 @@ static void pqi_event_worker(struct work_struct *work) pqi_schedule_rescan_worker(ctrl_info); } -static void pqi_take_ctrl_offline(struct pqi_ctrl_info *ctrl_info) -{ - unsigned int i; - unsigned int path; - struct pqi_queue_group *queue_group; - unsigned long flags; - struct pqi_io_request *io_request; - struct pqi_io_request *next; - struct scsi_cmnd *scmd; - - ctrl_info->controller_online = false; - dev_err(&ctrl_info->pci_dev->dev, "controller offline\n"); - sis_shutdown_ctrl(ctrl_info); - pci_disable_device(ctrl_info->pci_dev); - pqi_perform_lockup_action(); - - for (i = 0; i < ctrl_info->num_queue_groups; i++) { - queue_group = &ctrl_info->queue_groups[i]; - - for (path = 0; path < 2; path++) { - spin_lock_irqsave( - &queue_group->submit_lock[path], flags); - - list_for_each_entry_safe(io_request, next, - &queue_group->request_list[path], - request_list_entry) { - - scmd = io_request->scmd; - if (scmd) { - set_host_byte(scmd, DID_NO_CONNECT); - pqi_scsi_done(scmd); - } - - list_del(&io_request->request_list_entry); - } - - spin_unlock_irqrestore( - &queue_group->submit_lock[path], flags); - } - } -} - #define PQI_HEARTBEAT_TIMER_INTERVAL (10 * HZ) static void pqi_heartbeat_timer_handler(unsigned long data) @@ -3461,9 +3432,11 @@ static void pqi_start_io(struct pqi_ctrl_info *ctrl_info, spin_lock_irqsave(&queue_group->submit_lock[path], flags); - if (io_request) + if (io_request) { + io_request->queue_group = queue_group; list_add_tail(&io_request->request_list_entry, &queue_group->request_list[path]); + } iq_pi = queue_group->iq_pi_copy[path]; @@ -3623,6 +3596,11 @@ static int pqi_submit_raid_request_synchronous(struct pqi_ctrl_info *ctrl_info, goto out; } + if (pqi_ctrl_offline(ctrl_info)) { + rc = -ENXIO; + goto out; + } + io_request = pqi_alloc_io_request(ctrl_info); put_unaligned_le16(io_request->index, @@ -4509,21 +4487,18 @@ static void pqi_raid_io_complete(struct pqi_io_request *io_request, pqi_scsi_done(scmd); } -static int pqi_raid_submit_scsi_cmd(struct pqi_ctrl_info *ctrl_info, +static int pqi_raid_submit_scsi_cmd_with_io_request( + struct pqi_ctrl_info *ctrl_info, struct pqi_io_request *io_request, struct pqi_scsi_dev *device, struct scsi_cmnd *scmd, struct pqi_queue_group *queue_group) { int rc; size_t cdb_length; - struct pqi_io_request *io_request; struct pqi_raid_path_request *request; - io_request = pqi_alloc_io_request(ctrl_info); io_request->io_complete_callback = pqi_raid_io_complete; io_request->scmd = scmd; - scmd->host_scribble = (unsigned char *)io_request; - request = io_request->iu; memset(request, 0, offsetof(struct pqi_raid_path_request, sg_descriptors)); @@ -4602,6 +4577,183 @@ static int pqi_raid_submit_scsi_cmd(struct pqi_ctrl_info *ctrl_info, return 0; } +static inline int pqi_raid_submit_scsi_cmd(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device, struct scsi_cmnd *scmd, + struct pqi_queue_group *queue_group) +{ + struct pqi_io_request *io_request; + + io_request = pqi_alloc_io_request(ctrl_info); + + return pqi_raid_submit_scsi_cmd_with_io_request(ctrl_info, io_request, + device, scmd, queue_group); +} + +static inline void pqi_schedule_bypass_retry(struct pqi_ctrl_info *ctrl_info) +{ + if (!pqi_ctrl_blocked(ctrl_info)) + schedule_work(&ctrl_info->raid_bypass_retry_work); +} + +static bool pqi_raid_bypass_retry_needed(struct pqi_io_request *io_request) +{ + struct scsi_cmnd *scmd; + struct pqi_ctrl_info *ctrl_info; + + if (!io_request->raid_bypass) + return false; + + scmd = io_request->scmd; + if ((scmd->result & 0xff) == SAM_STAT_GOOD) + return false; + if (host_byte(scmd->result) == DID_NO_CONNECT) + return false; + + ctrl_info = shost_to_hba(scmd->device->host); + if (pqi_ctrl_offline(ctrl_info)) + return false; + + return true; +} + +static inline void pqi_add_to_raid_bypass_retry_list( + struct pqi_ctrl_info *ctrl_info, + struct pqi_io_request *io_request, bool at_head) +{ + unsigned long flags; + + spin_lock_irqsave(&ctrl_info->raid_bypass_retry_list_lock, flags); + if (at_head) + list_add(&io_request->request_list_entry, + &ctrl_info->raid_bypass_retry_list); + else + list_add_tail(&io_request->request_list_entry, + &ctrl_info->raid_bypass_retry_list); + spin_unlock_irqrestore(&ctrl_info->raid_bypass_retry_list_lock, flags); +} + +static void pqi_queued_raid_bypass_complete(struct pqi_io_request *io_request, + void *context) +{ + struct scsi_cmnd *scmd; + + scmd = io_request->scmd; + pqi_free_io_request(io_request); + pqi_scsi_done(scmd); +} + +static void pqi_queue_raid_bypass_retry(struct pqi_io_request *io_request) +{ + struct scsi_cmnd *scmd; + struct pqi_ctrl_info *ctrl_info; + + io_request->io_complete_callback = pqi_queued_raid_bypass_complete; + scmd = io_request->scmd; + scmd->result = 0; + ctrl_info = shost_to_hba(scmd->device->host); + + pqi_add_to_raid_bypass_retry_list(ctrl_info, io_request, false); + pqi_schedule_bypass_retry(ctrl_info); +} + +static int pqi_retry_raid_bypass(struct pqi_io_request *io_request) +{ + struct scsi_cmnd *scmd; + struct pqi_scsi_dev *device; + struct pqi_ctrl_info *ctrl_info; + struct pqi_queue_group *queue_group; + + scmd = io_request->scmd; + device = scmd->device->hostdata; + if (pqi_device_in_reset(device)) { + pqi_free_io_request(io_request); + set_host_byte(scmd, DID_RESET); + pqi_scsi_done(scmd); + return 0; + } + + ctrl_info = shost_to_hba(scmd->device->host); + queue_group = io_request->queue_group; + + pqi_reinit_io_request(io_request); + + return pqi_raid_submit_scsi_cmd_with_io_request(ctrl_info, io_request, + device, scmd, queue_group); +} + +static inline struct pqi_io_request *pqi_next_queued_raid_bypass_request( + struct pqi_ctrl_info *ctrl_info) +{ + unsigned long flags; + struct pqi_io_request *io_request; + + spin_lock_irqsave(&ctrl_info->raid_bypass_retry_list_lock, flags); + io_request = list_first_entry_or_null( + &ctrl_info->raid_bypass_retry_list, + struct pqi_io_request, request_list_entry); + if (io_request) + list_del(&io_request->request_list_entry); + spin_unlock_irqrestore(&ctrl_info->raid_bypass_retry_list_lock, flags); + + return io_request; +} + +static void pqi_retry_raid_bypass_requests(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + struct pqi_io_request *io_request; + + pqi_ctrl_busy(ctrl_info); + + while (1) { + if (pqi_ctrl_blocked(ctrl_info)) + break; + io_request = pqi_next_queued_raid_bypass_request(ctrl_info); + if (!io_request) + break; + rc = pqi_retry_raid_bypass(io_request); + if (rc) { + pqi_add_to_raid_bypass_retry_list(ctrl_info, io_request, + true); + pqi_schedule_bypass_retry(ctrl_info); + break; + } + } + + pqi_ctrl_unbusy(ctrl_info); +} + +static void pqi_raid_bypass_retry_worker(struct work_struct *work) +{ + struct pqi_ctrl_info *ctrl_info; + + ctrl_info = container_of(work, struct pqi_ctrl_info, + raid_bypass_retry_work); + pqi_retry_raid_bypass_requests(ctrl_info); +} + +static void pqi_complete_all_queued_raid_bypass_retries( + struct pqi_ctrl_info *ctrl_info, int result) +{ + unsigned long flags; + struct pqi_io_request *io_request; + struct pqi_io_request *next; + struct scsi_cmnd *scmd; + + spin_lock_irqsave(&ctrl_info->raid_bypass_retry_list_lock, flags); + + list_for_each_entry_safe(io_request, next, + &ctrl_info->raid_bypass_retry_list, request_list_entry) { + list_del(&io_request->request_list_entry); + scmd = io_request->scmd; + pqi_free_io_request(io_request); + scmd->result = result; + pqi_scsi_done(scmd); + } + + spin_unlock_irqrestore(&ctrl_info->raid_bypass_retry_list_lock, flags); +} + static void pqi_aio_io_complete(struct pqi_io_request *io_request, void *context) { @@ -4611,6 +4763,10 @@ static void pqi_aio_io_complete(struct pqi_io_request *io_request, scsi_dma_unmap(scmd); if (io_request->status == -EAGAIN) set_host_byte(scmd, DID_IMM_RETRY); + else if (pqi_raid_bypass_retry_needed(io_request)) { + pqi_queue_raid_bypass_retry(io_request); + return; + } pqi_free_io_request(io_request); pqi_scsi_done(scmd); } @@ -4620,13 +4776,13 @@ static inline int pqi_aio_submit_scsi_cmd(struct pqi_ctrl_info *ctrl_info, struct pqi_queue_group *queue_group) { return pqi_aio_submit_io(ctrl_info, scmd, device->aio_handle, - scmd->cmnd, scmd->cmd_len, queue_group, NULL); + scmd->cmnd, scmd->cmd_len, queue_group, NULL, false); } static int pqi_aio_submit_io(struct pqi_ctrl_info *ctrl_info, struct scsi_cmnd *scmd, u32 aio_handle, u8 *cdb, unsigned int cdb_length, struct pqi_queue_group *queue_group, - struct pqi_encryption_info *encryption_info) + struct pqi_encryption_info *encryption_info, bool raid_bypass) { int rc; struct pqi_io_request *io_request; @@ -4635,8 +4791,7 @@ static int pqi_aio_submit_io(struct pqi_ctrl_info *ctrl_info, io_request = pqi_alloc_io_request(ctrl_info); io_request->io_complete_callback = pqi_aio_io_complete; io_request->scmd = scmd; - - scmd->host_scribble = (unsigned char *)io_request; + io_request->raid_bypass = raid_bypass; request = io_request->iu; memset(request, 0, @@ -4761,11 +4916,8 @@ static int pqi_scsi_queue_command(struct Scsi_Host *shost, !blk_rq_is_passthrough(scmd->request)) { rc = pqi_raid_bypass_submit_scsi_cmd(ctrl_info, device, scmd, queue_group); - if (rc == 0 || - rc == SCSI_MLQUEUE_HOST_BUSY || - rc == SAM_STAT_CHECK_CONDITION || - rc == SAM_STAT_RESERVATION_CONFLICT) - raid_bypassed = true; + if (rc == 0 || rc == SCSI_MLQUEUE_HOST_BUSY) + raid_bypassed = true; } if (!raid_bypassed) rc = pqi_raid_submit_scsi_cmd(ctrl_info, device, scmd, @@ -6159,6 +6311,11 @@ static struct pqi_ctrl_info *pqi_alloc_ctrl_info(int numa_node) PQI_RESERVED_IO_SLOTS_SYNCHRONOUS_REQUESTS); init_waitqueue_head(&ctrl_info->block_requests_wait); + INIT_LIST_HEAD(&ctrl_info->raid_bypass_retry_list); + spin_lock_init(&ctrl_info->raid_bypass_retry_list_lock); + INIT_WORK(&ctrl_info->raid_bypass_retry_work, + pqi_raid_bypass_retry_worker); + ctrl_info->ctrl_id = atomic_inc_return(&pqi_controller_count) - 1; ctrl_info->irq_mode = IRQ_MODE_NONE; ctrl_info->max_msix_vectors = PQI_MAX_MSIX_VECTORS; @@ -6228,6 +6385,60 @@ static void pqi_perform_lockup_action(void) } } +static void pqi_complete_all_queued_requests(struct pqi_ctrl_info *ctrl_info, + int result) +{ + unsigned int i; + unsigned int path; + struct pqi_queue_group *queue_group; + unsigned long flags; + struct pqi_io_request *io_request; + struct pqi_io_request *next; + struct scsi_cmnd *scmd; + + for (i = 0; i < ctrl_info->num_queue_groups; i++) { + queue_group = &ctrl_info->queue_groups[i]; + + for (path = 0; path < 2; path++) { + spin_lock_irqsave( + &queue_group->submit_lock[path], flags); + + list_for_each_entry_safe(io_request, next, + &queue_group->request_list[path], + request_list_entry) { + + scmd = io_request->scmd; + if (scmd) { + scmd->result = result; + pqi_scsi_done(scmd); + } + + list_del(&io_request->request_list_entry); + } + + spin_unlock_irqrestore( + &queue_group->submit_lock[path], flags); + } + } +} + +static void pqi_fail_all_queued_requests(struct pqi_ctrl_info *ctrl_info) +{ + pqi_complete_all_queued_requests(ctrl_info, DID_NO_CONNECT << 16); + pqi_complete_all_queued_raid_bypass_retries(ctrl_info, + DID_NO_CONNECT << 16); +} + +static void pqi_take_ctrl_offline(struct pqi_ctrl_info *ctrl_info) +{ + ctrl_info->controller_online = false; + sis_shutdown_ctrl(ctrl_info); + pci_disable_device(ctrl_info->pci_dev); + dev_err(&ctrl_info->pci_dev->dev, "controller offline\n"); + pqi_perform_lockup_action(); + pqi_fail_all_queued_requests(ctrl_info); +} + static void pqi_print_ctrl_info(struct pci_dev *pci_dev, const struct pci_device_id *id) {