diff options
Diffstat (limited to 'drivers/scsi/mpt3sas/mpt3sas_scsih.c')
-rw-r--r-- | drivers/scsi/mpt3sas/mpt3sas_scsih.c | 2219 |
1 files changed, 2003 insertions, 216 deletions
diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 22998cbd538f..b258f210120a 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -60,6 +60,9 @@ #include "mpt3sas_base.h" #define RAID_CHANNEL 1 + +#define PCIE_CHANNEL 2 + /* forward proto's */ static void _scsih_expander_node_remove(struct MPT3SAS_ADAPTER *ioc, struct _sas_node *sas_expander); @@ -69,7 +72,11 @@ static void _scsih_remove_device(struct MPT3SAS_ADAPTER *ioc, struct _sas_device *sas_device); static int _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 retry_count, u8 is_pd); - +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 */ @@ -281,7 +288,7 @@ struct _scsi_io_transfer { * Note: The logging levels are defined in mpt3sas_debug.h. */ static int -_scsih_set_debug_level(const char *val, struct kernel_param *kp) +_scsih_set_debug_level(const char *val, const struct kernel_param *kp) { int ret = param_set_int(val, kp); struct MPT3SAS_ADAPTER *ioc; @@ -406,11 +413,6 @@ _scsih_get_sas_address(struct MPT3SAS_ADAPTER *ioc, u16 handle, *sas_address = 0; - if (handle <= ioc->sas_hba.num_phys) { - *sas_address = ioc->sas_hba.sas_address; - return 0; - } - if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ioc->name, @@ -420,7 +422,15 @@ _scsih_get_sas_address(struct MPT3SAS_ADAPTER *ioc, u16 handle, ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { - *sas_address = le64_to_cpu(sas_device_pg0.SASAddress); + /* For HBA, vSES doesn't return HBA SAS address. Instead return + * vSES's sas address. + */ + if ((handle <= ioc->sas_hba.num_phys) && + (!(le32_to_cpu(sas_device_pg0.DeviceInfo) & + MPI2_SAS_DEVICE_INFO_SEP))) + *sas_address = ioc->sas_hba.sas_address; + else + *sas_address = le64_to_cpu(sas_device_pg0.SASAddress); return 0; } @@ -439,21 +449,22 @@ _scsih_get_sas_address(struct MPT3SAS_ADAPTER *ioc, u16 handle, /** * _scsih_determine_boot_device - determine boot device. * @ioc: per adapter object - * @device: either sas_device or raid_device object - * @is_raid: [flag] 1 = raid object, 0 = sas object + * @device: sas_device or pcie_device object + * @channel: SAS or PCIe channel * * Determines whether this device should be first reported device to * to scsi-ml or sas transport, this purpose is for persistent boot device. * There are primary, alternate, and current entries in bios page 2. The order * priority is primary, alternate, then current. This routine saves - * the corresponding device object and is_raid flag in the ioc object. + * the corresponding device object. * The saved data to be used later in _scsih_probe_boot_devices(). */ static void -_scsih_determine_boot_device(struct MPT3SAS_ADAPTER *ioc, - void *device, u8 is_raid) +_scsih_determine_boot_device(struct MPT3SAS_ADAPTER *ioc, void *device, + u32 channel) { struct _sas_device *sas_device; + struct _pcie_device *pcie_device; struct _raid_device *raid_device; u64 sas_address; u64 device_name; @@ -468,18 +479,24 @@ _scsih_determine_boot_device(struct MPT3SAS_ADAPTER *ioc, if (!ioc->bios_pg3.BiosVersion) return; - if (!is_raid) { - sas_device = device; - sas_address = sas_device->sas_address; - device_name = sas_device->device_name; - enclosure_logical_id = sas_device->enclosure_logical_id; - slot = sas_device->slot; - } else { + if (channel == RAID_CHANNEL) { raid_device = device; sas_address = raid_device->wwid; device_name = 0; enclosure_logical_id = 0; slot = 0; + } else if (channel == PCIE_CHANNEL) { + pcie_device = device; + sas_address = pcie_device->wwid; + device_name = 0; + enclosure_logical_id = 0; + slot = 0; + } else { + sas_device = device; + sas_address = sas_device->sas_address; + device_name = sas_device->device_name; + enclosure_logical_id = sas_device->enclosure_logical_id; + slot = sas_device->slot; } if (!ioc->req_boot_device.device) { @@ -493,7 +510,7 @@ _scsih_determine_boot_device(struct MPT3SAS_ADAPTER *ioc, ioc->name, __func__, (unsigned long long)sas_address)); ioc->req_boot_device.device = device; - ioc->req_boot_device.is_raid = is_raid; + ioc->req_boot_device.channel = channel; } } @@ -508,7 +525,7 @@ _scsih_determine_boot_device(struct MPT3SAS_ADAPTER *ioc, ioc->name, __func__, (unsigned long long)sas_address)); ioc->req_alt_boot_device.device = device; - ioc->req_alt_boot_device.is_raid = is_raid; + ioc->req_alt_boot_device.channel = channel; } } @@ -523,7 +540,7 @@ _scsih_determine_boot_device(struct MPT3SAS_ADAPTER *ioc, ioc->name, __func__, (unsigned long long)sas_address)); ioc->current_boot_device.device = device; - ioc->current_boot_device.is_raid = is_raid; + ioc->current_boot_device.channel = channel; } } } @@ -536,7 +553,7 @@ __mpt3sas_get_sdev_from_target(struct MPT3SAS_ADAPTER *ioc, assert_spin_locked(&ioc->sas_device_lock); - ret = tgt_priv->sdev; + ret = tgt_priv->sas_dev; if (ret) sas_device_get(ret); @@ -557,6 +574,44 @@ mpt3sas_get_sdev_from_target(struct MPT3SAS_ADAPTER *ioc, return ret; } +static struct _pcie_device * +__mpt3sas_get_pdev_from_target(struct MPT3SAS_ADAPTER *ioc, + struct MPT3SAS_TARGET *tgt_priv) +{ + struct _pcie_device *ret; + + assert_spin_locked(&ioc->pcie_device_lock); + + ret = tgt_priv->pcie_dev; + if (ret) + pcie_device_get(ret); + + return ret; +} + +/** + * mpt3sas_get_pdev_from_target - pcie device search + * @ioc: per adapter object + * @tgt_priv: starget private object + * + * Context: This function will acquire ioc->pcie_device_lock and will release + * before returning the pcie_device object. + * + * This searches for pcie_device from target, then return pcie_device object. + */ +static struct _pcie_device * +mpt3sas_get_pdev_from_target(struct MPT3SAS_ADAPTER *ioc, + struct MPT3SAS_TARGET *tgt_priv) +{ + struct _pcie_device *ret; + unsigned long flags; + + spin_lock_irqsave(&ioc->pcie_device_lock, flags); + ret = __mpt3sas_get_pdev_from_target(ioc, tgt_priv); + spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); + + return ret; +} struct _sas_device * __mpt3sas_get_sdev_by_addr(struct MPT3SAS_ADAPTER *ioc, @@ -636,7 +691,7 @@ found_device: * This searches for sas_device based on sas_address, then return sas_device * object. */ -static struct _sas_device * +struct _sas_device * mpt3sas_get_sdev_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) { struct _sas_device *sas_device; @@ -650,6 +705,69 @@ mpt3sas_get_sdev_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) } /** + * _scsih_display_enclosure_chassis_info - display device location info + * @ioc: per adapter object + * @sas_device: per sas device object + * @sdev: scsi device struct + * @starget: scsi target struct + * + * Returns nothing. + */ +static void +_scsih_display_enclosure_chassis_info(struct MPT3SAS_ADAPTER *ioc, + struct _sas_device *sas_device, struct scsi_device *sdev, + struct scsi_target *starget) +{ + if (sdev) { + if (sas_device->enclosure_handle != 0) + sdev_printk(KERN_INFO, sdev, + "enclosure logical id (0x%016llx), slot(%d) \n", + (unsigned long long) + sas_device->enclosure_logical_id, + sas_device->slot); + if (sas_device->connector_name[0] != '\0') + sdev_printk(KERN_INFO, sdev, + "enclosure level(0x%04x), connector name( %s)\n", + sas_device->enclosure_level, + sas_device->connector_name); + if (sas_device->is_chassis_slot_valid) + sdev_printk(KERN_INFO, sdev, "chassis slot(0x%04x)\n", + sas_device->chassis_slot); + } else if (starget) { + if (sas_device->enclosure_handle != 0) + starget_printk(KERN_INFO, starget, + "enclosure logical id(0x%016llx), slot(%d) \n", + (unsigned long long) + sas_device->enclosure_logical_id, + sas_device->slot); + if (sas_device->connector_name[0] != '\0') + starget_printk(KERN_INFO, starget, + "enclosure level(0x%04x), connector name( %s)\n", + sas_device->enclosure_level, + sas_device->connector_name); + if (sas_device->is_chassis_slot_valid) + starget_printk(KERN_INFO, starget, + "chassis slot(0x%04x)\n", + sas_device->chassis_slot); + } else { + if (sas_device->enclosure_handle != 0) + pr_info(MPT3SAS_FMT + "enclosure logical id(0x%016llx), slot(%d) \n", + ioc->name, (unsigned long long) + sas_device->enclosure_logical_id, + sas_device->slot); + if (sas_device->connector_name[0] != '\0') + pr_info(MPT3SAS_FMT + "enclosure level(0x%04x), connector name( %s)\n", + ioc->name, sas_device->enclosure_level, + sas_device->connector_name); + if (sas_device->is_chassis_slot_valid) + pr_info(MPT3SAS_FMT "chassis slot(0x%04x)\n", + ioc->name, sas_device->chassis_slot); + } +} + +/** * _scsih_sas_device_remove - remove sas_device from list. * @ioc: per adapter object * @sas_device: the sas_device object @@ -670,17 +788,7 @@ _scsih_sas_device_remove(struct MPT3SAS_ADAPTER *ioc, ioc->name, sas_device->handle, (unsigned long long) sas_device->sas_address); - if (sas_device->enclosure_handle != 0) - pr_info(MPT3SAS_FMT - "removing enclosure logical id(0x%016llx), slot(%d)\n", - ioc->name, (unsigned long long) - sas_device->enclosure_logical_id, sas_device->slot); - - if (sas_device->connector_name[0] != '\0') - pr_info(MPT3SAS_FMT - "removing enclosure level(0x%04x), connector name( %s)\n", - ioc->name, sas_device->enclosure_level, - sas_device->connector_name); + _scsih_display_enclosure_chassis_info(ioc, sas_device, NULL, NULL); /* * The lock serializes access to the list, but we still need to verify @@ -772,17 +880,8 @@ _scsih_sas_device_add(struct MPT3SAS_ADAPTER *ioc, ioc->name, __func__, sas_device->handle, (unsigned long long)sas_device->sas_address)); - if (sas_device->enclosure_handle != 0) - dewtprintk(ioc, pr_info(MPT3SAS_FMT - "%s: enclosure logical id(0x%016llx), slot( %d)\n", - ioc->name, __func__, (unsigned long long) - sas_device->enclosure_logical_id, sas_device->slot)); - - if (sas_device->connector_name[0] != '\0') - dewtprintk(ioc, pr_info(MPT3SAS_FMT - "%s: enclosure level(0x%04x), connector name( %s)\n", - ioc->name, __func__, - sas_device->enclosure_level, sas_device->connector_name)); + dewtprintk(ioc, _scsih_display_enclosure_chassis_info(ioc, sas_device, + NULL, NULL)); spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device_get(sas_device); @@ -832,17 +931,8 @@ _scsih_sas_device_init_add(struct MPT3SAS_ADAPTER *ioc, __func__, sas_device->handle, (unsigned long long)sas_device->sas_address)); - if (sas_device->enclosure_handle != 0) - dewtprintk(ioc, pr_info(MPT3SAS_FMT - "%s: enclosure logical id(0x%016llx), slot( %d)\n", - ioc->name, __func__, (unsigned long long) - sas_device->enclosure_logical_id, sas_device->slot)); - - if (sas_device->connector_name[0] != '\0') - dewtprintk(ioc, pr_info(MPT3SAS_FMT - "%s: enclosure level(0x%04x), connector name( %s)\n", - ioc->name, __func__, sas_device->enclosure_level, - sas_device->connector_name)); + dewtprintk(ioc, _scsih_display_enclosure_chassis_info(ioc, sas_device, + NULL, NULL)); spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device_get(sas_device); @@ -851,6 +941,282 @@ _scsih_sas_device_init_add(struct MPT3SAS_ADAPTER *ioc, spin_unlock_irqrestore(&ioc->sas_device_lock, flags); } + +static struct _pcie_device * +__mpt3sas_get_pdev_by_wwid(struct MPT3SAS_ADAPTER *ioc, u64 wwid) +{ + struct _pcie_device *pcie_device; + + assert_spin_locked(&ioc->pcie_device_lock); + + list_for_each_entry(pcie_device, &ioc->pcie_device_list, list) + if (pcie_device->wwid == wwid) + goto found_device; + + list_for_each_entry(pcie_device, &ioc->pcie_device_init_list, list) + if (pcie_device->wwid == wwid) + goto found_device; + + return NULL; + +found_device: + pcie_device_get(pcie_device); + return pcie_device; +} + + +/** + * mpt3sas_get_pdev_by_wwid - pcie device search + * @ioc: per adapter object + * @wwid: wwid + * + * Context: This function will acquire ioc->pcie_device_lock and will release + * before returning the pcie_device object. + * + * This searches for pcie_device based on wwid, then return pcie_device object. + */ +static struct _pcie_device * +mpt3sas_get_pdev_by_wwid(struct MPT3SAS_ADAPTER *ioc, u64 wwid) +{ + struct _pcie_device *pcie_device; + unsigned long flags; + + spin_lock_irqsave(&ioc->pcie_device_lock, flags); + pcie_device = __mpt3sas_get_pdev_by_wwid(ioc, wwid); + spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); + + return pcie_device; +} + + +static struct _pcie_device * +__mpt3sas_get_pdev_by_idchannel(struct MPT3SAS_ADAPTER *ioc, int id, + int channel) +{ + struct _pcie_device *pcie_device; + + assert_spin_locked(&ioc->pcie_device_lock); + + list_for_each_entry(pcie_device, &ioc->pcie_device_list, list) + if (pcie_device->id == id && pcie_device->channel == channel) + goto found_device; + + list_for_each_entry(pcie_device, &ioc->pcie_device_init_list, list) + if (pcie_device->id == id && pcie_device->channel == channel) + goto found_device; + + return NULL; + +found_device: + pcie_device_get(pcie_device); + return pcie_device; +} + +static struct _pcie_device * +__mpt3sas_get_pdev_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) +{ + struct _pcie_device *pcie_device; + + assert_spin_locked(&ioc->pcie_device_lock); + + list_for_each_entry(pcie_device, &ioc->pcie_device_list, list) + if (pcie_device->handle == handle) + goto found_device; + + list_for_each_entry(pcie_device, &ioc->pcie_device_init_list, list) + if (pcie_device->handle == handle) + goto found_device; + + return NULL; + +found_device: + pcie_device_get(pcie_device); + return pcie_device; +} + + +/** + * mpt3sas_get_pdev_by_handle - pcie device search + * @ioc: per adapter object + * @handle: Firmware device handle + * + * Context: This function will acquire ioc->pcie_device_lock and will release + * before returning the pcie_device object. + * + * This searches for pcie_device based on handle, then return pcie_device + * object. + */ +struct _pcie_device * +mpt3sas_get_pdev_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) +{ + struct _pcie_device *pcie_device; + unsigned long flags; + + 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); + + return pcie_device; +} + +/** + * _scsih_pcie_device_remove - remove pcie_device from list. + * @ioc: per adapter object + * @pcie_device: the pcie_device object + * Context: This function will acquire ioc->pcie_device_lock. + * + * If pcie_device is on the list, remove it and decrement its reference count. + */ +static void +_scsih_pcie_device_remove(struct MPT3SAS_ADAPTER *ioc, + struct _pcie_device *pcie_device) +{ + unsigned long flags; + int was_on_pcie_device_list = 0; + + if (!pcie_device) + return; + pr_info(MPT3SAS_FMT + "removing handle(0x%04x), wwid(0x%016llx)\n", + ioc->name, pcie_device->handle, + (unsigned long long) pcie_device->wwid); + if (pcie_device->enclosure_handle != 0) + pr_info(MPT3SAS_FMT + "removing enclosure logical id(0x%016llx), slot(%d)\n", + ioc->name, + (unsigned long long)pcie_device->enclosure_logical_id, + pcie_device->slot); + if (pcie_device->connector_name[0] != '\0') + pr_info(MPT3SAS_FMT + "removing enclosure level(0x%04x), connector name( %s)\n", + ioc->name, pcie_device->enclosure_level, + pcie_device->connector_name); + + spin_lock_irqsave(&ioc->pcie_device_lock, flags); + if (!list_empty(&pcie_device->list)) { + list_del_init(&pcie_device->list); + was_on_pcie_device_list = 1; + } + spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); + if (was_on_pcie_device_list) { + kfree(pcie_device->serial_number); + pcie_device_put(pcie_device); + } +} + + +/** + * _scsih_pcie_device_remove_by_handle - removing pcie device object by handle + * @ioc: per adapter object + * @handle: device handle + * + * Return nothing. + */ +static void +_scsih_pcie_device_remove_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) +{ + struct _pcie_device *pcie_device; + unsigned long flags; + int was_on_pcie_device_list = 0; + + if (ioc->shost_recovery) + return; + + spin_lock_irqsave(&ioc->pcie_device_lock, flags); + pcie_device = __mpt3sas_get_pdev_by_handle(ioc, handle); + if (pcie_device) { + if (!list_empty(&pcie_device->list)) { + list_del_init(&pcie_device->list); + was_on_pcie_device_list = 1; + pcie_device_put(pcie_device); + } + } + spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); + if (was_on_pcie_device_list) { + _scsih_pcie_device_remove_from_sml(ioc, pcie_device); + pcie_device_put(pcie_device); + } +} + +/** + * _scsih_pcie_device_add - add pcie_device object + * @ioc: per adapter object + * @pcie_device: pcie_device object + * + * This is added to the pcie_device_list link list. + */ +static void +_scsih_pcie_device_add(struct MPT3SAS_ADAPTER *ioc, + struct _pcie_device *pcie_device) +{ + unsigned long flags; + + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: handle (0x%04x), wwid(0x%016llx)\n", ioc->name, __func__, + pcie_device->handle, (unsigned long long)pcie_device->wwid)); + if (pcie_device->enclosure_handle != 0) + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: enclosure logical id(0x%016llx), slot( %d)\n", + ioc->name, __func__, + (unsigned long long)pcie_device->enclosure_logical_id, + pcie_device->slot)); + if (pcie_device->connector_name[0] != '\0') + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: enclosure level(0x%04x), connector name( %s)\n", + ioc->name, __func__, pcie_device->enclosure_level, + pcie_device->connector_name)); + + spin_lock_irqsave(&ioc->pcie_device_lock, flags); + pcie_device_get(pcie_device); + list_add_tail(&pcie_device->list, &ioc->pcie_device_list); + spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); + + if (scsi_add_device(ioc->shost, PCIE_CHANNEL, pcie_device->id, 0)) { + _scsih_pcie_device_remove(ioc, pcie_device); + } else if (!pcie_device->starget) { + if (!ioc->is_driver_loading) { +/*TODO-- Need to find out whether this condition will occur or not*/ + clear_bit(pcie_device->handle, ioc->pend_os_device_add); + } + } else + clear_bit(pcie_device->handle, ioc->pend_os_device_add); +} + +/* + * _scsih_pcie_device_init_add - insert pcie_device to the init list. + * @ioc: per adapter object + * @pcie_device: the pcie_device object + * Context: This function will acquire ioc->pcie_device_lock. + * + * Adding new object at driver load time to the ioc->pcie_device_init_list. + */ +static void +_scsih_pcie_device_init_add(struct MPT3SAS_ADAPTER *ioc, + struct _pcie_device *pcie_device) +{ + unsigned long flags; + + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: handle (0x%04x), wwid(0x%016llx)\n", ioc->name, __func__, + pcie_device->handle, (unsigned long long)pcie_device->wwid)); + if (pcie_device->enclosure_handle != 0) + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: enclosure logical id(0x%016llx), slot( %d)\n", + ioc->name, __func__, + (unsigned long long)pcie_device->enclosure_logical_id, + pcie_device->slot)); + if (pcie_device->connector_name[0] != '\0') + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: enclosure level(0x%04x), connector name( %s)\n", + ioc->name, __func__, pcie_device->enclosure_level, + pcie_device->connector_name)); + + spin_lock_irqsave(&ioc->pcie_device_lock, flags); + pcie_device_get(pcie_device); + list_add_tail(&pcie_device->list, &ioc->pcie_device_init_list); + _scsih_determine_boot_device(ioc, pcie_device, PCIE_CHANNEL); + spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); +} /** * _scsih_raid_device_find_by_id - raid device search * @ioc: per adapter object @@ -1062,6 +1428,23 @@ _scsih_is_end_device(u32 device_info) } /** + * _scsih_is_nvme_device - determines if device is an nvme device + * @device_info: bitfield providing information about the device. + * Context: none + * + * Returns 1 if nvme device. + */ +static int +_scsih_is_nvme_device(u32 device_info) +{ + if ((device_info & MPI26_PCIE_DEVINFO_MASK_DEVICE_TYPE) + == MPI26_PCIE_DEVINFO_NVME) + return 1; + else + return 0; +} + +/** * _scsih_scsi_lookup_get - returns scmd entry * @ioc: per adapter object * @smid: system request message index @@ -1278,6 +1661,7 @@ scsih_target_alloc(struct scsi_target *starget) struct MPT3SAS_TARGET *sas_target_priv_data; struct _sas_device *sas_device; struct _raid_device *raid_device; + struct _pcie_device *pcie_device; unsigned long flags; struct sas_rphy *rphy; @@ -1307,6 +1691,28 @@ scsih_target_alloc(struct scsi_target *starget) return 0; } + /* PCIe devices */ + if (starget->channel == PCIE_CHANNEL) { + spin_lock_irqsave(&ioc->pcie_device_lock, flags); + pcie_device = __mpt3sas_get_pdev_by_idchannel(ioc, starget->id, + starget->channel); + if (pcie_device) { + sas_target_priv_data->handle = pcie_device->handle; + sas_target_priv_data->sas_address = pcie_device->wwid; + sas_target_priv_data->pcie_dev = pcie_device; + pcie_device->starget = starget; + pcie_device->id = starget->id; + pcie_device->channel = starget->channel; + sas_target_priv_data->flags |= + MPT_TARGET_FLAGS_PCIE_DEVICE; + if (pcie_device->fast_path) + sas_target_priv_data->flags |= + MPT_TARGET_FASTPATH_IO; + } + spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); + return 0; + } + /* sas/sata devices */ spin_lock_irqsave(&ioc->sas_device_lock, flags); rphy = dev_to_rphy(starget->dev.parent); @@ -1316,7 +1722,7 @@ scsih_target_alloc(struct scsi_target *starget) if (sas_device) { sas_target_priv_data->handle = sas_device->handle; sas_target_priv_data->sas_address = sas_device->sas_address; - sas_target_priv_data->sdev = sas_device; + sas_target_priv_data->sas_dev = sas_device; sas_device->starget = starget; sas_device->id = starget->id; sas_device->channel = starget->channel; @@ -1324,7 +1730,8 @@ scsih_target_alloc(struct scsi_target *starget) sas_target_priv_data->flags |= MPT_TARGET_FLAGS_RAID_COMPONENT; if (sas_device->fast_path) - sas_target_priv_data->flags |= MPT_TARGET_FASTPATH_IO; + sas_target_priv_data->flags |= + MPT_TARGET_FASTPATH_IO; } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); @@ -1345,7 +1752,9 @@ scsih_target_destroy(struct scsi_target *starget) struct MPT3SAS_TARGET *sas_target_priv_data; struct _sas_device *sas_device; struct _raid_device *raid_device; + struct _pcie_device *pcie_device; unsigned long flags; + struct sas_rphy *rphy; sas_target_priv_data = starget->hostdata; if (!sas_target_priv_data) @@ -1363,7 +1772,29 @@ scsih_target_destroy(struct scsi_target *starget) goto out; } + if (starget->channel == PCIE_CHANNEL) { + spin_lock_irqsave(&ioc->pcie_device_lock, flags); + pcie_device = __mpt3sas_get_pdev_from_target(ioc, + sas_target_priv_data); + if (pcie_device && (pcie_device->starget == starget) && + (pcie_device->id == starget->id) && + (pcie_device->channel == starget->channel)) + pcie_device->starget = NULL; + + if (pcie_device) { + /* + * Corresponding get() is in _scsih_target_alloc() + */ + sas_target_priv_data->pcie_dev = NULL; + pcie_device_put(pcie_device); + pcie_device_put(pcie_device); + } + spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); + goto out; + } + spin_lock_irqsave(&ioc->sas_device_lock, flags); + rphy = dev_to_rphy(starget->dev.parent); sas_device = __mpt3sas_get_sdev_from_target(ioc, sas_target_priv_data); if (sas_device && (sas_device->starget == starget) && (sas_device->id == starget->id) && @@ -1374,7 +1805,7 @@ scsih_target_destroy(struct scsi_target *starget) /* * Corresponding get() is in _scsih_target_alloc() */ - sas_target_priv_data->sdev = NULL; + sas_target_priv_data->sas_dev = NULL; sas_device_put(sas_device); sas_device_put(sas_device); @@ -1403,6 +1834,7 @@ scsih_slave_alloc(struct scsi_device *sdev) struct scsi_target *starget; struct _raid_device *raid_device; struct _sas_device *sas_device; + struct _pcie_device *pcie_device; unsigned long flags; sas_device_priv_data = kzalloc(sizeof(*sas_device_priv_data), @@ -1431,8 +1863,22 @@ scsih_slave_alloc(struct scsi_device *sdev) raid_device->sdev = sdev; /* raid is single lun */ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); } + if (starget->channel == PCIE_CHANNEL) { + spin_lock_irqsave(&ioc->pcie_device_lock, flags); + pcie_device = __mpt3sas_get_pdev_by_wwid(ioc, + sas_target_priv_data->sas_address); + if (pcie_device && (pcie_device->starget == NULL)) { + sdev_printk(KERN_INFO, sdev, + "%s : pcie_device->starget set to starget @ %d\n", + __func__, __LINE__); + pcie_device->starget = starget; + } + + if (pcie_device) + pcie_device_put(pcie_device); + spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); - if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) { + } else if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) { spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = __mpt3sas_get_sdev_by_addr(ioc, sas_target_priv_data->sas_address); @@ -1466,6 +1912,7 @@ scsih_slave_destroy(struct scsi_device *sdev) struct Scsi_Host *shost; struct MPT3SAS_ADAPTER *ioc; struct _sas_device *sas_device; + struct _pcie_device *pcie_device; unsigned long flags; if (!sdev->hostdata) @@ -1478,7 +1925,19 @@ scsih_slave_destroy(struct scsi_device *sdev) shost = dev_to_shost(&starget->dev); ioc = shost_priv(shost); - if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) { + if (sas_target_priv_data->flags & MPT_TARGET_FLAGS_PCIE_DEVICE) { + spin_lock_irqsave(&ioc->pcie_device_lock, flags); + pcie_device = __mpt3sas_get_pdev_from_target(ioc, + sas_target_priv_data); + if (pcie_device && !sas_target_priv_data->num_luns) + pcie_device->starget = NULL; + + if (pcie_device) + pcie_device_put(pcie_device); + + spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); + + } else if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) { spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = __mpt3sas_get_sdev_from_target(ioc, sas_target_priv_data); @@ -1562,6 +2021,14 @@ scsih_is_raid(struct device *dev) return (sdev->channel == RAID_CHANNEL) ? 1 : 0; } +static int +scsih_is_nvme(struct device *dev) +{ + struct scsi_device *sdev = to_scsi_device(dev); + + return (sdev->channel == PCIE_CHANNEL) ? 1 : 0; +} + /** * scsih_get_resync - get raid volume resync percent complete * @dev the device struct object @@ -1837,6 +2304,7 @@ scsih_slave_configure(struct scsi_device *sdev) struct MPT3SAS_DEVICE *sas_device_priv_data; struct MPT3SAS_TARGET *sas_target_priv_data; struct _sas_device *sas_device; + struct _pcie_device *pcie_device; struct _raid_device *raid_device; unsigned long flags; int qdepth; @@ -1967,6 +2435,55 @@ scsih_slave_configure(struct scsi_device *sdev) } } + /* PCIe handling */ + if (sas_target_priv_data->flags & MPT_TARGET_FLAGS_PCIE_DEVICE) { + spin_lock_irqsave(&ioc->pcie_device_lock, flags); + pcie_device = __mpt3sas_get_pdev_by_wwid(ioc, + sas_device_priv_data->sas_target->sas_address); + if (!pcie_device) { + spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); + dfailprintk(ioc, pr_warn(MPT3SAS_FMT + "failure at %s:%d/%s()!\n", ioc->name, __FILE__, + __LINE__, __func__)); + return 1; + } + + qdepth = MPT3SAS_NVME_QUEUE_DEPTH; + ds = "NVMe"; + sdev_printk(KERN_INFO, sdev, + "%s: handle(0x%04x), wwid(0x%016llx), port(%d)\n", + ds, handle, (unsigned long long)pcie_device->wwid, + pcie_device->port_num); + if (pcie_device->enclosure_handle != 0) + sdev_printk(KERN_INFO, sdev, + "%s: enclosure logical id(0x%016llx), slot(%d)\n", + ds, + (unsigned long long)pcie_device->enclosure_logical_id, + pcie_device->slot); + if (pcie_device->connector_name[0] != '\0') + sdev_printk(KERN_INFO, sdev, + "%s: enclosure level(0x%04x)," + "connector name( %s)\n", ds, + pcie_device->enclosure_level, + pcie_device->connector_name); + pcie_device_put(pcie_device); + spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); + scsih_change_queue_depth(sdev, qdepth); + + if (pcie_device->nvme_mdts) + blk_queue_max_hw_sectors(sdev->request_queue, + pcie_device->nvme_mdts/512); + /* Enable QUEUE_FLAG_NOMERGES flag, so that IOs won't be + ** merged and can eliminate holes created during merging + ** operation. + **/ + queue_flag_set_unlocked(QUEUE_FLAG_NOMERGES, + sdev->request_queue); + blk_queue_virt_boundary(sdev->request_queue, + ioc->page_size - 1); + return 0; + } + spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = __mpt3sas_get_sdev_by_addr(ioc, sas_device_priv_data->sas_target->sas_address); @@ -2005,16 +2522,8 @@ scsih_slave_configure(struct scsi_device *sdev) "sas_addr(0x%016llx), phy(%d), device_name(0x%016llx)\n", ds, handle, (unsigned long long)sas_device->sas_address, sas_device->phy, (unsigned long long)sas_device->device_name); - if (sas_device->enclosure_handle != 0) - sdev_printk(KERN_INFO, sdev, - "%s: enclosure_logical_id(0x%016llx), slot(%d)\n", - ds, (unsigned long long) - sas_device->enclosure_logical_id, sas_device->slot); - if (sas_device->connector_name[0] != '\0') - sdev_printk(KERN_INFO, sdev, - "%s: enclosure level(0x%04x), connector name( %s)\n", - ds, sas_device->enclosure_level, - sas_device->connector_name); + + _scsih_display_enclosure_chassis_info(NULL, sas_device, sdev, NULL); sas_device_put(sas_device); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); @@ -2400,6 +2909,7 @@ _scsih_tm_display_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd) struct scsi_target *starget = scmd->device->sdev_target; struct MPT3SAS_TARGET *priv_target = starget->hostdata; struct _sas_device *sas_device = NULL; + struct _pcie_device *pcie_device = NULL; unsigned long flags; char *device_str = NULL; @@ -2416,6 +2926,31 @@ _scsih_tm_display_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd) "%s handle(0x%04x), %s wwid(0x%016llx)\n", device_str, priv_target->handle, device_str, (unsigned long long)priv_target->sas_address); + + } else if (priv_target->flags & MPT_TARGET_FLAGS_PCIE_DEVICE) { + spin_lock_irqsave(&ioc->pcie_device_lock, flags); + pcie_device = __mpt3sas_get_pdev_from_target(ioc, priv_target); + if (pcie_device) { + starget_printk(KERN_INFO, starget, + "handle(0x%04x), wwid(0x%016llx), port(%d)\n", + pcie_device->handle, + (unsigned long long)pcie_device->wwid, + pcie_device->port_num); + if (pcie_device->enclosure_handle != 0) + starget_printk(KERN_INFO, starget, + "enclosure logical id(0x%016llx), slot(%d)\n", + (unsigned long long) + pcie_device->enclosure_logical_id, + pcie_device->slot); + if (pcie_device->connector_name[0] != '\0') + starget_printk(KERN_INFO, starget, + "enclosure level(0x%04x), connector name( %s)\n", + pcie_device->enclosure_level, + pcie_device->connector_name); + pcie_device_put(pcie_device); + } + spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); + } else { spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = __mpt3sas_get_sdev_from_target(ioc, priv_target); @@ -2433,17 +2968,9 @@ _scsih_tm_display_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd) sas_device->handle, (unsigned long long)sas_device->sas_address, sas_device->phy); - if (sas_device->enclosure_handle != 0) - starget_printk(KERN_INFO, starget, - "enclosure_logical_id(0x%016llx), slot(%d)\n", - (unsigned long long) - sas_device->enclosure_logical_id, - sas_device->slot); - if (sas_device->connector_name[0] != '\0') - starget_printk(KERN_INFO, starget, - "enclosure level(0x%04x),connector name(%s)\n", - sas_device->enclosure_level, - sas_device->connector_name); + + _scsih_display_enclosure_chassis_info(NULL, sas_device, + NULL, starget); sas_device_put(sas_device); } @@ -3007,8 +3534,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; @@ -3018,7 +3543,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, @@ -3029,7 +3554,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); } /** @@ -3113,6 +3639,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 * @handle: device handle @@ -3133,18 +3686,14 @@ _scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle) Mpi2SCSITaskManagementRequest_t *mpi_request; u16 smid; struct _sas_device *sas_device = NULL; + struct _pcie_device *pcie_device = NULL; struct MPT3SAS_TARGET *sas_target_priv_data = NULL; u64 sas_address = 0; unsigned long flags; struct _tr_list *delayed_tr; u32 ioc_state; - if (ioc->remove_host) { - dewtprintk(ioc, pr_info(MPT3SAS_FMT - "%s: host has been removed: handle(0x%04x)\n", - __func__, ioc->name, handle)); - return; - } else if (ioc->pci_error_recovery) { + if (ioc->pci_error_recovery) { dewtprintk(ioc, pr_info(MPT3SAS_FMT "%s: host in pci error recovery: handle(0x%04x)\n", __func__, ioc->name, @@ -3175,24 +3724,52 @@ _scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle) sas_address = sas_device->sas_address; } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - + if (!sas_device) { + spin_lock_irqsave(&ioc->pcie_device_lock, flags); + pcie_device = __mpt3sas_get_pdev_by_handle(ioc, handle); + if (pcie_device && pcie_device->starget && + pcie_device->starget->hostdata) { + sas_target_priv_data = pcie_device->starget->hostdata; + sas_target_priv_data->deleted = 1; + sas_address = pcie_device->wwid; + } + spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); + } if (sas_target_priv_data) { dewtprintk(ioc, pr_info(MPT3SAS_FMT "setting delete flag: handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, handle, (unsigned long long)sas_address)); - if (sas_device->enclosure_handle != 0) - dewtprintk(ioc, pr_info(MPT3SAS_FMT - "setting delete flag:enclosure logical id(0x%016llx)," - " slot(%d)\n", ioc->name, (unsigned long long) - sas_device->enclosure_logical_id, - sas_device->slot)); - if (sas_device->connector_name[0] != '\0') - dewtprintk(ioc, pr_info(MPT3SAS_FMT - "setting delete flag: enclosure level(0x%04x)," - " connector name( %s)\n", ioc->name, - sas_device->enclosure_level, - sas_device->connector_name)); + if (sas_device) { + if (sas_device->enclosure_handle != 0) + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "setting delete flag:enclosure logical " + "id(0x%016llx), slot(%d)\n", ioc->name, + (unsigned long long) + sas_device->enclosure_logical_id, + sas_device->slot)); + if (sas_device->connector_name[0] != '\0') + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "setting delete flag: enclosure " + "level(0x%04x), connector name( %s)\n", + ioc->name, sas_device->enclosure_level, + sas_device->connector_name)); + } else if (pcie_device) { + if (pcie_device->enclosure_handle != 0) + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "setting delete flag: logical " + "id(0x%016llx), slot(%d)\n", ioc->name, + (unsigned long long) + pcie_device->enclosure_logical_id, + pcie_device->slot)); + if (pcie_device->connector_name[0] != '\0') + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "setting delete flag:, enclosure " + "level(0x%04x), " + "connector name( %s)\n", ioc->name, + pcie_device->enclosure_level, + pcie_device->connector_name)); + } _scsih_ublock_io_device(ioc, sas_address); sas_target_priv_data->handle = MPT3SAS_INVALID_DEVICE_HANDLE; } @@ -3227,6 +3804,8 @@ _scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle) out: if (sas_device) sas_device_put(sas_device); + if (pcie_device) + pcie_device_put(pcie_device); } /** @@ -3731,6 +4310,81 @@ _scsih_check_topo_delete_events(struct MPT3SAS_ADAPTER *ioc, } /** + * _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 * @handle: device handle @@ -3979,7 +4633,7 @@ _scsih_flush_running_cmds(struct MPT3SAS_ADAPTER *ioc) */ static void _scsih_setup_eedp(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, - Mpi2SCSIIORequest_t *mpi_request) + Mpi25SCSIIORequest_t *mpi_request) { u16 eedp_flags; unsigned char prot_op = scsi_get_prot_op(scmd); @@ -4082,7 +4736,8 @@ scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) struct _raid_device *raid_device; struct request *rq = scmd->request; int class; - Mpi2SCSIIORequest_t *mpi_request; + Mpi25SCSIIORequest_t *mpi_request; + struct _pcie_device *pcie_device = NULL; u32 mpi_control; u16 smid; u16 handle; @@ -4159,8 +4814,9 @@ scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) /* Make sure Device is not raid volume. * We do not expose raid functionality to upper layer for warpdrive. */ - if (!ioc->is_warpdrive && !scsih_is_raid(&scmd->device->sdev_gendev) - && sas_is_tlr_enabled(scmd->device) && scmd->cmd_len != 32) + if (((!ioc->is_warpdrive && !scsih_is_raid(&scmd->device->sdev_gendev)) + && !scsih_is_nvme(&scmd->device->sdev_gendev)) + && sas_is_tlr_enabled(scmd->device) && scmd->cmd_len != 32) mpi_control |= MPI2_SCSIIO_CONTROL_TLR_ON; smid = mpt3sas_base_get_smid_scsiio(ioc, ioc->scsi_io_cb_idx, scmd); @@ -4170,7 +4826,7 @@ scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) goto out; } mpi_request = mpt3sas_base_get_msg_frame(ioc, smid); - memset(mpi_request, 0, sizeof(Mpi2SCSIIORequest_t)); + memset(mpi_request, 0, ioc->request_sz); _scsih_setup_eedp(ioc, scmd, mpi_request); if (scmd->cmd_len == 32) @@ -4189,13 +4845,14 @@ scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) mpi_request->SenseBufferLength = SCSI_SENSE_BUFFERSIZE; mpi_request->SenseBufferLowAddress = mpt3sas_base_get_sense_buffer_dma(ioc, smid); - mpi_request->SGLOffset0 = offsetof(Mpi2SCSIIORequest_t, SGL) / 4; + mpi_request->SGLOffset0 = offsetof(Mpi25SCSIIORequest_t, SGL) / 4; int_to_scsilun(sas_device_priv_data->lun, (struct scsi_lun *) mpi_request->LUN); memcpy(mpi_request->CDB.CDB32, scmd->cmnd, scmd->cmd_len); if (mpi_request->DataLength) { - if (ioc->build_sg_scmd(ioc, scmd, smid)) { + pcie_device = sas_target_priv_data->pcie_dev; + if (ioc->build_sg_scmd(ioc, scmd, smid, pcie_device)) { mpt3sas_base_free_smid(ioc, smid); goto out; } @@ -4204,8 +4861,8 @@ scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) raid_device = sas_target_priv_data->raid_device; if (raid_device && raid_device->direct_io_enabled) - mpt3sas_setup_direct_io(ioc, scmd, raid_device, mpi_request, - smid); + mpt3sas_setup_direct_io(ioc, scmd, + raid_device, mpi_request, smid); if (likely(mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST)) { if (sas_target_priv_data->flags & MPT_TARGET_FASTPATH_IO) { @@ -4273,6 +4930,7 @@ _scsih_scsi_ioc_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, char *desc_scsi_state = ioc->tmp_string; u32 log_info = le32_to_cpu(mpi_reply->IOCLogInfo); struct _sas_device *sas_device = NULL; + struct _pcie_device *pcie_device = NULL; struct scsi_target *starget = scmd->device->sdev_target; struct MPT3SAS_TARGET *priv_target = starget->hostdata; char *device_str = NULL; @@ -4405,6 +5063,28 @@ _scsih_scsi_ioc_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, if (priv_target->flags & MPT_TARGET_FLAGS_VOLUME) { pr_warn(MPT3SAS_FMT "\t%s wwid(0x%016llx)\n", ioc->name, device_str, (unsigned long long)priv_target->sas_address); + } else if (priv_target->flags & MPT_TARGET_FLAGS_PCIE_DEVICE) { + pcie_device = mpt3sas_get_pdev_from_target(ioc, priv_target); + if (pcie_device) { + pr_info(MPT3SAS_FMT "\twwid(0x%016llx), port(%d)\n", + ioc->name, + (unsigned long long)pcie_device->wwid, + pcie_device->port_num); + if (pcie_device->enclosure_handle != 0) + pr_info(MPT3SAS_FMT + "\tenclosure logical id(0x%016llx), " + "slot(%d)\n", ioc->name, + (unsigned long long) + pcie_device->enclosure_logical_id, + pcie_device->slot); + if (pcie_device->connector_name[0]) + pr_info(MPT3SAS_FMT + "\tenclosure level(0x%04x)," + "connector name( %s)\n", + ioc->name, pcie_device->enclosure_level, + pcie_device->connector_name); + pcie_device_put(pcie_device); + } } else { sas_device = mpt3sas_get_sdev_from_target(ioc, priv_target); if (sas_device) { @@ -4412,19 +5092,9 @@ _scsih_scsi_ioc_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, "\tsas_address(0x%016llx), phy(%d)\n", ioc->name, (unsigned long long) sas_device->sas_address, sas_device->phy); - if (sas_device->enclosure_handle != 0) - pr_warn(MPT3SAS_FMT - "\tenclosure_logical_id(0x%016llx)," - "slot(%d)\n", ioc->name, - (unsigned long long) - sas_device->enclosure_logical_id, - sas_device->slot); - if (sas_device->connector_name[0]) - pr_warn(MPT3SAS_FMT - "\tenclosure level(0x%04x)," - " connector name( %s)\n", ioc->name, - sas_device->enclosure_level, - sas_device->connector_name); + + _scsih_display_enclosure_chassis_info(ioc, sas_device, + NULL, NULL); sas_device_put(sas_device); } @@ -4451,11 +5121,10 @@ _scsih_scsi_ioc_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, struct sense_info data; _scsih_normalize_sense(scmd->sense_buffer, &data); pr_warn(MPT3SAS_FMT - "\t[sense_key,asc,ascq]: [0x%02x,0x%02x,0x%02x], count(%d)\n", - ioc->name, data.skey, - data.asc, data.ascq, le32_to_cpu(mpi_reply->SenseCount)); + "\t[sense_key,asc,ascq]: [0x%02x,0x%02x,0x%02x], count(%d)\n", + ioc->name, data.skey, + data.asc, data.ascq, le32_to_cpu(mpi_reply->SenseCount)); } - if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) { response_info = le32_to_cpu(mpi_reply->ResponseInfo); response_bytes = (u8 *)&response_info; @@ -4602,16 +5271,8 @@ _scsih_smart_predicted_fault(struct MPT3SAS_ADAPTER *ioc, u16 handle) ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME))) goto out_unlock; - if (sas_device->enclosure_handle != 0) - starget_printk(KERN_INFO, starget, "predicted fault, " - "enclosure logical id(0x%016llx), slot(%d)\n", - (unsigned long long)sas_device->enclosure_logical_id, - sas_device->slot); - if (sas_device->connector_name[0] != '\0') - starget_printk(KERN_WARNING, starget, "predicted fault, " - "enclosure level(0x%04x), connector name( %s)\n", - sas_device->enclosure_level, - sas_device->connector_name); + _scsih_display_enclosure_chassis_info(NULL, sas_device, NULL, starget); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); if (ioc->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM) @@ -4666,7 +5327,7 @@ out_unlock: static u8 _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) { - Mpi2SCSIIORequest_t *mpi_request; + Mpi25SCSIIORequest_t *mpi_request; Mpi2SCSIIOReply_t *mpi_reply; struct scsi_cmnd *scmd; u16 ioc_status; @@ -4731,9 +5392,10 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) le32_to_cpu(mpi_reply->ResponseInfo) & 0xFF; if (!sas_device_priv_data->tlr_snoop_check) { sas_device_priv_data->tlr_snoop_check++; - if (!ioc->is_warpdrive && + if ((!ioc->is_warpdrive && !scsih_is_raid(&scmd->device->sdev_gendev) && - sas_is_tlr_enabled(scmd->device) && + !scsih_is_nvme(&scmd->device->sdev_gendev)) + && sas_is_tlr_enabled(scmd->device) && response_code == MPI2_SCSITASKMGMT_RSP_INVALID_FRAME) { sas_disable_tlr(scmd->device); sdev_printk(KERN_INFO, scmd->device, "TLR disabled\n"); @@ -4804,6 +5466,11 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) } else if (log_info == VIRTUAL_IO_FAILED_RETRY) { scmd->result = DID_RESET << 16; break; + } else if ((scmd->device->channel == RAID_CHANNEL) && + (scsi_state == (MPI2_SCSI_STATE_TERMINATED | + MPI2_SCSI_STATE_NO_SCSI_STATUS))) { + scmd->result = DID_RESET << 16; + break; } scmd->result = DID_SOFT_ERROR << 16; break; @@ -5274,8 +5941,6 @@ mpt3sas_expander_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address) spin_lock_irqsave(&ioc->sas_node_lock, flags); sas_expander = mpt3sas_scsih_expander_find_by_sas_address(ioc, sas_address); - if (sas_expander) - list_del(&sas_expander->list); spin_unlock_irqrestore(&ioc->sas_node_lock, flags); if (sas_expander) _scsih_expander_node_remove(ioc, sas_expander); @@ -5386,6 +6051,52 @@ _scsih_check_access_status(struct MPT3SAS_ADAPTER *ioc, u64 sas_address, } /** + * _scsih_get_enclosure_logicalid_chassis_slot - get device's + * EnclosureLogicalID and ChassisSlot information. + * @ioc: per adapter object + * @sas_device_pg0: SAS device page0 + * @sas_device: per sas device object + * + * Returns nothing. + */ +static void +_scsih_get_enclosure_logicalid_chassis_slot(struct MPT3SAS_ADAPTER *ioc, + Mpi2SasDevicePage0_t *sas_device_pg0, struct _sas_device *sas_device) +{ + Mpi2ConfigReply_t mpi_reply; + Mpi2SasEnclosurePage0_t enclosure_pg0; + + if (!sas_device_pg0 || !sas_device) + return; + + sas_device->enclosure_handle = + le16_to_cpu(sas_device_pg0->EnclosureHandle); + sas_device->is_chassis_slot_valid = 0; + + if (!le16_to_cpu(sas_device_pg0->EnclosureHandle)) + return; + + if (mpt3sas_config_get_enclosure_pg0(ioc, &mpi_reply, + &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, + le16_to_cpu(sas_device_pg0->EnclosureHandle))) { + pr_err(MPT3SAS_FMT + "Enclosure Pg0 read failed for handle(0x%04x)\n", + ioc->name, le16_to_cpu(sas_device_pg0->EnclosureHandle)); + return; + } + + sas_device->enclosure_logical_id = + le64_to_cpu(enclosure_pg0.EnclosureLogicalID); + + if (le16_to_cpu(enclosure_pg0.Flags) & + MPI2_SAS_ENCLS0_FLAGS_CHASSIS_SLOT_VALID) { + sas_device->is_chassis_slot_valid = 1; + sas_device->chassis_slot = enclosure_pg0.ChassisSlot; + } +} + + +/** * _scsih_check_device - checking device responsiveness * @ioc: per adapter object * @parent_sas_address: sas address of parent expander or sas host @@ -5409,7 +6120,6 @@ _scsih_check_device(struct MPT3SAS_ADAPTER *ioc, struct MPT3SAS_TARGET *sas_target_priv_data; u32 device_info; - if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) return; @@ -5456,6 +6166,9 @@ _scsih_check_device(struct MPT3SAS_ADAPTER *ioc, sas_device->enclosure_level = 0; sas_device->connector_name[0] = '\0'; } + + _scsih_get_enclosure_logicalid_chassis_slot(ioc, + &sas_device_pg0, sas_device); } /* check if device is present */ @@ -5507,6 +6220,7 @@ _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u32 ioc_status; u64 sas_address; u32 device_info; + int encl_pg0_rc = -1; if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { @@ -5551,6 +6265,16 @@ _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num, return -1; } + if (sas_device_pg0.EnclosureHandle) { + encl_pg0_rc = mpt3sas_config_get_enclosure_pg0(ioc, &mpi_reply, + &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, + sas_device_pg0.EnclosureHandle); + if (encl_pg0_rc) + pr_info(MPT3SAS_FMT + "Enclosure Pg0 read failed for handle(0x%04x)\n", + ioc->name, sas_device_pg0.EnclosureHandle); + } + sas_device = kzalloc(sizeof(struct _sas_device), GFP_KERNEL); if (!sas_device) { @@ -5588,13 +6312,21 @@ _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num, sas_device->enclosure_level = 0; sas_device->connector_name[0] = '\0'; } - /* get enclosure_logical_id */ - if (sas_device->enclosure_handle && !(mpt3sas_config_get_enclosure_pg0( - ioc, &mpi_reply, &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, - sas_device->enclosure_handle))) + + /* get enclosure_logical_id & chassis_slot */ + sas_device->is_chassis_slot_valid = 0; + if (encl_pg0_rc == 0) { sas_device->enclosure_logical_id = le64_to_cpu(enclosure_pg0.EnclosureLogicalID); + if (le16_to_cpu(enclosure_pg0.Flags) & + MPI2_SAS_ENCLS0_FLAGS_CHASSIS_SLOT_VALID) { + sas_device->is_chassis_slot_valid = 1; + sas_device->chassis_slot = + enclosure_pg0.ChassisSlot; + } + } + /* get device name */ sas_device->device_name = le64_to_cpu(sas_device_pg0.DeviceName); @@ -5625,23 +6357,15 @@ _scsih_remove_device(struct MPT3SAS_ADAPTER *ioc, _scsih_turn_off_pfa_led(ioc, sas_device); sas_device->pfa_led_on = 0; } + dewtprintk(ioc, pr_info(MPT3SAS_FMT "%s: enter: handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__, sas_device->handle, (unsigned long long) sas_device->sas_address)); - if (sas_device->enclosure_handle != 0) - dewtprintk(ioc, pr_info(MPT3SAS_FMT - "%s: enter: enclosure logical id(0x%016llx), slot(%d)\n", - ioc->name, __func__, - (unsigned long long)sas_device->enclosure_logical_id, - sas_device->slot)); - if (sas_device->connector_name[0] != '\0') - dewtprintk(ioc, pr_info(MPT3SAS_FMT - "%s: enter: enclosure level(0x%04x), connector name( %s)\n", - ioc->name, __func__, - sas_device->enclosure_level, - sas_device->connector_name)); + + dewtprintk(ioc, _scsih_display_enclosure_chassis_info(ioc, sas_device, + NULL, NULL)); if (sas_device->starget && sas_device->starget->hostdata) { sas_target_priv_data = sas_device->starget->hostdata; @@ -5660,34 +6384,16 @@ _scsih_remove_device(struct MPT3SAS_ADAPTER *ioc, "removing handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, sas_device->handle, (unsigned long long) sas_device->sas_address); - if (sas_device->enclosure_handle != 0) - pr_info(MPT3SAS_FMT - "removing : enclosure logical id(0x%016llx), slot(%d)\n", - ioc->name, - (unsigned long long)sas_device->enclosure_logical_id, - sas_device->slot); - if (sas_device->connector_name[0] != '\0') - pr_info(MPT3SAS_FMT - "removing enclosure level(0x%04x), connector name( %s)\n", - ioc->name, sas_device->enclosure_level, - sas_device->connector_name); + + _scsih_display_enclosure_chassis_info(ioc, sas_device, NULL, NULL); dewtprintk(ioc, pr_info(MPT3SAS_FMT "%s: exit: handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__, sas_device->handle, (unsigned long long) sas_device->sas_address)); - if (sas_device->enclosure_handle != 0) - dewtprintk(ioc, pr_info(MPT3SAS_FMT - "%s: exit: enclosure logical id(0x%016llx), slot(%d)\n", - ioc->name, __func__, - (unsigned long long)sas_device->enclosure_logical_id, - sas_device->slot)); - if (sas_device->connector_name[0] != '\0') - dewtprintk(ioc, pr_info(MPT3SAS_FMT - "%s: exit: enclosure level(0x%04x), connector name(%s)\n", - ioc->name, __func__, sas_device->enclosure_level, - sas_device->connector_name)); + dewtprintk(ioc, _scsih_display_enclosure_chassis_info(ioc, sas_device, + NULL, NULL)); } /** @@ -6028,7 +6734,705 @@ 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 + * @wwid: wwid + * @handle: sas device handle + * @access_flags: errors returned during discovery of the device + * + * Return 0 for success, else failure + */ +static u8 +_scsih_check_pcie_access_status(struct MPT3SAS_ADAPTER *ioc, u64 wwid, + u16 handle, u8 access_status) +{ + u8 rc = 1; + char *desc = NULL; + + switch (access_status) { + case MPI26_PCIEDEV0_ASTATUS_NO_ERRORS: + case MPI26_PCIEDEV0_ASTATUS_NEEDS_INITIALIZATION: + rc = 0; + break; + case MPI26_PCIEDEV0_ASTATUS_CAPABILITY_FAILED: + desc = "PCIe device capability failed"; + break; + case MPI26_PCIEDEV0_ASTATUS_DEVICE_BLOCKED: + desc = "PCIe device blocked"; + break; + case MPI26_PCIEDEV0_ASTATUS_MEMORY_SPACE_ACCESS_FAILED: + desc = "PCIe device mem space access failed"; + break; + case MPI26_PCIEDEV0_ASTATUS_UNSUPPORTED_DEVICE: + desc = "PCIe device unsupported"; + break; + case MPI26_PCIEDEV0_ASTATUS_MSIX_REQUIRED: + desc = "PCIe device MSIx Required"; + break; + case MPI26_PCIEDEV0_ASTATUS_INIT_FAIL_MAX: + desc = "PCIe device init fail max"; + break; + case MPI26_PCIEDEV0_ASTATUS_UNKNOWN: + desc = "PCIe device status unknown"; + break; + case MPI26_PCIEDEV0_ASTATUS_NVME_READY_TIMEOUT: + desc = "nvme ready timeout"; + break; + case MPI26_PCIEDEV0_ASTATUS_NVME_DEVCFG_UNSUPPORTED: + desc = "nvme device configuration unsupported"; + break; + case MPI26_PCIEDEV0_ASTATUS_NVME_IDENTIFY_FAILED: + desc = "nvme identify failed"; + break; + case MPI26_PCIEDEV0_ASTATUS_NVME_QCONFIG_FAILED: + desc = "nvme qconfig failed"; + break; + case MPI26_PCIEDEV0_ASTATUS_NVME_QCREATION_FAILED: + desc = "nvme qcreation failed"; + break; + case MPI26_PCIEDEV0_ASTATUS_NVME_EVENTCFG_FAILED: + desc = "nvme eventcfg failed"; + break; + case MPI26_PCIEDEV0_ASTATUS_NVME_GET_FEATURE_STAT_FAILED: + desc = "nvme get feature stat failed"; + break; + case MPI26_PCIEDEV0_ASTATUS_NVME_IDLE_TIMEOUT: + desc = "nvme idle timeout"; + break; + case MPI26_PCIEDEV0_ASTATUS_NVME_FAILURE_STATUS: + desc = "nvme failure status"; + break; + default: + pr_err(MPT3SAS_FMT + " NVMe discovery error(0x%02x): wwid(0x%016llx)," + "handle(0x%04x)\n", ioc->name, access_status, + (unsigned long long)wwid, handle); + return rc; + } + + if (!rc) + return rc; + + pr_info(MPT3SAS_FMT + "NVMe discovery error(%s): wwid(0x%016llx), handle(0x%04x)\n", + ioc->name, desc, + (unsigned long long)wwid, handle); + return rc; +} + +/** + * _scsih_pcie_device_remove_from_sml - removing pcie device + * from SML and free up associated memory + * @ioc: per adapter object + * @pcie_device: the pcie_device object + * + * Return nothing. + */ +static void +_scsih_pcie_device_remove_from_sml(struct MPT3SAS_ADAPTER *ioc, + struct _pcie_device *pcie_device) +{ + struct MPT3SAS_TARGET *sas_target_priv_data; + + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: enter: handle(0x%04x), wwid(0x%016llx)\n", ioc->name, __func__, + pcie_device->handle, (unsigned long long) + pcie_device->wwid)); + if (pcie_device->enclosure_handle != 0) + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: enter: enclosure logical id(0x%016llx), slot(%d)\n", + ioc->name, __func__, + (unsigned long long)pcie_device->enclosure_logical_id, + pcie_device->slot)); + if (pcie_device->connector_name[0] != '\0') + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: enter: enclosure level(0x%04x), connector name( %s)\n", + ioc->name, __func__, + pcie_device->enclosure_level, + pcie_device->connector_name)); + + if (pcie_device->starget && pcie_device->starget->hostdata) { + sas_target_priv_data = pcie_device->starget->hostdata; + sas_target_priv_data->deleted = 1; + _scsih_ublock_io_device(ioc, pcie_device->wwid); + sas_target_priv_data->handle = MPT3SAS_INVALID_DEVICE_HANDLE; + } + + pr_info(MPT3SAS_FMT + "removing handle(0x%04x), wwid (0x%016llx)\n", + ioc->name, pcie_device->handle, + (unsigned long long) pcie_device->wwid); + if (pcie_device->enclosure_handle != 0) + pr_info(MPT3SAS_FMT + "removing : enclosure logical id(0x%016llx), slot(%d)\n", + ioc->name, + (unsigned long long)pcie_device->enclosure_logical_id, + pcie_device->slot); + if (pcie_device->connector_name[0] != '\0') + pr_info(MPT3SAS_FMT + "removing: enclosure level(0x%04x), connector name( %s)\n", + ioc->name, pcie_device->enclosure_level, + pcie_device->connector_name); + + if (pcie_device->starget) + scsi_remove_target(&pcie_device->starget->dev); + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: exit: handle(0x%04x), wwid(0x%016llx)\n", ioc->name, __func__, + pcie_device->handle, (unsigned long long) + pcie_device->wwid)); + if (pcie_device->enclosure_handle != 0) + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: exit: enclosure logical id(0x%016llx), slot(%d)\n", + ioc->name, __func__, + (unsigned long long)pcie_device->enclosure_logical_id, + pcie_device->slot)); + if (pcie_device->connector_name[0] != '\0') + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: exit: enclosure level(0x%04x), connector name( %s)\n", + ioc->name, __func__, pcie_device->enclosure_level, + pcie_device->connector_name)); + + kfree(pcie_device->serial_number); +} + + +/** + * _scsih_pcie_check_device - checking device responsiveness + * @ioc: per adapter object + * @handle: attached device handle + * + * Returns nothing. + */ +static void +_scsih_pcie_check_device(struct MPT3SAS_ADAPTER *ioc, u16 handle) +{ + Mpi2ConfigReply_t mpi_reply; + Mpi26PCIeDevicePage0_t pcie_device_pg0; + u32 ioc_status; + struct _pcie_device *pcie_device; + u64 wwid; + unsigned long flags; + struct scsi_target *starget; + struct MPT3SAS_TARGET *sas_target_priv_data; + u32 device_info; + + if ((mpt3sas_config_get_pcie_device_pg0(ioc, &mpi_reply, + &pcie_device_pg0, MPI26_PCIE_DEVICE_PGAD_FORM_HANDLE, handle))) + return; + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) + return; + + /* check if this is end device */ + device_info = le32_to_cpu(pcie_device_pg0.DeviceInfo); + if (!(_scsih_is_nvme_device(device_info))) + return; + + wwid = le64_to_cpu(pcie_device_pg0.WWID); + spin_lock_irqsave(&ioc->pcie_device_lock, flags); + pcie_device = __mpt3sas_get_pdev_by_wwid(ioc, wwid); + + if (!pcie_device) { + spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); + return; + } + + if (unlikely(pcie_device->handle != handle)) { + starget = pcie_device->starget; + sas_target_priv_data = starget->hostdata; + starget_printk(KERN_INFO, starget, + "handle changed from(0x%04x) to (0x%04x)!!!\n", + pcie_device->handle, handle); + sas_target_priv_data->handle = handle; + pcie_device->handle = handle; + + if (le32_to_cpu(pcie_device_pg0.Flags) & + MPI26_PCIEDEV0_FLAGS_ENCL_LEVEL_VALID) { + pcie_device->enclosure_level = + pcie_device_pg0.EnclosureLevel; + memcpy(&pcie_device->connector_name[0], + &pcie_device_pg0.ConnectorName[0], 4); + } else { + pcie_device->enclosure_level = 0; + pcie_device->connector_name[0] = '\0'; + } + } + + /* check if device is present */ + if (!(le32_to_cpu(pcie_device_pg0.Flags) & + MPI26_PCIEDEV0_FLAGS_DEVICE_PRESENT)) { + pr_info(MPT3SAS_FMT + "device is not present handle(0x%04x), flags!!!\n", + ioc->name, handle); + spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); + pcie_device_put(pcie_device); + return; + } + + /* check if there were any issues with discovery */ + if (_scsih_check_pcie_access_status(ioc, wwid, handle, + pcie_device_pg0.AccessStatus)) { + spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); + pcie_device_put(pcie_device); + return; + } + + spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); + pcie_device_put(pcie_device); + + _scsih_ublock_io_device(ioc, wwid); + + return; +} + +/** + * _scsih_pcie_add_device - creating pcie device object + * @ioc: per adapter object + * @handle: pcie device handle + * + * Creating end device object, stored in ioc->pcie_device_list. + * + * Return 1 means queue the event later, 0 means complete the event + */ +static int +_scsih_pcie_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle) +{ + Mpi26PCIeDevicePage0_t pcie_device_pg0; + Mpi26PCIeDevicePage2_t pcie_device_pg2; + Mpi2ConfigReply_t mpi_reply; + Mpi2SasEnclosurePage0_t enclosure_pg0; + struct _pcie_device *pcie_device; + u32 pcie_device_type; + u32 ioc_status; + u64 wwid; + + if ((mpt3sas_config_get_pcie_device_pg0(ioc, &mpi_reply, + &pcie_device_pg0, MPI26_PCIE_DEVICE_PGAD_FORM_HANDLE, handle))) { + pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return 0; + } + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + pr_err(MPT3SAS_FMT + "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return 0; + } + + set_bit(handle, ioc->pend_os_device_add); + wwid = le64_to_cpu(pcie_device_pg0.WWID); + + /* check if device is present */ + if (!(le32_to_cpu(pcie_device_pg0.Flags) & + MPI26_PCIEDEV0_FLAGS_DEVICE_PRESENT)) { + pr_err(MPT3SAS_FMT + "device is not present handle(0x04%x)!!!\n", + ioc->name, handle); + return 0; + } + + /* check if there were any issues with discovery */ + if (_scsih_check_pcie_access_status(ioc, wwid, handle, + pcie_device_pg0.AccessStatus)) + return 0; + + if (!(_scsih_is_nvme_device(le32_to_cpu(pcie_device_pg0.DeviceInfo)))) + return 0; + + pcie_device = mpt3sas_get_pdev_by_wwid(ioc, wwid); + if (pcie_device) { + clear_bit(handle, ioc->pend_os_device_add); + pcie_device_put(pcie_device); + return 0; + } + + pcie_device = kzalloc(sizeof(struct _pcie_device), GFP_KERNEL); + if (!pcie_device) { + pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return 0; + } + + kref_init(&pcie_device->refcount); + pcie_device->id = ioc->pcie_target_id++; + pcie_device->channel = PCIE_CHANNEL; + pcie_device->handle = handle; + pcie_device->device_info = le32_to_cpu(pcie_device_pg0.DeviceInfo); + pcie_device->wwid = wwid; + pcie_device->port_num = pcie_device_pg0.PortNum; + pcie_device->fast_path = (le32_to_cpu(pcie_device_pg0.Flags) & + MPI26_PCIEDEV0_FLAGS_FAST_PATH_CAPABLE) ? 1 : 0; + pcie_device_type = pcie_device->device_info & + MPI26_PCIE_DEVINFO_MASK_DEVICE_TYPE; + + pcie_device->enclosure_handle = + le16_to_cpu(pcie_device_pg0.EnclosureHandle); + if (pcie_device->enclosure_handle != 0) + pcie_device->slot = le16_to_cpu(pcie_device_pg0.Slot); + + if (le16_to_cpu(pcie_device_pg0.Flags) & + MPI26_PCIEDEV0_FLAGS_ENCL_LEVEL_VALID) { + pcie_device->enclosure_level = pcie_device_pg0.EnclosureLevel; + memcpy(&pcie_device->connector_name[0], + &pcie_device_pg0.ConnectorName[0], 4); + } else { + pcie_device->enclosure_level = 0; + pcie_device->connector_name[0] = '\0'; + } + + /* get enclosure_logical_id */ + if (pcie_device->enclosure_handle && + !(mpt3sas_config_get_enclosure_pg0(ioc, &mpi_reply, + &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, + pcie_device->enclosure_handle))) + pcie_device->enclosure_logical_id = + le64_to_cpu(enclosure_pg0.EnclosureLogicalID); + + /* TODO -- Add device name once FW supports it */ + if (mpt3sas_config_get_pcie_device_pg2(ioc, &mpi_reply, + &pcie_device_pg2, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle)) { + pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + kfree(pcie_device); + return 0; + } + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + kfree(pcie_device); + return 0; + } + pcie_device->nvme_mdts = + le32_to_cpu(pcie_device_pg2.MaximumDataTransferSize); + + if (ioc->wait_for_discovery_to_complete) + _scsih_pcie_device_init_add(ioc, pcie_device); + else + _scsih_pcie_device_add(ioc, pcie_device); + + 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); } /** @@ -6282,6 +7686,35 @@ _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)) + return; + + 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_cont("enumeration_status(0x%08x)", + le32_to_cpu(event_data->EnumerationStatus)); + pr_cont("\n"); +} + +/** * _scsih_ir_fastpath - turn on fastpath for IR physdisk * @ioc: per adapter object * @handle: device handle for physical disk @@ -7085,7 +8518,7 @@ Mpi2SasDevicePage0_t *sas_device_pg0) { struct MPT3SAS_TARGET *sas_target_priv_data = NULL; struct scsi_target *starget; - struct _sas_device *sas_device; + struct _sas_device *sas_device = NULL; unsigned long flags; spin_lock_irqsave(&ioc->sas_device_lock, flags); @@ -7126,6 +8559,9 @@ Mpi2SasDevicePage0_t *sas_device_pg0) sas_device->connector_name[0] = '\0'; } + _scsih_get_enclosure_logicalid_chassis_slot(ioc, + sas_device_pg0, sas_device); + if (sas_device->handle == sas_device_pg0->DevHandle) goto out; pr_info("\thandle changed from(0x%04x)!!!\n", @@ -7190,6 +8626,130 @@ _scsih_search_responding_sas_devices(struct MPT3SAS_ADAPTER *ioc) } /** + * _scsih_mark_responding_pcie_device - mark a pcie_device as responding + * @ioc: per adapter object + * @pcie_device_pg0: PCIe Device page 0 + * + * After host reset, find out whether devices are still responding. + * Used in _scsih_remove_unresponding_devices. + * + * Return nothing. + */ +static void +_scsih_mark_responding_pcie_device(struct MPT3SAS_ADAPTER *ioc, + Mpi26PCIeDevicePage0_t *pcie_device_pg0) +{ + struct MPT3SAS_TARGET *sas_target_priv_data = NULL; + struct scsi_target *starget; + struct _pcie_device *pcie_device; + unsigned long flags; + + spin_lock_irqsave(&ioc->pcie_device_lock, flags); + list_for_each_entry(pcie_device, &ioc->pcie_device_list, list) { + if ((pcie_device->wwid == pcie_device_pg0->WWID) && + (pcie_device->slot == pcie_device_pg0->Slot)) { + pcie_device->responding = 1; + starget = pcie_device->starget; + if (starget && starget->hostdata) { + sas_target_priv_data = starget->hostdata; + sas_target_priv_data->tm_busy = 0; + sas_target_priv_data->deleted = 0; + } else + sas_target_priv_data = NULL; + if (starget) { + starget_printk(KERN_INFO, starget, + "handle(0x%04x), wwid(0x%016llx) ", + pcie_device->handle, + (unsigned long long)pcie_device->wwid); + if (pcie_device->enclosure_handle != 0) + starget_printk(KERN_INFO, starget, + "enclosure logical id(0x%016llx), " + "slot(%d)\n", + (unsigned long long) + pcie_device->enclosure_logical_id, + pcie_device->slot); + } + + if (((le32_to_cpu(pcie_device_pg0->Flags)) & + MPI26_PCIEDEV0_FLAGS_ENCL_LEVEL_VALID) && + (ioc->hba_mpi_version_belonged != MPI2_VERSION)) { + pcie_device->enclosure_level = + pcie_device_pg0->EnclosureLevel; + memcpy(&pcie_device->connector_name[0], + &pcie_device_pg0->ConnectorName[0], 4); + } else { + pcie_device->enclosure_level = 0; + pcie_device->connector_name[0] = '\0'; + } + + if (pcie_device->handle == pcie_device_pg0->DevHandle) + goto out; + pr_info("\thandle changed from(0x%04x)!!!\n", + pcie_device->handle); + pcie_device->handle = pcie_device_pg0->DevHandle; + if (sas_target_priv_data) + sas_target_priv_data->handle = + pcie_device_pg0->DevHandle; + goto out; + } + } + + out: + spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); +} + +/** + * _scsih_search_responding_pcie_devices - + * @ioc: per adapter object + * + * After host reset, find out whether devices are still responding. + * If not remove. + * + * Return nothing. + */ +static void +_scsih_search_responding_pcie_devices(struct MPT3SAS_ADAPTER *ioc) +{ + Mpi26PCIeDevicePage0_t pcie_device_pg0; + Mpi2ConfigReply_t mpi_reply; + u16 ioc_status; + u16 handle; + u32 device_info; + + pr_info(MPT3SAS_FMT "search for end-devices: start\n", ioc->name); + + if (list_empty(&ioc->pcie_device_list)) + goto out; + + handle = 0xFFFF; + while (!(mpt3sas_config_get_pcie_device_pg0(ioc, &mpi_reply, + &pcie_device_pg0, MPI26_PCIE_DEVICE_PGAD_FORM_GET_NEXT_HANDLE, + handle))) { + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + pr_info(MPT3SAS_FMT "\tbreak from %s: " + "ioc_status(0x%04x), loginfo(0x%08x)\n", ioc->name, + __func__, ioc_status, + le32_to_cpu(mpi_reply.IOCLogInfo)); + break; + } + handle = le16_to_cpu(pcie_device_pg0.DevHandle); + device_info = le32_to_cpu(pcie_device_pg0.DeviceInfo); + if (!(_scsih_is_nvme_device(device_info))) + continue; + pcie_device_pg0.WWID = le64_to_cpu(pcie_device_pg0.WWID), + pcie_device_pg0.Slot = le16_to_cpu(pcie_device_pg0.Slot); + pcie_device_pg0.Flags = le32_to_cpu(pcie_device_pg0.Flags); + pcie_device_pg0.DevHandle = handle; + _scsih_mark_responding_pcie_device(ioc, &pcie_device_pg0); + } +out: + pr_info(MPT3SAS_FMT "search for PCIe end-devices: complete\n", + ioc->name); +} + +/** * _scsih_mark_responding_raid_device - mark a raid_device as responding * @ioc: per adapter object * @wwid: world wide identifier for raid volume @@ -7322,8 +8882,7 @@ _scsih_search_responding_raid_devices(struct MPT3SAS_ADAPTER *ioc) /** * _scsih_mark_responding_expander - mark a expander as responding * @ioc: per adapter object - * @sas_address: sas address - * @handle: + * @expander_pg0:SAS Expander Config Page0 * * After host reset, find out whether devices are still responding. * Used in _scsih_remove_unresponsive_expanders. @@ -7331,18 +8890,41 @@ _scsih_search_responding_raid_devices(struct MPT3SAS_ADAPTER *ioc) * Return nothing. */ static void -_scsih_mark_responding_expander(struct MPT3SAS_ADAPTER *ioc, u64 sas_address, - u16 handle) +_scsih_mark_responding_expander(struct MPT3SAS_ADAPTER *ioc, + Mpi2ExpanderPage0_t *expander_pg0) { - struct _sas_node *sas_expander; + struct _sas_node *sas_expander = NULL; unsigned long flags; - int i; + int i, encl_pg0_rc = -1; + Mpi2ConfigReply_t mpi_reply; + Mpi2SasEnclosurePage0_t enclosure_pg0; + u16 handle = le16_to_cpu(expander_pg0->DevHandle); + u64 sas_address = le64_to_cpu(expander_pg0->SASAddress); + + if (le16_to_cpu(expander_pg0->EnclosureHandle)) { + encl_pg0_rc = mpt3sas_config_get_enclosure_pg0(ioc, &mpi_reply, + &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, + le16_to_cpu(expander_pg0->EnclosureHandle)); + if (encl_pg0_rc) + pr_info(MPT3SAS_FMT + "Enclosure Pg0 read failed for handle(0x%04x)\n", + ioc->name, + le16_to_cpu(expander_pg0->EnclosureHandle)); + } spin_lock_irqsave(&ioc->sas_node_lock, flags); list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) { if (sas_expander->sas_address != sas_address) continue; sas_expander->responding = 1; + + if (!encl_pg0_rc) + sas_expander->enclosure_logical_id = + le64_to_cpu(enclosure_pg0.EnclosureLogicalID); + + sas_expander->enclosure_handle = + le16_to_cpu(expander_pg0->EnclosureHandle); + if (sas_expander->handle == handle) goto out; pr_info("\texpander(0x%016llx): handle changed" \ @@ -7395,7 +8977,7 @@ _scsih_search_responding_expanders(struct MPT3SAS_ADAPTER *ioc) pr_info("\texpander present: handle(0x%04x), sas_addr(0x%016llx)\n", handle, (unsigned long long)sas_address); - _scsih_mark_responding_expander(ioc, sas_address, handle); + _scsih_mark_responding_expander(ioc, &expander_pg0); } out: @@ -7403,17 +8985,18 @@ _scsih_search_responding_expanders(struct MPT3SAS_ADAPTER *ioc) } /** - * _scsih_remove_unresponding_sas_devices - removing unresponding devices + * _scsih_remove_unresponding_devices - removing unresponding devices * @ioc: per adapter object * * Return nothing. */ static void -_scsih_remove_unresponding_sas_devices(struct MPT3SAS_ADAPTER *ioc) +_scsih_remove_unresponding_devices(struct MPT3SAS_ADAPTER *ioc) { struct _sas_device *sas_device, *sas_device_next; struct _sas_node *sas_expander, *sas_expander_next; struct _raid_device *raid_device, *raid_device_next; + struct _pcie_device *pcie_device, *pcie_device_next; struct list_head tmp_list; unsigned long flags; LIST_HEAD(head); @@ -7447,6 +9030,26 @@ _scsih_remove_unresponding_sas_devices(struct MPT3SAS_ADAPTER *ioc) sas_device_put(sas_device); } + pr_info(MPT3SAS_FMT + " Removing unresponding devices: pcie end-devices\n" + , ioc->name); + INIT_LIST_HEAD(&head); + spin_lock_irqsave(&ioc->pcie_device_lock, flags); + list_for_each_entry_safe(pcie_device, pcie_device_next, + &ioc->pcie_device_list, list) { + if (!pcie_device->responding) + list_move_tail(&pcie_device->list, &head); + else + pcie_device->responding = 0; + } + spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); + + list_for_each_entry_safe(pcie_device, pcie_device_next, &head, list) { + _scsih_pcie_device_remove_from_sml(ioc, pcie_device); + list_del_init(&pcie_device->list); + pcie_device_put(pcie_device); + } + /* removing unresponding volumes */ if (ioc->ir_firmware) { pr_info(MPT3SAS_FMT "removing unresponding devices: volumes\n", @@ -7476,7 +9079,6 @@ _scsih_remove_unresponding_sas_devices(struct MPT3SAS_ADAPTER *ioc) spin_unlock_irqrestore(&ioc->sas_node_lock, flags); list_for_each_entry_safe(sas_expander, sas_expander_next, &tmp_list, list) { - list_del(&sas_expander->list); _scsih_expander_node_remove(ioc, sas_expander); } @@ -7520,6 +9122,7 @@ _scsih_scan_for_devices_after_reset(struct MPT3SAS_ADAPTER *ioc) { Mpi2ExpanderPage0_t expander_pg0; Mpi2SasDevicePage0_t sas_device_pg0; + Mpi26PCIeDevicePage0_t pcie_device_pg0; Mpi2RaidVolPage1_t volume_pg1; Mpi2RaidVolPage0_t volume_pg0; Mpi2RaidPhysDiskPage0_t pd_pg0; @@ -7530,6 +9133,7 @@ _scsih_scan_for_devices_after_reset(struct MPT3SAS_ADAPTER *ioc) u16 handle, parent_handle; u64 sas_address; struct _sas_device *sas_device; + struct _pcie_device *pcie_device; struct _sas_node *expander_device; static struct _raid_device *raid_device; u8 retry_count; @@ -7755,7 +9359,44 @@ _scsih_scan_for_devices_after_reset(struct MPT3SAS_ADAPTER *ioc) } pr_info(MPT3SAS_FMT "\tscan devices: end devices complete\n", ioc->name); + pr_info(MPT3SAS_FMT "\tscan devices: pcie end devices start\n", + ioc->name); + /* pcie devices */ + handle = 0xFFFF; + while (!(mpt3sas_config_get_pcie_device_pg0(ioc, &mpi_reply, + &pcie_device_pg0, MPI26_PCIE_DEVICE_PGAD_FORM_GET_NEXT_HANDLE, + handle))) { + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) + & MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + pr_info(MPT3SAS_FMT "\tbreak from pcie end device" + " scan: ioc_status(0x%04x), loginfo(0x%08x)\n", + ioc->name, ioc_status, + le32_to_cpu(mpi_reply.IOCLogInfo)); + break; + } + handle = le16_to_cpu(pcie_device_pg0.DevHandle); + if (!(_scsih_is_nvme_device( + le32_to_cpu(pcie_device_pg0.DeviceInfo)))) + continue; + pcie_device = mpt3sas_get_pdev_by_wwid(ioc, + le64_to_cpu(pcie_device_pg0.WWID)); + if (pcie_device) { + pcie_device_put(pcie_device); + continue; + } + retry_count = 0; + parent_handle = le16_to_cpu(pcie_device_pg0.ParentDevHandle); + _scsih_pcie_add_device(ioc, handle); + + pr_info(MPT3SAS_FMT "\tAFTER adding pcie end device: " + "handle (0x%04x), wwid(0x%016llx)\n", ioc->name, + handle, + (unsigned long long) le64_to_cpu(pcie_device_pg0.WWID)); + } + pr_info(MPT3SAS_FMT "\tpcie devices: pcie end devices complete\n", + ioc->name); pr_info(MPT3SAS_FMT "scan devices: complete\n", ioc->name); } /** @@ -7805,6 +9446,7 @@ mpt3sas_scsih_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase) !ioc->sas_hba.num_phys)) { _scsih_prep_device_scan(ioc); _scsih_search_responding_sas_devices(ioc); + _scsih_search_responding_pcie_devices(ioc); _scsih_search_responding_raid_devices(ioc); _scsih_search_responding_expanders(ioc); _scsih_error_recovery_delete_devices(ioc); @@ -7849,7 +9491,7 @@ _mpt3sas_fw_work(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event) goto out; ssleep(1); } - _scsih_remove_unresponding_sas_devices(ioc); + _scsih_remove_unresponding_devices(ioc); _scsih_scan_for_devices_after_reset(ioc); break; case MPT3SAS_PORT_ENABLE_COMPLETE: @@ -7892,6 +9534,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); @@ -7982,6 +9634,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 *) @@ -8044,6 +9701,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: @@ -8056,19 +9715,21 @@ mpt3sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, (Mpi26EventDataActiveCableExcept_t *) mpi_reply->EventData; switch (ActiveCableEventData->ReasonCode) { case MPI26_EVENT_ACTIVE_CABLE_INSUFFICIENT_POWER: - pr_notice(MPT3SAS_FMT "Receptacle ID %d: This active cable" - " requires %d mW of power\n", ioc->name, - ActiveCableEventData->ReceptacleID, + pr_notice(MPT3SAS_FMT + "Currently an active cable with ReceptacleID %d\n", + ioc->name, ActiveCableEventData->ReceptacleID); + pr_notice("cannot be powered and devices connected\n"); + pr_notice("to this active cable will not be seen\n"); + pr_notice("This active cable requires %d mW of power\n", ActiveCableEventData->ActiveCablePowerRequirement); - pr_notice(MPT3SAS_FMT "Receptacle ID %d: Devices connected" - " to this active cable will not be seen\n", - ioc->name, ActiveCableEventData->ReceptacleID); break; case MPI26_EVENT_ACTIVE_CABLE_DEGRADED: - pr_notice(MPT3SAS_FMT "ReceptacleID %d: This cable", - ioc->name, ActiveCableEventData->ReceptacleID); - pr_notice(" is not running at an optimal speed(12 Gb/s)\n"); + pr_notice(MPT3SAS_FMT + "Currently a cable with ReceptacleID %d\n", + ioc->name, ActiveCableEventData->ReceptacleID); + pr_notice( + "is not running at optimal speed(12 Gb/s rate)\n"); break; } @@ -8100,7 +9761,6 @@ mpt3sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, * _scsih_expander_node_remove - removing expander device from list. * @ioc: per adapter object * @sas_expander: the sas_device object - * Context: Calling function should acquire ioc->sas_node_lock. * * Removing object and freeing associated memory from the * ioc->sas_expander_list. @@ -8112,6 +9772,7 @@ _scsih_expander_node_remove(struct MPT3SAS_ADAPTER *ioc, struct _sas_node *sas_expander) { struct _sas_port *mpt3sas_port, *next; + unsigned long flags; /* remove sibling ports attached to this expander */ list_for_each_entry_safe(mpt3sas_port, next, @@ -8139,6 +9800,10 @@ _scsih_expander_node_remove(struct MPT3SAS_ADAPTER *ioc, sas_expander->handle, (unsigned long long) sas_expander->sas_address); + spin_lock_irqsave(&ioc->sas_node_lock, flags); + list_del(&sas_expander->list); + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); + kfree(sas_expander->phy); kfree(sas_expander); } @@ -8231,6 +9896,7 @@ static void scsih_remove(struct pci_dev *pdev) struct _sas_port *mpt3sas_port, *next_port; struct _raid_device *raid_device, *next; struct MPT3SAS_TARGET *sas_target_priv_data; + struct _pcie_device *pcie_device, *pcienext; struct workqueue_struct *wq; unsigned long flags; @@ -8259,6 +9925,12 @@ static void scsih_remove(struct pci_dev *pdev) (unsigned long long) raid_device->wwid); _scsih_raid_device_remove(ioc, raid_device); } + list_for_each_entry_safe(pcie_device, pcienext, &ioc->pcie_device_list, + list) { + _scsih_pcie_device_remove_from_sml(ioc, pcie_device); + list_del_init(&pcie_device->list); + pcie_device_put(pcie_device); + } /* free ports attached to the sas_host */ list_for_each_entry_safe(mpt3sas_port, next_port, @@ -8330,42 +10002,52 @@ scsih_shutdown(struct pci_dev *pdev) static void _scsih_probe_boot_devices(struct MPT3SAS_ADAPTER *ioc) { - u8 is_raid; + u32 channel; void *device; struct _sas_device *sas_device; struct _raid_device *raid_device; + struct _pcie_device *pcie_device; u16 handle; u64 sas_address_parent; u64 sas_address; unsigned long flags; int rc; + int tid; /* no Bios, return immediately */ if (!ioc->bios_pg3.BiosVersion) return; device = NULL; - is_raid = 0; if (ioc->req_boot_device.device) { device = ioc->req_boot_device.device; - is_raid = ioc->req_boot_device.is_raid; + channel = ioc->req_boot_device.channel; } else if (ioc->req_alt_boot_device.device) { device = ioc->req_alt_boot_device.device; - is_raid = ioc->req_alt_boot_device.is_raid; + channel = ioc->req_alt_boot_device.channel; } else if (ioc->current_boot_device.device) { device = ioc->current_boot_device.device; - is_raid = ioc->current_boot_device.is_raid; + channel = ioc->current_boot_device.channel; } if (!device) return; - if (is_raid) { + if (channel == RAID_CHANNEL) { raid_device = device; rc = scsi_add_device(ioc->shost, RAID_CHANNEL, raid_device->id, 0); if (rc) _scsih_raid_device_remove(ioc, raid_device); + } else if (channel == PCIE_CHANNEL) { + spin_lock_irqsave(&ioc->pcie_device_lock, flags); + pcie_device = device; + tid = pcie_device->id; + list_move_tail(&pcie_device->list, &ioc->pcie_device_list); + spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); + rc = scsi_add_device(ioc->shost, PCIE_CHANNEL, tid, 0); + if (rc) + _scsih_pcie_device_remove(ioc, pcie_device); } else { spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = device; @@ -8498,6 +10180,101 @@ _scsih_probe_sas(struct MPT3SAS_ADAPTER *ioc) } /** + * get_next_pcie_device - Get the next pcie device + * @ioc: per adapter object + * + * Get the next pcie device from pcie_device_init_list list. + * + * Returns pcie device structure if pcie_device_init_list list is not empty + * otherwise returns NULL + */ +static struct _pcie_device *get_next_pcie_device(struct MPT3SAS_ADAPTER *ioc) +{ + struct _pcie_device *pcie_device = NULL; + unsigned long flags; + + spin_lock_irqsave(&ioc->pcie_device_lock, flags); + if (!list_empty(&ioc->pcie_device_init_list)) { + pcie_device = list_first_entry(&ioc->pcie_device_init_list, + struct _pcie_device, list); + pcie_device_get(pcie_device); + } + spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); + + return pcie_device; +} + +/** + * pcie_device_make_active - Add pcie device to pcie_device_list list + * @ioc: per adapter object + * @pcie_device: pcie device object + * + * Add the pcie device which has registered with SCSI Transport Later to + * pcie_device_list list + */ +static void pcie_device_make_active(struct MPT3SAS_ADAPTER *ioc, + struct _pcie_device *pcie_device) +{ + unsigned long flags; + + spin_lock_irqsave(&ioc->pcie_device_lock, flags); + + if (!list_empty(&pcie_device->list)) { + list_del_init(&pcie_device->list); + pcie_device_put(pcie_device); + } + pcie_device_get(pcie_device); + list_add_tail(&pcie_device->list, &ioc->pcie_device_list); + + spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); +} + +/** + * _scsih_probe_pcie - reporting PCIe devices to scsi-ml + * @ioc: per adapter object + * + * Called during initial loading of the driver. + */ +static void +_scsih_probe_pcie(struct MPT3SAS_ADAPTER *ioc) +{ + struct _pcie_device *pcie_device; + int rc; + + /* PCIe Device List */ + while ((pcie_device = get_next_pcie_device(ioc))) { + if (pcie_device->starget) { + pcie_device_put(pcie_device); + continue; + } + rc = scsi_add_device(ioc->shost, PCIE_CHANNEL, + pcie_device->id, 0); + if (rc) { + _scsih_pcie_device_remove(ioc, pcie_device); + pcie_device_put(pcie_device); + continue; + } else if (!pcie_device->starget) { + /* + * When async scanning is enabled, its not possible to + * remove devices while scanning is turned on due to an + * oops in scsi_sysfs_add_sdev()->add_device()-> + * sysfs_addrm_start() + */ + if (!ioc->is_driver_loading) { + /* TODO-- Need to find out whether this condition will + * occur or not + */ + _scsih_pcie_device_remove(ioc, pcie_device); + pcie_device_put(pcie_device); + continue; + } + } + pcie_device_make_active(ioc, pcie_device); + pcie_device_put(pcie_device); + } +} + +/** * _scsih_probe_devices - probing for devices * @ioc: per adapter object * @@ -8525,8 +10302,10 @@ _scsih_probe_devices(struct MPT3SAS_ADAPTER *ioc) _scsih_probe_sas(ioc); _scsih_probe_raid(ioc); } - } else + } else { _scsih_probe_sas(ioc); + _scsih_probe_pcie(ioc); + } } /** @@ -8740,6 +10519,7 @@ _scsih_determine_hba_mpi_version(struct pci_dev *pdev) case MPI26_MFGPAGE_DEVID_SAS3516: case MPI26_MFGPAGE_DEVID_SAS3516_1: case MPI26_MFGPAGE_DEVID_SAS3416: + case MPI26_MFGPAGE_DEVID_SAS3616: return MPI26_VERSION; } return 0; @@ -8817,6 +10597,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) case MPI26_MFGPAGE_DEVID_SAS3516: case MPI26_MFGPAGE_DEVID_SAS3516_1: case MPI26_MFGPAGE_DEVID_SAS3416: + case MPI26_MFGPAGE_DEVID_SAS3616: ioc->is_gen35_ioc = 1; break; default: @@ -8867,11 +10648,14 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) spin_lock_init(&ioc->sas_node_lock); spin_lock_init(&ioc->fw_event_lock); spin_lock_init(&ioc->raid_device_lock); + spin_lock_init(&ioc->pcie_device_lock); spin_lock_init(&ioc->diag_trigger_lock); INIT_LIST_HEAD(&ioc->sas_device_list); INIT_LIST_HEAD(&ioc->sas_device_init_list); INIT_LIST_HEAD(&ioc->sas_expander_list); + INIT_LIST_HEAD(&ioc->pcie_device_list); + INIT_LIST_HEAD(&ioc->pcie_device_init_list); INIT_LIST_HEAD(&ioc->fw_event_list); INIT_LIST_HEAD(&ioc->raid_device_list); INIT_LIST_HEAD(&ioc->sas_hba.sas_port_list); @@ -9273,6 +11057,9 @@ static const struct pci_device_id mpt3sas_pci_table[] = { PCI_ANY_ID, PCI_ANY_ID }, { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3416, PCI_ANY_ID, PCI_ANY_ID }, + /* Mercator ~ 3616*/ + { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3616, + PCI_ANY_ID, PCI_ANY_ID }, {0} /* Terminating entry */ }; MODULE_DEVICE_TABLE(pci, mpt3sas_pci_table); |