Commit 1e46731e authored by Mahesh Rajashekhara's avatar Mahesh Rajashekhara Committed by Martin K. Petersen

scsi: smartpqi: check for null device pointers

- wait on all outstanding I/O to complete before the device is removed.

- check for null device pointers in IO entry/completion functions.
Reviewed-by: default avatarScott Teel <scott.teel@microsemi.com>
Reviewed-by: default avatarMurthy Bhat <murthy.bhat@microsemi.com>
Reviewed-by: default avatarDave Carroll <david.carroll@microsemi.com>
Reviewed-by: default avatarKevin Barnett <kevin.barnett@microsemi.com>
Signed-off-by: default avatarMahesh Rajashekhara <mahesh.rajashekhara@microsemi.com>
Signed-off-by: default avatarDon Brace <don.brace@microsemi.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent c1b10475
...@@ -862,6 +862,7 @@ struct pqi_scsi_dev { ...@@ -862,6 +862,7 @@ struct pqi_scsi_dev {
u8 volume_offline : 1; u8 volume_offline : 1;
bool aio_enabled; /* only valid for physical disks */ bool aio_enabled; /* only valid for physical disks */
bool in_reset; bool in_reset;
bool in_remove;
bool device_offline; bool device_offline;
u8 vendor[8]; /* bytes 8-15 of inquiry data */ u8 vendor[8]; /* bytes 8-15 of inquiry data */
u8 model[16]; /* bytes 16-31 of inquiry data */ u8 model[16]; /* bytes 16-31 of inquiry data */
...@@ -1063,6 +1064,7 @@ struct pqi_ctrl_info { ...@@ -1063,6 +1064,7 @@ struct pqi_ctrl_info {
struct mutex lun_reset_mutex; struct mutex lun_reset_mutex;
bool controller_online; bool controller_online;
bool block_requests; bool block_requests;
bool in_shutdown;
u8 inbound_spanning_supported : 1; u8 inbound_spanning_supported : 1;
u8 outbound_spanning_supported : 1; u8 outbound_spanning_supported : 1;
u8 pqi_mode_enabled : 1; u8 pqi_mode_enabled : 1;
......
...@@ -74,6 +74,8 @@ static int pqi_aio_submit_io(struct pqi_ctrl_info *ctrl_info, ...@@ -74,6 +74,8 @@ static int pqi_aio_submit_io(struct pqi_ctrl_info *ctrl_info,
struct scsi_cmnd *scmd, u32 aio_handle, u8 *cdb, struct scsi_cmnd *scmd, u32 aio_handle, u8 *cdb,
unsigned int cdb_length, struct pqi_queue_group *queue_group, unsigned int cdb_length, struct pqi_queue_group *queue_group,
struct pqi_encryption_info *encryption_info, bool raid_bypass); struct pqi_encryption_info *encryption_info, bool raid_bypass);
static int pqi_device_wait_for_pending_io(struct pqi_ctrl_info *ctrl_info,
struct pqi_scsi_dev *device, unsigned long timeout_secs);
/* for flags argument to pqi_submit_raid_request_synchronous() */ /* for flags argument to pqi_submit_raid_request_synchronous() */
#define PQI_SYNC_FLAGS_INTERRUPTABLE 0x1 #define PQI_SYNC_FLAGS_INTERRUPTABLE 0x1
...@@ -317,6 +319,17 @@ static inline bool pqi_device_in_reset(struct pqi_scsi_dev *device) ...@@ -317,6 +319,17 @@ static inline bool pqi_device_in_reset(struct pqi_scsi_dev *device)
return device->in_reset; return device->in_reset;
} }
static inline void pqi_device_remove_start(struct pqi_scsi_dev *device)
{
device->in_remove = true;
}
static inline bool pqi_device_in_remove(struct pqi_ctrl_info *ctrl_info,
struct pqi_scsi_dev *device)
{
return device->in_remove & !ctrl_info->in_shutdown;
}
static inline void pqi_schedule_rescan_worker_with_delay( static inline void pqi_schedule_rescan_worker_with_delay(
struct pqi_ctrl_info *ctrl_info, unsigned long delay) struct pqi_ctrl_info *ctrl_info, unsigned long delay)
{ {
...@@ -1487,9 +1500,24 @@ static int pqi_add_device(struct pqi_ctrl_info *ctrl_info, ...@@ -1487,9 +1500,24 @@ static int pqi_add_device(struct pqi_ctrl_info *ctrl_info,
return rc; return rc;
} }
#define PQI_PENDING_IO_TIMEOUT_SECS 20
static inline void pqi_remove_device(struct pqi_ctrl_info *ctrl_info, static inline void pqi_remove_device(struct pqi_ctrl_info *ctrl_info,
struct pqi_scsi_dev *device) struct pqi_scsi_dev *device)
{ {
int rc;
pqi_device_remove_start(device);
rc = pqi_device_wait_for_pending_io(ctrl_info, device,
PQI_PENDING_IO_TIMEOUT_SECS);
if (rc)
dev_err(&ctrl_info->pci_dev->dev,
"scsi %d:%d:%d:%d removing device with %d outstanding commands\n",
ctrl_info->scsi_host->host_no, device->bus,
device->target, device->lun,
atomic_read(&device->scsi_cmds_outstanding));
if (pqi_is_logical_device(device)) if (pqi_is_logical_device(device))
scsi_remove_device(device->sdev); scsi_remove_device(device->sdev);
else else
...@@ -5042,7 +5070,17 @@ void pqi_prep_for_scsi_done(struct scsi_cmnd *scmd) ...@@ -5042,7 +5070,17 @@ void pqi_prep_for_scsi_done(struct scsi_cmnd *scmd)
{ {
struct pqi_scsi_dev *device; struct pqi_scsi_dev *device;
if (!scmd->device) {
set_host_byte(scmd, DID_NO_CONNECT);
return;
}
device = scmd->device->hostdata; device = scmd->device->hostdata;
if (!device) {
set_host_byte(scmd, DID_NO_CONNECT);
return;
}
atomic_dec(&device->scsi_cmds_outstanding); atomic_dec(&device->scsi_cmds_outstanding);
} }
...@@ -5059,9 +5097,16 @@ static int pqi_scsi_queue_command(struct Scsi_Host *shost, ...@@ -5059,9 +5097,16 @@ static int pqi_scsi_queue_command(struct Scsi_Host *shost,
device = scmd->device->hostdata; device = scmd->device->hostdata;
ctrl_info = shost_to_hba(shost); ctrl_info = shost_to_hba(shost);
if (!device) {
set_host_byte(scmd, DID_NO_CONNECT);
pqi_scsi_done(scmd);
return 0;
}
atomic_inc(&device->scsi_cmds_outstanding); atomic_inc(&device->scsi_cmds_outstanding);
if (pqi_ctrl_offline(ctrl_info)) { if (pqi_ctrl_offline(ctrl_info) || pqi_device_in_remove(ctrl_info,
device)) {
set_host_byte(scmd, DID_NO_CONNECT); set_host_byte(scmd, DID_NO_CONNECT);
pqi_scsi_done(scmd); pqi_scsi_done(scmd);
return 0; return 0;
...@@ -5214,12 +5259,23 @@ static void pqi_fail_io_queued_for_device(struct pqi_ctrl_info *ctrl_info, ...@@ -5214,12 +5259,23 @@ static void pqi_fail_io_queued_for_device(struct pqi_ctrl_info *ctrl_info,
} }
static int pqi_device_wait_for_pending_io(struct pqi_ctrl_info *ctrl_info, static int pqi_device_wait_for_pending_io(struct pqi_ctrl_info *ctrl_info,
struct pqi_scsi_dev *device) struct pqi_scsi_dev *device, unsigned long timeout_secs)
{ {
unsigned long timeout;
timeout = (timeout_secs * HZ) + jiffies;
while (atomic_read(&device->scsi_cmds_outstanding)) { while (atomic_read(&device->scsi_cmds_outstanding)) {
pqi_check_ctrl_health(ctrl_info); pqi_check_ctrl_health(ctrl_info);
if (pqi_ctrl_offline(ctrl_info)) if (pqi_ctrl_offline(ctrl_info))
return -ENXIO; return -ENXIO;
if (timeout_secs != NO_TIMEOUT) {
if (time_after(jiffies, timeout)) {
dev_err(&ctrl_info->pci_dev->dev,
"timed out waiting for pending IO\n");
return -ETIMEDOUT;
}
}
usleep_range(1000, 2000); usleep_range(1000, 2000);
} }
...@@ -5345,7 +5401,8 @@ static int pqi_device_reset(struct pqi_ctrl_info *ctrl_info, ...@@ -5345,7 +5401,8 @@ static int pqi_device_reset(struct pqi_ctrl_info *ctrl_info,
msleep(PQI_LUN_RESET_RETRY_INTERVAL_MSECS); msleep(PQI_LUN_RESET_RETRY_INTERVAL_MSECS);
} }
if (rc == 0) if (rc == 0)
rc = pqi_device_wait_for_pending_io(ctrl_info, device); rc = pqi_device_wait_for_pending_io(ctrl_info,
device, NO_TIMEOUT);
return rc == 0 ? SUCCESS : FAILED; return rc == 0 ? SUCCESS : FAILED;
} }
...@@ -7188,6 +7245,8 @@ static void pqi_pci_remove(struct pci_dev *pci_dev) ...@@ -7188,6 +7245,8 @@ static void pqi_pci_remove(struct pci_dev *pci_dev)
if (!ctrl_info) if (!ctrl_info)
return; return;
ctrl_info->in_shutdown = true;
pqi_remove_ctrl(ctrl_info); pqi_remove_ctrl(ctrl_info);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment