Commit d3f623ae authored by Sreekanth Reddy's avatar Sreekanth Reddy Committed by Martin K. Petersen

scsi: mpt3sas: Add support for NVMe shutdown

Introduce function _scsih_nvme_shutdown() to issue IO Unit Control message
to IOC firmware with operation code 'shutdown'. This causes IOC firmware to
issue NVMe shutdown commands to all NVMe drives attached to it.

NVMe Shutdown:

NVMe devices need to have a specific shutdown sequence performed before
power is removed. For this, the IOC firmware needs to be notified when the
system is being shutdown. So during the system shutdown time, driver issues
an IO Unit Control request with operation code MPI26_CTRL_OP_SHUTDOWN to
inform firmware that a shutdown is initiated.

This shutdown command is issued only if NVMe devices are attached to the
controller.

During each NVMe device addition, driver reads pcie device page2 to get
shutdown latency (e.g. drive's RTD3 Entry Latency) and updates the max
latency value among the added NVMe drives in ioc->max_shutdown_latency.
This is used as the timeout value for IO Unit Control command at the time
of shutdown.

When a NVMe drive is removed and its shutdown latency matches which
ioc->max_shutdown_latency then ioc->max_shutdown_latency is updated to next
max value (by iterating over the list of available devices).  If the
shutdown latency is 0, then default timeout is set to six seconds.

Link: https://lore.kernel.org/r/20191226111333.26131-3-sreekanth.reddy@broadcom.comSigned-off-by: default avatarSreekanth Reddy <sreekanth.reddy@broadcom.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 1ade26b6
...@@ -140,6 +140,7 @@ ...@@ -140,6 +140,7 @@
#define MAX_CHAIN_ELEMT_SZ 16 #define MAX_CHAIN_ELEMT_SZ 16
#define DEFAULT_NUM_FWCHAIN_ELEMTS 8 #define DEFAULT_NUM_FWCHAIN_ELEMTS 8
#define IO_UNIT_CONTROL_SHUTDOWN_TIMEOUT 6
#define FW_IMG_HDR_READ_TIMEOUT 15 #define FW_IMG_HDR_READ_TIMEOUT 15
#define IOC_OPERATIONAL_WAIT_COUNT 10 #define IOC_OPERATIONAL_WAIT_COUNT 10
...@@ -589,6 +590,7 @@ static inline void sas_device_put(struct _sas_device *s) ...@@ -589,6 +590,7 @@ static inline void sas_device_put(struct _sas_device *s)
* @connector_name: ASCII value of the Connector's name * @connector_name: ASCII value of the Connector's name
* @serial_number: pointer of serial number string allocated runtime * @serial_number: pointer of serial number string allocated runtime
* @access_status: Device's Access Status * @access_status: Device's Access Status
* @shutdown_latency: NVMe device's RTD3 Entry Latency
* @refcount: reference count for deletion * @refcount: reference count for deletion
*/ */
struct _pcie_device { struct _pcie_device {
...@@ -611,6 +613,7 @@ struct _pcie_device { ...@@ -611,6 +613,7 @@ struct _pcie_device {
u8 *serial_number; u8 *serial_number;
u8 reset_timeout; u8 reset_timeout;
u8 access_status; u8 access_status;
u16 shutdown_latency;
struct kref refcount; struct kref refcount;
}; };
/** /**
...@@ -1073,6 +1076,10 @@ typedef void (*MPT3SAS_FLUSH_RUNNING_CMDS)(struct MPT3SAS_ADAPTER *ioc); ...@@ -1073,6 +1076,10 @@ typedef void (*MPT3SAS_FLUSH_RUNNING_CMDS)(struct MPT3SAS_ADAPTER *ioc);
* @event_context: unique id for each logged event * @event_context: unique id for each logged event
* @event_log: event log pointer * @event_log: event log pointer
* @event_masks: events that are masked * @event_masks: events that are masked
* @max_shutdown_latency: timeout value for NVMe shutdown operation,
* which is equal that NVMe drive's RTD3 Entry Latency
* which has reported maximum RTD3 Entry Latency value
* among attached NVMe drives.
* @facts: static facts data * @facts: static facts data
* @prev_fw_facts: previous fw facts data * @prev_fw_facts: previous fw facts data
* @pfacts: static port facts data * @pfacts: static port facts data
...@@ -1283,7 +1290,7 @@ struct MPT3SAS_ADAPTER { ...@@ -1283,7 +1290,7 @@ struct MPT3SAS_ADAPTER {
u8 tm_custom_handling; u8 tm_custom_handling;
u8 nvme_abort_timeout; u8 nvme_abort_timeout;
u16 max_shutdown_latency;
/* static config pages */ /* static config pages */
struct mpt3sas_facts facts; struct mpt3sas_facts facts;
......
...@@ -1049,6 +1049,34 @@ mpt3sas_get_pdev_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) ...@@ -1049,6 +1049,34 @@ mpt3sas_get_pdev_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
return pcie_device; return pcie_device;
} }
/**
* _scsih_set_nvme_max_shutdown_latency - Update max_shutdown_latency.
* @ioc: per adapter object
* Context: This function will acquire ioc->pcie_device_lock
*
* Update ioc->max_shutdown_latency to that NVMe drives RTD3 Entry Latency
* which has reported maximum among all available NVMe drives.
* Minimum max_shutdown_latency will be six seconds.
*/
static void
_scsih_set_nvme_max_shutdown_latency(struct MPT3SAS_ADAPTER *ioc)
{
struct _pcie_device *pcie_device;
unsigned long flags;
u16 shutdown_latency = IO_UNIT_CONTROL_SHUTDOWN_TIMEOUT;
spin_lock_irqsave(&ioc->pcie_device_lock, flags);
list_for_each_entry(pcie_device, &ioc->pcie_device_list, list) {
if (pcie_device->shutdown_latency) {
if (shutdown_latency < pcie_device->shutdown_latency)
shutdown_latency =
pcie_device->shutdown_latency;
}
}
ioc->max_shutdown_latency = shutdown_latency;
spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
}
/** /**
* _scsih_pcie_device_remove - remove pcie_device from list. * _scsih_pcie_device_remove - remove pcie_device from list.
* @ioc: per adapter object * @ioc: per adapter object
...@@ -1063,6 +1091,7 @@ _scsih_pcie_device_remove(struct MPT3SAS_ADAPTER *ioc, ...@@ -1063,6 +1091,7 @@ _scsih_pcie_device_remove(struct MPT3SAS_ADAPTER *ioc,
{ {
unsigned long flags; unsigned long flags;
int was_on_pcie_device_list = 0; int was_on_pcie_device_list = 0;
u8 update_latency = 0;
if (!pcie_device) if (!pcie_device)
return; return;
...@@ -1082,11 +1111,21 @@ _scsih_pcie_device_remove(struct MPT3SAS_ADAPTER *ioc, ...@@ -1082,11 +1111,21 @@ _scsih_pcie_device_remove(struct MPT3SAS_ADAPTER *ioc,
list_del_init(&pcie_device->list); list_del_init(&pcie_device->list);
was_on_pcie_device_list = 1; was_on_pcie_device_list = 1;
} }
if (pcie_device->shutdown_latency == ioc->max_shutdown_latency)
update_latency = 1;
spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
if (was_on_pcie_device_list) { if (was_on_pcie_device_list) {
kfree(pcie_device->serial_number); kfree(pcie_device->serial_number);
pcie_device_put(pcie_device); pcie_device_put(pcie_device);
} }
/*
* This device's RTD3 Entry Latency matches IOC's
* max_shutdown_latency. Recalculate IOC's max_shutdown_latency
* from the available drives as current drive is getting removed.
*/
if (update_latency)
_scsih_set_nvme_max_shutdown_latency(ioc);
} }
...@@ -1101,6 +1140,7 @@ _scsih_pcie_device_remove_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) ...@@ -1101,6 +1140,7 @@ _scsih_pcie_device_remove_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
struct _pcie_device *pcie_device; struct _pcie_device *pcie_device;
unsigned long flags; unsigned long flags;
int was_on_pcie_device_list = 0; int was_on_pcie_device_list = 0;
u8 update_latency = 0;
if (ioc->shost_recovery) if (ioc->shost_recovery)
return; return;
...@@ -1113,12 +1153,22 @@ _scsih_pcie_device_remove_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) ...@@ -1113,12 +1153,22 @@ _scsih_pcie_device_remove_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
was_on_pcie_device_list = 1; was_on_pcie_device_list = 1;
pcie_device_put(pcie_device); pcie_device_put(pcie_device);
} }
if (pcie_device->shutdown_latency == ioc->max_shutdown_latency)
update_latency = 1;
} }
spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
if (was_on_pcie_device_list) { if (was_on_pcie_device_list) {
_scsih_pcie_device_remove_from_sml(ioc, pcie_device); _scsih_pcie_device_remove_from_sml(ioc, pcie_device);
pcie_device_put(pcie_device); pcie_device_put(pcie_device);
} }
/*
* This device's RTD3 Entry Latency matches IOC's
* max_shutdown_latency. Recalculate IOC's max_shutdown_latency
* from the available drives as current drive is getting removed.
*/
if (update_latency)
_scsih_set_nvme_max_shutdown_latency(ioc);
} }
/** /**
...@@ -6933,6 +6983,16 @@ _scsih_pcie_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle) ...@@ -6933,6 +6983,16 @@ _scsih_pcie_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle)
le32_to_cpu(pcie_device_pg0.DeviceInfo)))) { le32_to_cpu(pcie_device_pg0.DeviceInfo)))) {
pcie_device->nvme_mdts = pcie_device->nvme_mdts =
le32_to_cpu(pcie_device_pg2.MaximumDataTransferSize); le32_to_cpu(pcie_device_pg2.MaximumDataTransferSize);
pcie_device->shutdown_latency =
le16_to_cpu(pcie_device_pg2.ShutdownLatency);
/*
* Set IOC's max_shutdown_latency to drive's RTD3 Entry Latency
* if drive's RTD3 Entry Latency is greater then IOC's
* max_shutdown_latency.
*/
if (pcie_device->shutdown_latency > ioc->max_shutdown_latency)
ioc->max_shutdown_latency =
pcie_device->shutdown_latency;
if (pcie_device_pg2.ControllerResetTO) if (pcie_device_pg2.ControllerResetTO)
pcie_device->reset_timeout = pcie_device->reset_timeout =
pcie_device_pg2.ControllerResetTO; pcie_device_pg2.ControllerResetTO;
...@@ -9357,6 +9417,7 @@ _mpt3sas_fw_work(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event) ...@@ -9357,6 +9417,7 @@ _mpt3sas_fw_work(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event)
} }
_scsih_remove_unresponding_devices(ioc); _scsih_remove_unresponding_devices(ioc);
_scsih_scan_for_devices_after_reset(ioc); _scsih_scan_for_devices_after_reset(ioc);
_scsih_set_nvme_max_shutdown_latency(ioc);
break; break;
case MPT3SAS_PORT_ENABLE_COMPLETE: case MPT3SAS_PORT_ENABLE_COMPLETE:
ioc->start_scan = 0; ioc->start_scan = 0;
...@@ -9659,6 +9720,75 @@ _scsih_expander_node_remove(struct MPT3SAS_ADAPTER *ioc, ...@@ -9659,6 +9720,75 @@ _scsih_expander_node_remove(struct MPT3SAS_ADAPTER *ioc,
kfree(sas_expander); kfree(sas_expander);
} }
/**
* _scsih_nvme_shutdown - NVMe shutdown notification
* @ioc: per adapter object
*
* Sending IoUnitControl request with shutdown operation code to alert IOC that
* the host system is shutting down so that IOC can issue NVMe shutdown to
* NVMe drives attached to it.
*/
static void
_scsih_nvme_shutdown(struct MPT3SAS_ADAPTER *ioc)
{
Mpi26IoUnitControlRequest_t *mpi_request;
Mpi26IoUnitControlReply_t *mpi_reply;
u16 smid;
/* are there any NVMe devices ? */
if (list_empty(&ioc->pcie_device_list))
return;
mutex_lock(&ioc->scsih_cmds.mutex);
if (ioc->scsih_cmds.status != MPT3_CMD_NOT_USED) {
ioc_err(ioc, "%s: scsih_cmd in use\n", __func__);
goto out;
}
ioc->scsih_cmds.status = MPT3_CMD_PENDING;
smid = mpt3sas_base_get_smid(ioc, ioc->scsih_cb_idx);
if (!smid) {
ioc_err(ioc,
"%s: failed obtaining a smid\n", __func__);
ioc->scsih_cmds.status = MPT3_CMD_NOT_USED;
goto out;
}
mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
ioc->scsih_cmds.smid = smid;
memset(mpi_request, 0, sizeof(Mpi26IoUnitControlRequest_t));
mpi_request->Function = MPI2_FUNCTION_IO_UNIT_CONTROL;
mpi_request->Operation = MPI26_CTRL_OP_SHUTDOWN;
init_completion(&ioc->scsih_cmds.done);
ioc->put_smid_default(ioc, smid);
/* Wait for max_shutdown_latency seconds */
ioc_info(ioc,
"Io Unit Control shutdown (sending), Shutdown latency %d sec\n",
ioc->max_shutdown_latency);
wait_for_completion_timeout(&ioc->scsih_cmds.done,
ioc->max_shutdown_latency*HZ);
if (!(ioc->scsih_cmds.status & MPT3_CMD_COMPLETE)) {
ioc_err(ioc, "%s: timeout\n", __func__);
goto out;
}
if (ioc->scsih_cmds.status & MPT3_CMD_REPLY_VALID) {
mpi_reply = ioc->scsih_cmds.reply;
ioc_info(ioc, "Io Unit Control shutdown (complete):"
"ioc_status(0x%04x), loginfo(0x%08x)\n",
le16_to_cpu(mpi_reply->IOCStatus),
le32_to_cpu(mpi_reply->IOCLogInfo));
}
out:
ioc->scsih_cmds.status = MPT3_CMD_NOT_USED;
mutex_unlock(&ioc->scsih_cmds.mutex);
}
/** /**
* _scsih_ir_shutdown - IR shutdown notification * _scsih_ir_shutdown - IR shutdown notification
* @ioc: per adapter object * @ioc: per adapter object
...@@ -9851,6 +9981,7 @@ scsih_shutdown(struct pci_dev *pdev) ...@@ -9851,6 +9981,7 @@ scsih_shutdown(struct pci_dev *pdev)
&ioc->ioc_pg1_copy); &ioc->ioc_pg1_copy);
_scsih_ir_shutdown(ioc); _scsih_ir_shutdown(ioc);
_scsih_nvme_shutdown(ioc);
mpt3sas_base_detach(ioc); mpt3sas_base_detach(ioc);
} }
...@@ -10533,6 +10664,8 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -10533,6 +10664,8 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
ioc->tm_sas_control_cb_idx = tm_sas_control_cb_idx; ioc->tm_sas_control_cb_idx = tm_sas_control_cb_idx;
ioc->logging_level = logging_level; ioc->logging_level = logging_level;
ioc->schedule_dead_ioc_flush_running_cmds = &_scsih_flush_running_cmds; ioc->schedule_dead_ioc_flush_running_cmds = &_scsih_flush_running_cmds;
/* Host waits for minimum of six seconds */
ioc->max_shutdown_latency = IO_UNIT_CONTROL_SHUTDOWN_TIMEOUT;
/* /*
* Enable MEMORY MOVE support flag. * Enable MEMORY MOVE support flag.
*/ */
...@@ -10681,6 +10814,7 @@ scsih_suspend(struct pci_dev *pdev, pm_message_t state) ...@@ -10681,6 +10814,7 @@ scsih_suspend(struct pci_dev *pdev, pm_message_t state)
mpt3sas_base_stop_watchdog(ioc); mpt3sas_base_stop_watchdog(ioc);
flush_scheduled_work(); flush_scheduled_work();
scsi_block_requests(shost); scsi_block_requests(shost);
_scsih_nvme_shutdown(ioc);
device_state = pci_choose_state(pdev, state); device_state = pci_choose_state(pdev, state);
ioc_info(ioc, "pdev=0x%p, slot=%s, entering operating state [D%d]\n", ioc_info(ioc, "pdev=0x%p, slot=%s, entering operating state [D%d]\n",
pdev, pci_name(pdev), device_state); pdev, pci_name(pdev), device_state);
......
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