scsi: smartpqi: correct aio error path

set the internal flag that causes I/O to be sent down the
RAID path when the AIO path is disabled

Reviewed-by: Scott Benesh <scott.benesh@microsemi.com>
Signed-off-by: Kevin Barnett <kevin.barnett@microsemi.com>
Signed-off-by: Don Brace <don.brace@microsemi.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
Kevin Barnett 2017-05-03 18:54:43 -05:00 committed by Martin K. Petersen
parent 3c50976f33
commit 376fb880a4
2 changed files with 285 additions and 68 deletions

View file

@ -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 {

View file

@ -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)
{