scsi: mpt3sas: Handle NVMe PCIe device related events generated from firmware.

* The controller firmware sends separate events for NVMe devices and
  PCIe switches similar to existing SAS events.

* NVMe device detection, addition and removal are reported by the
  firmware through PCIe Topology Change list events.

* The PCIe device state change events are sent when the firmware
  detects any abnormal conditions with a NVMe device or switch.

* The enumeration event are sent when the firmware starts PCIe device
  enumeration and stops.

* This patch has the code change to handle the events and add/remove
  NVMe devices in driver's inventory.

Signed-off-by: Chaitra P B <chaitra.basappa@broadcom.com>
Signed-off-by: Suganath Prabu S <suganath-prabu.subramani@broadcom.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
Suganath Prabu Subramani 2017-10-31 18:02:32 +05:30 committed by Martin K. Petersen
parent 3075ac4902
commit 4318c73478
2 changed files with 495 additions and 6 deletions

View file

@ -663,6 +663,26 @@ _base_display_event_data(struct MPT3SAS_ADAPTER *ioc,
case MPI2_EVENT_ACTIVE_CABLE_EXCEPTION:
desc = "Cable Event";
break;
case MPI2_EVENT_PCIE_DEVICE_STATUS_CHANGE:
desc = "PCIE Device Status Change";
break;
case MPI2_EVENT_PCIE_ENUMERATION:
{
Mpi26EventDataPCIeEnumeration_t *event_data =
(Mpi26EventDataPCIeEnumeration_t *)mpi_reply->EventData;
pr_info(MPT3SAS_FMT "PCIE Enumeration: (%s)", ioc->name,
(event_data->ReasonCode ==
MPI26_EVENT_PCIE_ENUM_RC_STARTED) ?
"start" : "stop");
if (event_data->EnumerationStatus)
pr_info("enumeration_status(0x%08x)",
le32_to_cpu(event_data->EnumerationStatus));
pr_info("\n");
return;
}
case MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST:
desc = "PCIE Topology Change List";
break;
}
if (!desc)
@ -6125,7 +6145,15 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc)
_base_unmask_events(ioc, MPI2_EVENT_LOG_ENTRY_ADDED);
_base_unmask_events(ioc, MPI2_EVENT_TEMP_THRESHOLD);
_base_unmask_events(ioc, MPI2_EVENT_ACTIVE_CABLE_EXCEPTION);
if (ioc->hba_mpi_version_belonged == MPI26_VERSION) {
if (ioc->is_gen35_ioc) {
_base_unmask_events(ioc,
MPI2_EVENT_PCIE_DEVICE_STATUS_CHANGE);
_base_unmask_events(ioc, MPI2_EVENT_PCIE_ENUMERATION);
_base_unmask_events(ioc,
MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST);
}
}
r = _base_make_ioc_operational(ioc);
if (r)
goto out_free_resources;

View file

@ -75,6 +75,8 @@ static int _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle,
static int _scsih_pcie_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle);
static void _scsih_pcie_device_remove_from_sml(struct MPT3SAS_ADAPTER *ioc,
struct _pcie_device *pcie_device);
static void
_scsih_pcie_check_device(struct MPT3SAS_ADAPTER *ioc, u16 handle);
static u8 _scsih_check_for_pending_tm(struct MPT3SAS_ADAPTER *ioc, u16 smid);
/* global parameters */
@ -3475,8 +3477,6 @@ _scsih_block_io_device(struct MPT3SAS_ADAPTER *ioc, u16 handle)
struct _sas_device *sas_device;
sas_device = mpt3sas_get_sdev_by_handle(ioc, handle);
if (!sas_device)
return;
shost_for_each_device(sdev, ioc->shost) {
sas_device_priv_data = sdev->hostdata;
@ -3486,7 +3486,7 @@ _scsih_block_io_device(struct MPT3SAS_ADAPTER *ioc, u16 handle)
continue;
if (sas_device_priv_data->block)
continue;
if (sas_device->pend_sas_rphy_add)
if (sas_device && sas_device->pend_sas_rphy_add)
continue;
if (sas_device_priv_data->ignore_delay_remove) {
sdev_printk(KERN_INFO, sdev,
@ -3497,7 +3497,8 @@ _scsih_block_io_device(struct MPT3SAS_ADAPTER *ioc, u16 handle)
_scsih_internal_device_block(sdev, sas_device_priv_data);
}
sas_device_put(sas_device);
if (sas_device)
sas_device_put(sas_device);
}
/**
@ -3580,6 +3581,33 @@ _scsih_block_io_to_children_attached_directly(struct MPT3SAS_ADAPTER *ioc,
}
}
/**
* _scsih_block_io_to_pcie_children_attached_directly
* @ioc: per adapter object
* @event_data: topology change event data
*
* This routine set sdev state to SDEV_BLOCK for all devices
* direct attached during device pull/reconnect.
*/
static void
_scsih_block_io_to_pcie_children_attached_directly(struct MPT3SAS_ADAPTER *ioc,
Mpi26EventDataPCIeTopologyChangeList_t *event_data)
{
int i;
u16 handle;
u16 reason_code;
for (i = 0; i < event_data->NumEntries; i++) {
handle =
le16_to_cpu(event_data->PortEntry[i].AttachedDevHandle);
if (!handle)
continue;
reason_code = event_data->PortEntry[i].PortStatus;
if (reason_code ==
MPI26_EVENT_PCIE_TOPO_PS_DELAY_NOT_RESPONDING)
_scsih_block_io_device(ioc, handle);
}
}
/**
* _scsih_tm_tr_send - send task management request
* @ioc: per adapter object
@ -4188,6 +4216,81 @@ _scsih_check_topo_delete_events(struct MPT3SAS_ADAPTER *ioc,
spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
}
/**
* _scsih_check_pcie_topo_remove_events - sanity check on topo
* events
* @ioc: per adapter object
* @event_data: the event data payload
*
* This handles the case where driver receives multiple switch
* or device add and delete events in a single shot. When there
* is a delete event the routine will void any pending add
* events waiting in the event queue.
*
* Return nothing.
*/
static void
_scsih_check_pcie_topo_remove_events(struct MPT3SAS_ADAPTER *ioc,
Mpi26EventDataPCIeTopologyChangeList_t *event_data)
{
struct fw_event_work *fw_event;
Mpi26EventDataPCIeTopologyChangeList_t *local_event_data;
unsigned long flags;
int i, reason_code;
u16 handle, switch_handle;
for (i = 0; i < event_data->NumEntries; i++) {
handle =
le16_to_cpu(event_data->PortEntry[i].AttachedDevHandle);
if (!handle)
continue;
reason_code = event_data->PortEntry[i].PortStatus;
if (reason_code == MPI26_EVENT_PCIE_TOPO_PS_NOT_RESPONDING)
_scsih_tm_tr_send(ioc, handle);
}
switch_handle = le16_to_cpu(event_data->SwitchDevHandle);
if (!switch_handle) {
_scsih_block_io_to_pcie_children_attached_directly(
ioc, event_data);
return;
}
/* TODO We are not supporting cascaded PCIe Switch removal yet*/
if ((event_data->SwitchStatus
== MPI26_EVENT_PCIE_TOPO_SS_DELAY_NOT_RESPONDING) ||
(event_data->SwitchStatus ==
MPI26_EVENT_PCIE_TOPO_SS_RESPONDING))
_scsih_block_io_to_pcie_children_attached_directly(
ioc, event_data);
if (event_data->SwitchStatus != MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING)
return;
/* mark ignore flag for pending events */
spin_lock_irqsave(&ioc->fw_event_lock, flags);
list_for_each_entry(fw_event, &ioc->fw_event_list, list) {
if (fw_event->event != MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST ||
fw_event->ignore)
continue;
local_event_data =
(Mpi26EventDataPCIeTopologyChangeList_t *)
fw_event->event_data;
if (local_event_data->SwitchStatus ==
MPI2_EVENT_SAS_TOPO_ES_ADDED ||
local_event_data->SwitchStatus ==
MPI2_EVENT_SAS_TOPO_ES_RESPONDING) {
if (le16_to_cpu(local_event_data->SwitchDevHandle) ==
switch_handle) {
dewtprintk(ioc, pr_info(MPT3SAS_FMT
"setting ignoring flag for switch event\n",
ioc->name));
fw_event->ignore = 1;
}
}
}
spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
}
/**
* _scsih_set_volume_delete_flag - setting volume delete flag
* @ioc: per adapter object
@ -6514,9 +6617,9 @@ out:
sas_device_put(sas_device);
spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
}
/**
* _scsih_check_pcie_access_status - check access flags
* @ioc: per adapter object
@ -6902,6 +7005,319 @@ _scsih_pcie_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle)
pcie_device_put(pcie_device);
return 0;
}
/**
* _scsih_pcie_topology_change_event_debug - debug for topology
* event
* @ioc: per adapter object
* @event_data: event data payload
* Context: user.
*/
static void
_scsih_pcie_topology_change_event_debug(struct MPT3SAS_ADAPTER *ioc,
Mpi26EventDataPCIeTopologyChangeList_t *event_data)
{
int i;
u16 handle;
u16 reason_code;
u8 port_number;
char *status_str = NULL;
u8 link_rate, prev_link_rate;
switch (event_data->SwitchStatus) {
case MPI26_EVENT_PCIE_TOPO_SS_ADDED:
status_str = "add";
break;
case MPI26_EVENT_PCIE_TOPO_SS_NOT_RESPONDING:
status_str = "remove";
break;
case MPI26_EVENT_PCIE_TOPO_SS_RESPONDING:
case 0:
status_str = "responding";
break;
case MPI26_EVENT_PCIE_TOPO_SS_DELAY_NOT_RESPONDING:
status_str = "remove delay";
break;
default:
status_str = "unknown status";
break;
}
pr_info(MPT3SAS_FMT "pcie topology change: (%s)\n",
ioc->name, status_str);
pr_info("\tswitch_handle(0x%04x), enclosure_handle(0x%04x)"
"start_port(%02d), count(%d)\n",
le16_to_cpu(event_data->SwitchDevHandle),
le16_to_cpu(event_data->EnclosureHandle),
event_data->StartPortNum, event_data->NumEntries);
for (i = 0; i < event_data->NumEntries; i++) {
handle =
le16_to_cpu(event_data->PortEntry[i].AttachedDevHandle);
if (!handle)
continue;
port_number = event_data->StartPortNum + i;
reason_code = event_data->PortEntry[i].PortStatus;
switch (reason_code) {
case MPI26_EVENT_PCIE_TOPO_PS_DEV_ADDED:
status_str = "target add";
break;
case MPI26_EVENT_PCIE_TOPO_PS_NOT_RESPONDING:
status_str = "target remove";
break;
case MPI26_EVENT_PCIE_TOPO_PS_DELAY_NOT_RESPONDING:
status_str = "delay target remove";
break;
case MPI26_EVENT_PCIE_TOPO_PS_PORT_CHANGED:
status_str = "link rate change";
break;
case MPI26_EVENT_PCIE_TOPO_PS_NO_CHANGE:
status_str = "target responding";
break;
default:
status_str = "unknown";
break;
}
link_rate = event_data->PortEntry[i].CurrentPortInfo &
MPI26_EVENT_PCIE_TOPO_PI_RATE_MASK;
prev_link_rate = event_data->PortEntry[i].PreviousPortInfo &
MPI26_EVENT_PCIE_TOPO_PI_RATE_MASK;
pr_info("\tport(%02d), attached_handle(0x%04x): %s:"
" link rate: new(0x%02x), old(0x%02x)\n", port_number,
handle, status_str, link_rate, prev_link_rate);
}
}
/**
* _scsih_pcie_topology_change_event - handle PCIe topology
* changes
* @ioc: per adapter object
* @fw_event: The fw_event_work object
* Context: user.
*
*/
static int
_scsih_pcie_topology_change_event(struct MPT3SAS_ADAPTER *ioc,
struct fw_event_work *fw_event)
{
int i;
u16 handle;
u16 reason_code;
u8 link_rate, prev_link_rate;
unsigned long flags;
int rc;
int requeue_event;
Mpi26EventDataPCIeTopologyChangeList_t *event_data =
(Mpi26EventDataPCIeTopologyChangeList_t *) fw_event->event_data;
struct _pcie_device *pcie_device;
if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
_scsih_pcie_topology_change_event_debug(ioc, event_data);
if (ioc->shost_recovery || ioc->remove_host ||
ioc->pci_error_recovery)
return 0;
if (fw_event->ignore) {
dewtprintk(ioc, pr_info(MPT3SAS_FMT "ignoring switch event\n",
ioc->name));
return 0;
}
/* handle siblings events */
for (i = 0; i < event_data->NumEntries; i++) {
if (fw_event->ignore) {
dewtprintk(ioc, pr_info(MPT3SAS_FMT
"ignoring switch event\n", ioc->name));
return 0;
}
if (ioc->remove_host || ioc->pci_error_recovery)
return 0;
reason_code = event_data->PortEntry[i].PortStatus;
handle =
le16_to_cpu(event_data->PortEntry[i].AttachedDevHandle);
if (!handle)
continue;
link_rate = event_data->PortEntry[i].CurrentPortInfo
& MPI26_EVENT_PCIE_TOPO_PI_RATE_MASK;
prev_link_rate = event_data->PortEntry[i].PreviousPortInfo
& MPI26_EVENT_PCIE_TOPO_PI_RATE_MASK;
switch (reason_code) {
case MPI26_EVENT_PCIE_TOPO_PS_PORT_CHANGED:
if (ioc->shost_recovery)
break;
if (link_rate == prev_link_rate)
break;
if (link_rate < MPI26_EVENT_PCIE_TOPO_PI_RATE_2_5)
break;
_scsih_pcie_check_device(ioc, handle);
/* This code after this point handles the test case
* where a device has been added, however its returning
* BUSY for sometime. Then before the Device Missing
* Delay expires and the device becomes READY, the
* device is removed and added back.
*/
spin_lock_irqsave(&ioc->pcie_device_lock, flags);
pcie_device = __mpt3sas_get_pdev_by_handle(ioc, handle);
spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
if (pcie_device) {
pcie_device_put(pcie_device);
break;
}
if (!test_bit(handle, ioc->pend_os_device_add))
break;
dewtprintk(ioc, pr_info(MPT3SAS_FMT
"handle(0x%04x) device not found: convert "
"event to a device add\n", ioc->name, handle));
event_data->PortEntry[i].PortStatus &= 0xF0;
event_data->PortEntry[i].PortStatus |=
MPI26_EVENT_PCIE_TOPO_PS_DEV_ADDED;
case MPI26_EVENT_PCIE_TOPO_PS_DEV_ADDED:
if (ioc->shost_recovery)
break;
if (link_rate < MPI26_EVENT_PCIE_TOPO_PI_RATE_2_5)
break;
rc = _scsih_pcie_add_device(ioc, handle);
if (!rc) {
/* mark entry vacant */
/* TODO This needs to be reviewed and fixed,
* we dont have an entry
* to make an event void like vacant
*/
event_data->PortEntry[i].PortStatus |=
MPI26_EVENT_PCIE_TOPO_PS_NO_CHANGE;
}
break;
case MPI26_EVENT_PCIE_TOPO_PS_NOT_RESPONDING:
_scsih_pcie_device_remove_by_handle(ioc, handle);
break;
}
}
return requeue_event;
}
/**
* _scsih_pcie_device_status_change_event_debug - debug for
* device event
* @event_data: event data payload
* Context: user.
*
* Return nothing.
*/
static void
_scsih_pcie_device_status_change_event_debug(struct MPT3SAS_ADAPTER *ioc,
Mpi26EventDataPCIeDeviceStatusChange_t *event_data)
{
char *reason_str = NULL;
switch (event_data->ReasonCode) {
case MPI26_EVENT_PCIDEV_STAT_RC_SMART_DATA:
reason_str = "smart data";
break;
case MPI26_EVENT_PCIDEV_STAT_RC_UNSUPPORTED:
reason_str = "unsupported device discovered";
break;
case MPI26_EVENT_PCIDEV_STAT_RC_INTERNAL_DEVICE_RESET:
reason_str = "internal device reset";
break;
case MPI26_EVENT_PCIDEV_STAT_RC_TASK_ABORT_INTERNAL:
reason_str = "internal task abort";
break;
case MPI26_EVENT_PCIDEV_STAT_RC_ABORT_TASK_SET_INTERNAL:
reason_str = "internal task abort set";
break;
case MPI26_EVENT_PCIDEV_STAT_RC_CLEAR_TASK_SET_INTERNAL:
reason_str = "internal clear task set";
break;
case MPI26_EVENT_PCIDEV_STAT_RC_QUERY_TASK_INTERNAL:
reason_str = "internal query task";
break;
case MPI26_EVENT_PCIDEV_STAT_RC_DEV_INIT_FAILURE:
reason_str = "device init failure";
break;
case MPI26_EVENT_PCIDEV_STAT_RC_CMP_INTERNAL_DEV_RESET:
reason_str = "internal device reset complete";
break;
case MPI26_EVENT_PCIDEV_STAT_RC_CMP_TASK_ABORT_INTERNAL:
reason_str = "internal task abort complete";
break;
case MPI26_EVENT_PCIDEV_STAT_RC_ASYNC_NOTIFICATION:
reason_str = "internal async notification";
break;
default:
reason_str = "unknown reason";
break;
}
pr_info(MPT3SAS_FMT "PCIE device status change: (%s)\n"
"\thandle(0x%04x), WWID(0x%016llx), tag(%d)",
ioc->name, reason_str, le16_to_cpu(event_data->DevHandle),
(unsigned long long)le64_to_cpu(event_data->WWID),
le16_to_cpu(event_data->TaskTag));
if (event_data->ReasonCode == MPI26_EVENT_PCIDEV_STAT_RC_SMART_DATA)
pr_info(MPT3SAS_FMT ", ASC(0x%x), ASCQ(0x%x)\n", ioc->name,
event_data->ASC, event_data->ASCQ);
pr_info("\n");
}
/**
* _scsih_pcie_device_status_change_event - handle device status
* change
* @ioc: per adapter object
* @fw_event: The fw_event_work object
* Context: user.
*
* Return nothing.
*/
static void
_scsih_pcie_device_status_change_event(struct MPT3SAS_ADAPTER *ioc,
struct fw_event_work *fw_event)
{
struct MPT3SAS_TARGET *target_priv_data;
struct _pcie_device *pcie_device;
u64 wwid;
unsigned long flags;
Mpi26EventDataPCIeDeviceStatusChange_t *event_data =
(Mpi26EventDataPCIeDeviceStatusChange_t *)fw_event->event_data;
if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
_scsih_pcie_device_status_change_event_debug(ioc,
event_data);
if (event_data->ReasonCode !=
MPI26_EVENT_PCIDEV_STAT_RC_INTERNAL_DEVICE_RESET &&
event_data->ReasonCode !=
MPI26_EVENT_PCIDEV_STAT_RC_CMP_INTERNAL_DEV_RESET)
return;
spin_lock_irqsave(&ioc->pcie_device_lock, flags);
wwid = le64_to_cpu(event_data->WWID);
pcie_device = __mpt3sas_get_pdev_by_wwid(ioc, wwid);
if (!pcie_device || !pcie_device->starget)
goto out;
target_priv_data = pcie_device->starget->hostdata;
if (!target_priv_data)
goto out;
if (event_data->ReasonCode ==
MPI26_EVENT_PCIDEV_STAT_RC_INTERNAL_DEVICE_RESET)
target_priv_data->tm_busy = 1;
else
target_priv_data->tm_busy = 0;
out:
if (pcie_device)
pcie_device_put(pcie_device);
spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
}
/**
* _scsih_sas_enclosure_dev_status_change_event_debug - debug for enclosure
* event
@ -7152,6 +7568,34 @@ _scsih_sas_discovery_event(struct MPT3SAS_ADAPTER *ioc,
}
}
/**
* _scsih_pcie_enumeration_event - handle enumeration events
* @ioc: per adapter object
* @fw_event: The fw_event_work object
* Context: user.
*
* Return nothing.
*/
static void
_scsih_pcie_enumeration_event(struct MPT3SAS_ADAPTER *ioc,
struct fw_event_work *fw_event)
{
Mpi26EventDataPCIeEnumeration_t *event_data =
(Mpi26EventDataPCIeEnumeration_t *)fw_event->event_data;
if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) {
pr_info(MPT3SAS_FMT "pcie enumeration event: (%s) Flag 0x%02x",
ioc->name,
((event_data->ReasonCode ==
MPI26_EVENT_PCIE_ENUM_RC_STARTED) ?
"started" : "completed"), event_data->Flags);
if (event_data->EnumerationStatus)
pr_info("enumeration_status(0x%08x)",
le32_to_cpu(event_data->EnumerationStatus));
pr_info("\n");
}
}
/**
* _scsih_ir_fastpath - turn on fastpath for IR physdisk
* @ioc: per adapter object
@ -8808,6 +9252,16 @@ _mpt3sas_fw_work(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event)
case MPI2_EVENT_IR_OPERATION_STATUS:
_scsih_sas_ir_operation_status_event(ioc, fw_event);
break;
case MPI2_EVENT_PCIE_DEVICE_STATUS_CHANGE:
_scsih_pcie_device_status_change_event(ioc, fw_event);
break;
case MPI2_EVENT_PCIE_ENUMERATION:
_scsih_pcie_enumeration_event(ioc, fw_event);
break;
case MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST:
_scsih_pcie_topology_change_event(ioc, fw_event);
return;
break;
}
out:
fw_event_work_put(fw_event);
@ -8898,6 +9352,11 @@ mpt3sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index,
(Mpi2EventDataSasTopologyChangeList_t *)
mpi_reply->EventData);
break;
case MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST:
_scsih_check_pcie_topo_remove_events(ioc,
(Mpi26EventDataPCIeTopologyChangeList_t *)
mpi_reply->EventData);
break;
case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST:
_scsih_check_ir_config_unhide_events(ioc,
(Mpi2EventDataIrConfigChangeList_t *)
@ -8960,6 +9419,8 @@ mpt3sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index,
case MPI2_EVENT_SAS_DISCOVERY:
case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE:
case MPI2_EVENT_IR_PHYSICAL_DISK:
case MPI2_EVENT_PCIE_ENUMERATION:
case MPI2_EVENT_PCIE_DEVICE_STATUS_CHANGE:
break;
case MPI2_EVENT_TEMP_THRESHOLD: