/* * Scsi Host Layer for MPT (Message Passing Technology) based controllers * * This code is based on drivers/scsi/mpt3sas/mpt3sas_scsih.c * Copyright (C) 2012-2014 LSI Corporation * Copyright (C) 2013-2014 Avago Technologies * (mailto: MPT-FusionLinux.pdl@avagotech.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * NO WARRANTY * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is * solely responsible for determining the appropriateness of using and * distributing the Program and assumes all risks associated with its * exercise of rights under this Agreement, including but not limited to * the risks and costs of program errors, damage to or loss of data, * programs or equipment, and unavailability or interruption of operations.
* DISCLAIMER OF LIABILITY * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
* You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA.
*/
/* scsi-mid layer global parmeter is max_report_luns, which is 511 */ #define MPT3SAS_MAX_LUN (16895) static u64 max_lun = MPT3SAS_MAX_LUN;
module_param(max_lun, ullong, 0444);
MODULE_PARM_DESC(max_lun, " max lun, default=16895 ");
static ushort hbas_to_enumerate;
module_param(hbas_to_enumerate, ushort, 0444);
MODULE_PARM_DESC(hbas_to_enumerate, " 0 - enumerates both SAS 2.0 & SAS 3.0 generation HBAs\n \
1 - enumerates only SAS 2.0 generation HBAs\n \
2 - enumerates only SAS 3.0 generation HBAs (default=0)");
/* diag_buffer_enable is bitwise * bit 0 set = TRACE * bit 1 set = SNAPSHOT * bit 2 set = EXTENDED * * Either bit can be set, or both
*/ staticint diag_buffer_enable = -1;
module_param(diag_buffer_enable, int, 0444);
MODULE_PARM_DESC(diag_buffer_enable, " post diag buffers (TRACE=1/SNAPSHOT=2/EXTENDED=4/default=0)"); staticint disable_discovery = -1;
module_param(disable_discovery, int, 0444);
MODULE_PARM_DESC(disable_discovery, " disable discovery ");
staticbool enable_sdev_max_qd;
module_param(enable_sdev_max_qd, bool, 0444);
MODULE_PARM_DESC(enable_sdev_max_qd, "Enable sdev max qd as can_queue, def=disabled(0)");
staticint multipath_on_hba = -1;
module_param(multipath_on_hba, int, 0);
MODULE_PARM_DESC(multipath_on_hba, "Multipath support to add same target device\n\t\t" "as many times as it is visible to HBA from various paths\n\t\t" "(by default:\n\t\t" "\t SAS 2.0 & SAS 3.0 HBA - This will be disabled,\n\t\t" "\t SAS 3.5 HBA - This will be enabled)");
/* * SAS Log info code for a NCQ collateral abort after an NCQ error: * IOC_LOGINFO_PREFIX_PL | PL_LOGINFO_CODE_SATA_NCQ_FAIL_ALL_CMDS_AFTR_ERR * See: drivers/message/fusion/lsi/mpi_log_sas.h
*/ #define IOC_LOGINFO_SATA_NCQ_FAIL_AFTER_ERR 0x31080000
/** * struct fw_event_work - firmware event struct * @list: link list framework * @work: work object (ioc->fault_reset_work_q) * @ioc: per adapter object * @device_handle: device handle * @VF_ID: virtual function id * @VP_ID: virtual port id * @ignore: flag meaning this event has been marked to ignore * @event: firmware event MPI2_EVENT_XXX defined in mpi2_ioc.h * @refcount: kref for this event * @event_data: reply event data payload follows * * This object stored on ioc->fw_event_list.
*/ struct fw_event_work { struct list_head list; struct work_struct work;
/** * struct _scsi_io_transfer - scsi io transfer * @handle: sas device handle (assigned by firmware) * @is_raid: flag set for hidden raid components * @dir: DMA_TO_DEVICE, DMA_FROM_DEVICE, * @data_length: data transfer length * @data_dma: dma pointer to data * @sense: sense data * @lun: lun number * @cdb_length: cdb length * @cdb: cdb contents * @timeout: timeout for this command * @VF_ID: virtual function id * @VP_ID: virtual port id * @valid_reply: flag set for reply message * @sense_length: sense length * @ioc_status: ioc status * @scsi_state: scsi state * @scsi_status: scsi staus * @log_info: log information * @transfer_length: data length transfer when there is a reply message * * Used for sending internal scsi commands to devices within this module. * Refer to _scsi_send_scsi_io().
*/ struct _scsi_io_transfer {
u16 handle;
u8 is_raid; enum dma_data_direction dir;
u32 data_length;
dma_addr_t data_dma;
u8 sense[SCSI_SENSE_BUFFERSIZE];
u32 lun;
u8 cdb_length;
u8 cdb[32];
u8 timeout;
u8 VF_ID;
u8 VP_ID;
u8 valid_reply; /* the following bits are only valid when 'valid_reply = 1' */
u32 sense_length;
u16 ioc_status;
u8 scsi_state;
u8 scsi_status;
u32 log_info;
u32 transfer_length;
};
/** * _scsih_set_debug_level - global setting of ioc->logging_level. * @val: value of the parameter to be set * @kp: pointer to kernel_param structure * * Note: The logging levels are defined in mpt3sas_debug.h.
*/ staticint
_scsih_set_debug_level(constchar *val, conststruct kernel_param *kp)
{ int ret = param_set_int(val, kp); struct MPT3SAS_ADAPTER *ioc;
/** * _scsih_srch_boot_sas_address - search based on sas_address * @sas_address: sas address * @boot_device: boot device object from bios page 2 * * Return: 1 when there's a match, 0 means no match.
*/ staticinlineint
_scsih_srch_boot_sas_address(u64 sas_address,
Mpi2BootDeviceSasWwid_t *boot_device)
{ return (sas_address == le64_to_cpu(boot_device->SASAddress)) ? 1 : 0;
}
/** * _scsih_srch_boot_device_name - search based on device name * @device_name: device name specified in INDENTIFY fram * @boot_device: boot device object from bios page 2 * * Return: 1 when there's a match, 0 means no match.
*/ staticinlineint
_scsih_srch_boot_device_name(u64 device_name,
Mpi2BootDeviceDeviceName_t *boot_device)
{ return (device_name == le64_to_cpu(boot_device->DeviceName)) ? 1 : 0;
}
/** * _scsih_srch_boot_encl_slot - search based on enclosure_logical_id/slot * @enclosure_logical_id: enclosure logical id * @slot_number: slot number * @boot_device: boot device object from bios page 2 * * Return: 1 when there's a match, 0 means no match.
*/ staticinlineint
_scsih_srch_boot_encl_slot(u64 enclosure_logical_id, u16 slot_number,
Mpi2BootDeviceEnclosureSlot_t *boot_device)
{ return (enclosure_logical_id == le64_to_cpu(boot_device->
EnclosureLogicalID) && slot_number == le16_to_cpu(boot_device->
SlotNumber)) ? 1 : 0;
}
/** * mpt3sas_get_port_by_id - get hba port entry corresponding to provided * port number from port list * @ioc: per adapter object * @port_id: port number * @bypass_dirty_port_flag: when set look the matching hba port entry even * if hba port entry is marked as dirty. * * Search for hba port entry corresponding to provided port number, * if available return port object otherwise return NULL.
*/ struct hba_port *
mpt3sas_get_port_by_id(struct MPT3SAS_ADAPTER *ioc,
u8 port_id, u8 bypass_dirty_port_flag)
{ struct hba_port *port, *port_next;
/* * When multipath_on_hba is disabled then * search the hba_port entry using default * port id i.e. 255
*/ if (!ioc->multipath_on_hba)
port_id = MULTIPATH_DISABLED_PORT_ID;
list_for_each_entry_safe(port, port_next,
&ioc->port_table_list, list) { if (port->port_id != port_id) continue; if (bypass_dirty_port_flag) return port; if (port->flags & HBA_PORT_FLAG_DIRTY_PORT) continue; return port;
}
/* * Allocate hba_port object for default port id (i.e. 255) * when multipath_on_hba is disabled for the HBA. * And add this object to port_table_list.
*/ if (!ioc->multipath_on_hba) {
port = kzalloc(sizeof(struct hba_port), GFP_ATOMIC); if (!port) return NULL;
/** * _scsih_is_boot_device - search for matching boot device. * @sas_address: sas address * @device_name: device name specified in INDENTIFY fram * @enclosure_logical_id: enclosure logical id * @slot: slot number * @form: specifies boot device form * @boot_device: boot device object from bios page 2 * * Return: 1 when there's a match, 0 means no match.
*/ staticint
_scsih_is_boot_device(u64 sas_address, u64 device_name,
u64 enclosure_logical_id, u16 slot, u8 form,
Mpi2BiosPage2BootDevice_t *boot_device)
{ int rc = 0;
switch (form) { case MPI2_BIOSPAGE2_FORM_SAS_WWID: if (!sas_address) break;
rc = _scsih_srch_boot_sas_address(
sas_address, &boot_device->SasWwid); break; case MPI2_BIOSPAGE2_FORM_ENCLOSURE_SLOT: if (!enclosure_logical_id) break;
rc = _scsih_srch_boot_encl_slot(
enclosure_logical_id,
slot, &boot_device->EnclosureSlot); break; case MPI2_BIOSPAGE2_FORM_DEVICE_NAME: if (!device_name) break;
rc = _scsih_srch_boot_device_name(
device_name, &boot_device->DeviceName); break; case MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED: break;
}
return rc;
}
/** * _scsih_get_sas_address - set the sas_address for given device handle * @ioc: ? * @handle: device handle * @sas_address: sas address * * Return: 0 success, non-zero when failure
*/ staticint
_scsih_get_sas_address(struct MPT3SAS_ADAPTER *ioc, u16 handle,
u64 *sas_address)
{
Mpi2SasDevicePage0_t sas_device_pg0;
Mpi2ConfigReply_t mpi_reply;
u32 ioc_status;
*sas_address = 0;
if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
ioc_err(ioc, "failure at %s:%d/%s()!\n",
__FILE__, __LINE__, __func__); return -ENXIO;
}
ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { /* 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;
}
/* we hit this because the given parent handle doesn't exist */ if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) return -ENXIO;
/* else error case */
ioc_err(ioc, "handle(0x%04x), ioc_status(0x%04x), failure at %s:%d/%s()!\n",
handle, ioc_status, __FILE__, __LINE__, __func__); return -EIO;
}
/** * _scsih_determine_boot_device - determine boot device. * @ioc: per adapter 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. * The saved data to be used later in _scsih_probe_boot_devices().
*/ staticvoid
_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;
u64 enclosure_logical_id;
u16 slot;
/* only process this function when driver loads */ if (!ioc->is_driver_loading) return;
/* no Bios, return immediately */ if (!ioc->bios_pg3.BiosVersion) return;
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.
*/ staticstruct _pcie_device *
mpt3sas_get_pdev_from_target(struct MPT3SAS_ADAPTER *ioc, struct MPT3SAS_TARGET *tgt_priv)
{ struct _pcie_device *ret; unsignedlong 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;
}
/** * __mpt3sas_get_sdev_by_rphy - sas device search * @ioc: per adapter object * @rphy: sas_rphy pointer * * Context: This function will acquire ioc->sas_device_lock and will release * before returning the sas_device object. * * This searches for sas_device from rphy object * then return sas_device object.
*/ struct _sas_device *
__mpt3sas_get_sdev_by_rphy(struct MPT3SAS_ADAPTER *ioc, struct sas_rphy *rphy)
{ struct _sas_device *sas_device;
/** * __mpt3sas_get_sdev_by_addr - get _sas_device object corresponding to provided * sas address from sas_device_list list * @ioc: per adapter object * @sas_address: device sas address * @port: port number * * Search for _sas_device object corresponding to provided sas address, * if available return _sas_device object address otherwise return NULL.
*/ struct _sas_device *
__mpt3sas_get_sdev_by_addr(struct MPT3SAS_ADAPTER *ioc,
u64 sas_address, struct hba_port *port)
{ struct _sas_device *sas_device;
if (!port) return NULL;
assert_spin_locked(&ioc->sas_device_lock);
list_for_each_entry(sas_device, &ioc->sas_device_list, list) { if (sas_device->sas_address != sas_address) continue; if (sas_device->port != port) continue;
sas_device_get(sas_device); return sas_device;
}
list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) { if (sas_device->sas_address != sas_address) continue; if (sas_device->port != port) continue;
sas_device_get(sas_device); return sas_device;
}
return NULL;
}
/** * mpt3sas_get_sdev_by_addr - sas device search * @ioc: per adapter object * @sas_address: sas address * @port: hba port entry * Context: Calling function should acquire ioc->sas_device_lock * * This searches for sas_device based on sas_address & port number, * then return sas_device object.
*/ struct _sas_device *
mpt3sas_get_sdev_by_addr(struct MPT3SAS_ADAPTER *ioc,
u64 sas_address, struct hba_port *port)
{ struct _sas_device *sas_device; unsignedlong flags;
/** * _scsih_sas_device_remove - remove sas_device from list. * @ioc: per adapter object * @sas_device: the sas_device object * Context: This function will acquire ioc->sas_device_lock. * * If sas_device is on the list, remove it and decrement its reference count.
*/ staticvoid
_scsih_sas_device_remove(struct MPT3SAS_ADAPTER *ioc, struct _sas_device *sas_device)
{ unsignedlong flags;
if (!sas_device) return;
ioc_info(ioc, "removing handle(0x%04x), sas_addr(0x%016llx)\n",
sas_device->handle, (u64)sas_device->sas_address);
/* * The lock serializes access to the list, but we still need to verify * that nobody removed the entry while we were waiting on the lock.
*/
spin_lock_irqsave(&ioc->sas_device_lock, flags); if (!list_empty(&sas_device->list)) {
list_del_init(&sas_device->list);
sas_device_put(sas_device);
}
spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
}
/** * _scsih_sas_device_add - insert sas_device to the list. * @ioc: per adapter object * @sas_device: the sas_device object * Context: This function will acquire ioc->sas_device_lock. * * Adding new object to the ioc->sas_device_list.
*/ staticvoid
_scsih_sas_device_add(struct MPT3SAS_ADAPTER *ioc, struct _sas_device *sas_device)
{ unsignedlong flags;
if (ioc->hide_drives) {
clear_bit(sas_device->handle, ioc->pend_os_device_add); return;
}
if (!mpt3sas_transport_port_add(ioc, sas_device->handle,
sas_device->sas_address_parent, sas_device->port)) {
_scsih_sas_device_remove(ioc, sas_device);
} elseif (!sas_device->starget) { /* * When asyn 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) {
mpt3sas_transport_port_remove(ioc,
sas_device->sas_address,
sas_device->sas_address_parent,
sas_device->port);
_scsih_sas_device_remove(ioc, sas_device);
}
} else
clear_bit(sas_device->handle, ioc->pend_os_device_add);
}
/** * _scsih_sas_device_init_add - insert sas_device to the list. * @ioc: per adapter object * @sas_device: the sas_device object * Context: This function will acquire ioc->sas_device_lock. * * Adding new object at driver load time to the ioc->sas_device_init_list.
*/ staticvoid
_scsih_sas_device_init_add(struct MPT3SAS_ADAPTER *ioc, struct _sas_device *sas_device)
{ unsignedlong flags;
/** * 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.
*/ staticstruct _pcie_device *
mpt3sas_get_pdev_by_wwid(struct MPT3SAS_ADAPTER *ioc, u64 wwid)
{ struct _pcie_device *pcie_device; unsignedlong flags;
/** * 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; unsignedlong flags;
/** * _scsih_set_nvme_max_shutdown_latency - Update max_shutdown_latency. * @ioc: per adapter object * Context: This function will acquire ioc->pcie_device_lock * * Update ioc->max_shutdown_latency to that NVMe drives RTD3 Entry Latency * which has reported maximum among all available NVMe drives. * Minimum max_shutdown_latency will be six seconds.
*/ staticvoid
_scsih_set_nvme_max_shutdown_latency(struct MPT3SAS_ADAPTER *ioc)
{ struct _pcie_device *pcie_device; unsignedlong flags;
u16 shutdown_latency = IO_UNIT_CONTROL_SHUTDOWN_TIMEOUT;
/** * _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.
*/ staticvoid
_scsih_pcie_device_remove(struct MPT3SAS_ADAPTER *ioc, struct _pcie_device *pcie_device)
{ unsignedlong flags; int was_on_pcie_device_list = 0;
u8 update_latency = 0;
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;
} if (pcie_device->shutdown_latency == ioc->max_shutdown_latency)
update_latency = 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);
}
/* * This device's RTD3 Entry Latency matches IOC's * max_shutdown_latency. Recalculate IOC's max_shutdown_latency * from the available drives as current drive is getting removed.
*/ if (update_latency)
_scsih_set_nvme_max_shutdown_latency(ioc);
}
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);
} if (pcie_device->shutdown_latency == ioc->max_shutdown_latency)
update_latency = 1;
}
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);
}
/* * This device's RTD3 Entry Latency matches IOC's * max_shutdown_latency. Recalculate IOC's max_shutdown_latency * from the available drives as current drive is getting removed.
*/ if (update_latency)
_scsih_set_nvme_max_shutdown_latency(ioc);
}
/** * _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.
*/ staticvoid
_scsih_pcie_device_add(struct MPT3SAS_ADAPTER *ioc, struct _pcie_device *pcie_device)
{ unsignedlong flags;
if (pcie_device->access_status ==
MPI26_PCIEDEV0_ASTATUS_DEVICE_BLOCKED) {
clear_bit(pcie_device->handle, ioc->pend_os_device_add); return;
} if (scsi_add_device(ioc->shost, PCIE_CHANNEL, pcie_device->id, 0)) {
_scsih_pcie_device_remove(ioc, pcie_device);
} elseif (!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.
*/ staticvoid
_scsih_pcie_device_init_add(struct MPT3SAS_ADAPTER *ioc, struct _pcie_device *pcie_device)
{ unsignedlong flags;
/** * mpt3sas_scsih_expander_find_by_handle - expander device search * @ioc: per adapter object * @handle: expander handle (assigned by firmware) * Context: Calling function should acquire ioc->sas_device_lock * * This searches for expander device based on handle, then returns the * sas_node object.
*/ struct _sas_node *
mpt3sas_scsih_expander_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
{ struct _sas_node *sas_expander, *r;
r = NULL;
list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) { if (sas_expander->handle != handle) continue;
r = sas_expander; goto out;
}
out: return r;
}
/** * mpt3sas_scsih_enclosure_find_by_handle - exclosure device search * @ioc: per adapter object * @handle: enclosure handle (assigned by firmware) * Context: Calling function should acquire ioc->sas_device_lock * * This searches for enclosure device based on handle, then returns the * enclosure object.
*/ staticstruct _enclosure_node *
mpt3sas_scsih_enclosure_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
{ struct _enclosure_node *enclosure_dev, *r;
r = NULL;
list_for_each_entry(enclosure_dev, &ioc->enclosure_list, list) { if (le16_to_cpu(enclosure_dev->pg0.EnclosureHandle) != handle) continue;
r = enclosure_dev; goto out;
}
out: return r;
} /** * mpt3sas_scsih_expander_find_by_sas_address - expander device search * @ioc: per adapter object * @sas_address: sas address * @port: hba port entry * Context: Calling function should acquire ioc->sas_node_lock. * * This searches for expander device based on sas_address & port number, * then returns the sas_node object.
*/ struct _sas_node *
mpt3sas_scsih_expander_find_by_sas_address(struct MPT3SAS_ADAPTER *ioc,
u64 sas_address, struct hba_port *port)
{ struct _sas_node *sas_expander, *r = NULL;
if (!port) return r;
list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) { if (sas_expander->sas_address != sas_address) continue; if (sas_expander->port != port) continue;
r = sas_expander; goto out;
}
out: return r;
}
/** * _scsih_expander_node_add - insert expander device to the list. * @ioc: per adapter object * @sas_expander: the sas_device object * Context: This function will acquire ioc->sas_node_lock. * * Adding new object to the ioc->sas_expander_list.
*/ staticvoid
_scsih_expander_node_add(struct MPT3SAS_ADAPTER *ioc, struct _sas_node *sas_expander)
{ unsignedlong flags;
/** * _scsih_is_end_device - determines if device is an end device * @device_info: bitfield providing information about the device. * Context: none * * Return: 1 if end device.
*/ staticint
_scsih_is_end_device(u32 device_info)
{ if (device_info & MPI2_SAS_DEVICE_INFO_END_DEVICE &&
((device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) |
(device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET) |
(device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE))) return 1; else return 0;
}
/** * _scsih_is_nvme_pciescsi_device - determines if * device is an pcie nvme/scsi device * @device_info: bitfield providing information about the device. * Context: none * * Returns 1 if device is pcie device type nvme/scsi.
*/ staticint
_scsih_is_nvme_pciescsi_device(u32 device_info)
{ if (((device_info & MPI26_PCIE_DEVINFO_MASK_DEVICE_TYPE)
== MPI26_PCIE_DEVINFO_NVME) ||
((device_info & MPI26_PCIE_DEVINFO_MASK_DEVICE_TYPE)
== MPI26_PCIE_DEVINFO_SCSI)) return 1; else return 0;
}
/** * _scsih_scsi_lookup_find_by_target - search for matching channel:id * @ioc: per adapter object * @id: target id * @channel: channel * Context: This function will acquire ioc->scsi_lookup_lock. * * This will search for a matching channel:id in the scsi_lookup array, * returning 1 if found.
*/ static u8
_scsih_scsi_lookup_find_by_target(struct MPT3SAS_ADAPTER *ioc, int id, int channel)
{ int smid; struct scsi_cmnd *scmd;
for (smid = 1;
smid <= ioc->shost->can_queue; smid++) {
scmd = mpt3sas_scsih_scsi_lookup_get(ioc, smid); if (!scmd) continue; if (scmd->device->id == id &&
scmd->device->channel == channel) return 1;
} return 0;
}
/** * _scsih_scsi_lookup_find_by_lun - search for matching channel:id:lun * @ioc: per adapter object * @id: target id * @lun: lun number * @channel: channel * Context: This function will acquire ioc->scsi_lookup_lock. * * This will search for a matching channel:id:lun in the scsi_lookup array, * returning 1 if found.
*/ static u8
_scsih_scsi_lookup_find_by_lun(struct MPT3SAS_ADAPTER *ioc, int id, unsignedint lun, int channel)
{ int smid; struct scsi_cmnd *scmd;
for (smid = 1; smid <= ioc->shost->can_queue; smid++) {
scmd = mpt3sas_scsih_scsi_lookup_get(ioc, smid); if (!scmd) continue; if (scmd->device->id == id &&
scmd->device->channel == channel &&
scmd->device->lun == lun) return 1;
} return 0;
}
/** * mpt3sas_scsih_scsi_lookup_get - returns scmd entry * @ioc: per adapter object * @smid: system request message index * * Return: the smid stored scmd pointer. * Then will dereference the stored scmd pointer.
*/ struct scsi_cmnd *
mpt3sas_scsih_scsi_lookup_get(struct MPT3SAS_ADAPTER *ioc, u16 smid)
{ struct scsi_cmnd *scmd = NULL; struct scsiio_tracker *st;
Mpi25SCSIIORequest_t *mpi_request;
u16 tag = smid - 1;
/* * If SCSI IO request is outstanding at driver level then * DevHandle filed must be non-zero. If DevHandle is zero * then it means that this smid is free at driver level, * so return NULL.
*/ if (!mpi_request->DevHandle) return scmd;
scmd = scsi_host_find_tag(ioc->shost, unique_tag); if (scmd) {
st = scsi_cmd_priv(scmd); if (st->cb_idx == 0xFF || st->smid == 0)
scmd = NULL;
}
} return scmd;
}
/* * raid transport support - * Enabled for SLES11 and newer, in older kernels the driver will panic when * unloading the driver followed by a load - I believe that the subroutine * raid_class_release() is not cleaning up properly.
*/
if (mpt3sas_config_get_raid_volume_pg0(ioc, &mpi_reply, &vol_pg0,
MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle, sizeof(Mpi2RaidVolPage0_t))) {
ioc_err(ioc, "failure at %s:%d/%s()!\n",
__FILE__, __LINE__, __func__); goto out;
}
volstate = le32_to_cpu(vol_pg0.VolumeStatusFlags); if (volstate & MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS) {
state = RAID_STATE_RESYNCING; goto out;
}
switch (vol_pg0.VolumeState) { case MPI2_RAID_VOL_STATE_OPTIMAL: case MPI2_RAID_VOL_STATE_ONLINE:
state = RAID_STATE_ACTIVE; break; case MPI2_RAID_VOL_STATE_DEGRADED:
state = RAID_STATE_DEGRADED; break; case MPI2_RAID_VOL_STATE_FAILED: case MPI2_RAID_VOL_STATE_MISSING:
state = RAID_STATE_OFFLINE; break;
}
out: switch (ioc->hba_mpi_version_belonged) { case MPI2_VERSION:
raid_set_state(mpt2sas_raid_template, dev, state); break; case MPI25_VERSION: case MPI26_VERSION:
raid_set_state(mpt3sas_raid_template, dev, state); break;
}
}
if ((mpt3sas_config_get_raid_volume_pg0(ioc, &mpi_reply, vol_pg0,
MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, raid_device->handle, sz))) {
dfailprintk(ioc,
ioc_warn(ioc, "failure at %s:%d/%s()!\n",
__FILE__, __LINE__, __func__));
kfree(vol_pg0); return 1;
}
raid_device->volume_type = vol_pg0->VolumeType;
/* figure out what the underlying devices are by * obtaining the device_info bits for the 1st device
*/ if (!(mpt3sas_config_get_phys_disk_pg0(ioc, &mpi_reply,
&pd_pg0, MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM,
vol_pg0->PhysDisk[0].PhysDiskNum))) { if (!(mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply,
&sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE,
le16_to_cpu(pd_pg0.DevHandle)))) {
raid_device->device_info =
le32_to_cpu(sas_device_pg0.DeviceInfo);
}
}
if (!ssp_target)
_scsih_display_sata_capabilities(ioc, handle, sdev);
mpt3sas_scsih_change_queue_depth(sdev, qdepth);
if (ssp_target) {
sas_read_port_mode_page(sdev);
_scsih_enable_tlr(ioc, sdev);
}
return 0;
}
/** * scsih_bios_param - fetch head, sector, cylinder info for a disk * @sdev: scsi device struct * @bdev: pointer to block device context * @capacity: device size (in 512 byte sectors) * @params: three element array to place output: * params[0] number of heads (max 255) * params[1] number of sectors (max 63) * params[2] number of cylinders
*/ staticint
scsih_bios_param(struct scsi_device *sdev, struct block_device *bdev,
sector_t capacity, int params[])
{ int heads; int sectors;
sector_t cylinders;
ulong dummy;
/** * _scsih_response_code - translation of device response code * @ioc: per adapter object * @response_code: response code returned by the device
*/ staticvoid
_scsih_response_code(struct MPT3SAS_ADAPTER *ioc, u8 response_code)
{ char *desc;
switch (response_code) { case MPI2_SCSITASKMGMT_RSP_TM_COMPLETE:
desc = "task management request completed"; break; case MPI2_SCSITASKMGMT_RSP_INVALID_FRAME:
desc = "invalid frame"; break; case MPI2_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED:
desc = "task management request not supported"; break; case MPI2_SCSITASKMGMT_RSP_TM_FAILED:
desc = "task management request failed"; break; case MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED:
desc = "task management request succeeded"; break; case MPI2_SCSITASKMGMT_RSP_TM_INVALID_LUN:
desc = "invalid lun"; break; case 0xA:
desc = "overlapped tag attempted"; break; case MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC:
desc = "task queued, however not sent to target"; break; default:
desc = "unknown"; break;
}
ioc_warn(ioc, "response_code(0x%01x): %s\n", response_code, desc);
}
/** * _scsih_tm_done - tm completion routine * @ioc: per adapter object * @smid: system request message index * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * Context: none. * * The callback handler when using scsih_issue_tm. * * Return: 1 meaning mf should be freed from _base_interrupt * 0 means the mf is freed from this function.
*/ static u8
_scsih_tm_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
{
MPI2DefaultReply_t *mpi_reply;
/** * mpt3sas_scsih_set_tm_flag - set per target tm_busy * @ioc: per adapter object * @handle: device handle * * During taskmangement request, we need to freeze the device queue.
*/ void
mpt3sas_scsih_set_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle)
{ struct MPT3SAS_DEVICE *sas_device_priv_data; struct scsi_device *sdev;
u8 skip = 0;
shost_for_each_device(sdev, ioc->shost) { if (skip) continue;
sas_device_priv_data = sdev->hostdata; if (!sas_device_priv_data) continue; if (sas_device_priv_data->sas_target->handle == handle) {
sas_device_priv_data->sas_target->tm_busy = 1;
skip = 1;
ioc->ignore_loginfos = 1;
}
}
}
/** * mpt3sas_scsih_clear_tm_flag - clear per target tm_busy * @ioc: per adapter object * @handle: device handle * * During taskmangement request, we need to freeze the device queue.
*/ void
mpt3sas_scsih_clear_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle)
{ struct MPT3SAS_DEVICE *sas_device_priv_data; struct scsi_device *sdev;
u8 skip = 0;
shost_for_each_device(sdev, ioc->shost) { if (skip) continue;
sas_device_priv_data = sdev->hostdata; if (!sas_device_priv_data) continue; if (sas_device_priv_data->sas_target->handle == handle) {
sas_device_priv_data->sas_target->tm_busy = 0;
skip = 1;
ioc->ignore_loginfos = 0;
}
}
}
/** * scsih_tm_cmd_map_status - map the target reset & LUN reset TM status * @ioc: per adapter object * @channel: the channel assigned by the OS * @id: the id assigned by the OS * @lun: lun number * @type: MPI2_SCSITASKMGMT_TASKTYPE__XXX (defined in mpi2_init.h) * @smid_task: smid assigned to the task * * Look whether TM has aborted the timed out SCSI command, if * TM has aborted the IO then return SUCCESS else return FAILED.
*/ staticint
scsih_tm_cmd_map_status(struct MPT3SAS_ADAPTER *ioc, uint channel,
uint id, uint lun, u8 type, u16 smid_task)
{
if (smid_task <= ioc->shost->can_queue) { switch (type) { case MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET: if (!(_scsih_scsi_lookup_find_by_target(ioc,
id, channel))) return SUCCESS; break; case MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET: case MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET: if (!(_scsih_scsi_lookup_find_by_lun(ioc, id,
lun, channel))) return SUCCESS; break; default: return SUCCESS;
}
} elseif (smid_task == ioc->scsih_cmds.smid) { if ((ioc->scsih_cmds.status & MPT3_CMD_COMPLETE) ||
(ioc->scsih_cmds.status & MPT3_CMD_NOT_USED)) return SUCCESS;
} elseif (smid_task == ioc->ctl_cmds.smid) { if ((ioc->ctl_cmds.status & MPT3_CMD_COMPLETE) ||
(ioc->ctl_cmds.status & MPT3_CMD_NOT_USED)) return SUCCESS;
}
return FAILED;
}
/** * scsih_tm_post_processing - post processing of target & LUN reset * @ioc: per adapter object * @handle: device handle * @channel: the channel assigned by the OS * @id: the id assigned by the OS * @lun: lun number * @type: MPI2_SCSITASKMGMT_TASKTYPE__XXX (defined in mpi2_init.h) * @smid_task: smid assigned to the task * * Post processing of target & LUN reset. Due to interrupt latency * issue it possible that interrupt for aborted IO might not be * received yet. So before returning failure status, poll the * reply descriptor pools for the reply of timed out SCSI command. * Return FAILED status if reply for timed out is not received * otherwise return SUCCESS.
*/ staticint
scsih_tm_post_processing(struct MPT3SAS_ADAPTER *ioc, u16 handle,
uint channel, uint id, uint lun, u8 type, u16 smid_task)
{ int rc;
/* * Due to interrupt latency issues, driver may receive interrupt for * TM first and then for aborted SCSI IO command. So, poll all the * ReplyDescriptor pools before returning the FAILED status to SML.
*/
mpt3sas_base_mask_interrupts(ioc);
mpt3sas_base_sync_reply_irqs(ioc, 1);
mpt3sas_base_unmask_interrupts(ioc);
/** * mpt3sas_scsih_issue_tm - main routine for sending tm requests * @ioc: per adapter struct * @handle: device handle * @channel: the channel assigned by the OS * @id: the id assigned by the OS * @lun: lun number * @type: MPI2_SCSITASKMGMT_TASKTYPE__XXX (defined in mpi2_init.h) * @smid_task: smid assigned to the task * @msix_task: MSIX table index supplied by the OS * @timeout: timeout in seconds * @tr_method: Target Reset Method * Context: user * * A generic API for sending task management requests to firmware. * * The callback index is set inside `ioc->tm_cb_idx`. * The caller is responsible to check for outstanding commands. * * Return: SUCCESS or FAILED.
*/ int
mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, uint channel,
uint id, u64 lun, u8 type, u16 smid_task, u16 msix_task,
u8 timeout, u8 tr_method)
{
Mpi2SCSITaskManagementRequest_t *mpi_request;
Mpi2SCSITaskManagementReply_t *mpi_reply;
Mpi25SCSIIORequest_t *request;
u16 smid = 0;
u32 ioc_state; int rc;
u8 issue_reset = 0;
switch (type) { case MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK:
rc = SUCCESS; /* * If DevHandle filed in smid_task's entry of request pool * doesn't match with device handle on which this task abort * TM is received then it means that TM has successfully * aborted the timed out command. Since smid_task's entry in * request pool will be memset to zero once the timed out * command is returned to the SML. If the command is not * aborted then smid_task’s entry won’t be cleared and it * will have same DevHandle value on which this task abort TM * is received and driver will return the TM status as FAILED.
*/
request = mpt3sas_base_get_msg_frame(ioc, smid_task); if (le16_to_cpu(request->DevHandle) != handle) break;
sas_device_priv_data = scmd->device->hostdata; if (!sas_device_priv_data || !sas_device_priv_data->sas_target ||
ioc->remove_host) {
sdev_printk(KERN_INFO, scmd->device, "device been deleted! scmd(0x%p)\n", scmd);
scmd->result = DID_NO_CONNECT << 16;
scsi_done(scmd);
r = SUCCESS; goto out;
}
/* check for completed command */ if (st == NULL || st->cb_idx == 0xFF) {
sdev_printk(KERN_INFO, scmd->device, "No reference found at " "driver, assuming scmd(0x%p) might have completed\n", scmd);
scmd->result = DID_RESET << 16;
r = SUCCESS; goto out;
}
/* for hidden raid components and volumes this is not supported */ if (sas_device_priv_data->sas_target->flags &
MPT_TARGET_FLAGS_RAID_COMPONENT ||
sas_device_priv_data->sas_target->flags & MPT_TARGET_FLAGS_VOLUME) {
scmd->result = DID_RESET << 16;
r = FAILED; goto out;
}
mpt3sas_halt_firmware(ioc);
handle = sas_device_priv_data->sas_target->handle;
pcie_device = mpt3sas_get_pdev_by_handle(ioc, handle); if (pcie_device && (!ioc->tm_custom_handling) &&
(!(mpt3sas_scsih_is_pcie_scsi_device(pcie_device->device_info))))
timeout = ioc->nvme_abort_timeout;
r = mpt3sas_scsih_issue_locked_tm(ioc, handle, scmd->device->channel,
scmd->device->id, scmd->device->lun,
MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK,
st->smid, st->msix_io, timeout, 0); /* Command must be cleared after abort */ if (r == SUCCESS && st->cb_idx != 0xFF)
r = FAILED;
out:
sdev_printk(KERN_INFO, scmd->device, "task abort: %s scmd(0x%p)\n",
((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); if (pcie_device)
pcie_device_put(pcie_device); return r;
}
/** * _scsih_fw_event_add - insert and queue up fw_event * @ioc: per adapter object * @fw_event: object describing the event * Context: This function will acquire ioc->fw_event_lock. * * This adds the firmware event object into link list, then queues it up to * be processed from user context.
*/ staticvoid
_scsih_fw_event_add(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event)
{ unsignedlong flags;
/** * _scsih_fw_event_del_from_list - delete fw_event from the list * @ioc: per adapter object * @fw_event: object describing the event * Context: This function will acquire ioc->fw_event_lock. * * If the fw_event is on the fw_event_list, remove it and do a put.
*/ staticvoid
_scsih_fw_event_del_from_list(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work
*fw_event)
{ unsignedlong flags;
/** * _scsih_fw_event_cleanup_queue - cleanup event queue * @ioc: per adapter object * * Walk the firmware event queue, either killing timers, or waiting * for outstanding events to complete * * Context: task, can sleep
*/ staticvoid
_scsih_fw_event_cleanup_queue(struct MPT3SAS_ADAPTER *ioc)
{ struct fw_event_work *fw_event;
if ((list_empty(&ioc->fw_event_list) && !ioc->current_event) ||
!ioc->firmware_event_thread) return; /* * Set current running event as ignore, so that * current running event will exit quickly. * As diag reset has occurred it is of no use * to process remaining stale event data entries.
*/ if (ioc->shost_recovery && ioc->current_event)
ioc->current_event->ignore = 1;
/* * Don't call cancel_work_sync() for current_event * other than MPT3SAS_REMOVE_UNRESPONDING_DEVICES; * otherwise we may observe deadlock if current * hard reset issued as part of processing the current_event. * * Orginal logic of cleaning the current_event is added * for handling the back to back host reset issued by the user. * i.e. during back to back host reset, driver use to process * the two instances of MPT3SAS_REMOVE_UNRESPONDING_DEVICES * event back to back and this made the drives to unregister * the devices from SML.
*/
/* * Driver has to clear ioc->start_scan flag when * it is cleaning up MPT3SAS_PORT_ENABLE_COMPLETE, * otherwise scsi_scan_host() API waits for the * 5 minute timer to expire. If we exit from * scsi_scan_host() early then we can issue the * new port enable request as part of current diag reset.
*/ if (fw_event->event == MPT3SAS_PORT_ENABLE_COMPLETE) {
ioc->port_enable_cmds.status |= MPT3_CMD_RESET;
ioc->start_scan = 0;
}
/* * Wait on the fw_event to complete. If this returns 1, then * the event was never executed, and we need a put for the * reference the work had on the fw_event. * * If it did execute, we wait for it to finish, and the put will * happen from _firmware_event_work()
*/ if (cancel_work_sync(&fw_event->work))
fw_event_work_put(fw_event);
}
ioc->fw_events_cleanup = 0;
}
/** * _scsih_internal_device_block - block the sdev device * @sdev: per device object * @sas_device_priv_data : per device driver private data * * make sure device is blocked without error, if not * print an error
*/ staticvoid
_scsih_internal_device_block(struct scsi_device *sdev, struct MPT3SAS_DEVICE *sas_device_priv_data)
{ int r = 0;
r = scsi_internal_device_block_nowait(sdev); if (r == -EINVAL)
sdev_printk(KERN_WARNING, sdev, "device_block failed with return(%d) for handle(0x%04x)\n",
r, sas_device_priv_data->sas_target->handle);
}
/** * _scsih_internal_device_unblock - unblock the sdev device * @sdev: per device object * @sas_device_priv_data : per device driver private data * make sure device is unblocked without error, if not retry * by blocking and then unblocking
*/
staticvoid
_scsih_internal_device_unblock(struct scsi_device *sdev, struct MPT3SAS_DEVICE *sas_device_priv_data)
{ int r = 0;
sdev_printk(KERN_WARNING, sdev, "device_unblock and setting to running, " "handle(0x%04x)\n", sas_device_priv_data->sas_target->handle);
sas_device_priv_data->block = 0;
r = scsi_internal_device_unblock_nowait(sdev, SDEV_RUNNING); if (r == -EINVAL) { /* The device has been set to SDEV_RUNNING by SD layer during * device addition but the request queue is still stopped by * our earlier block call. We need to perform a block again
* to get the device to SDEV_BLOCK and then to SDEV_RUNNING */
sdev_printk(KERN_WARNING, sdev, "device_unblock failed with return(%d) for handle(0x%04x) " "performing a block followed by an unblock\n",
r, sas_device_priv_data->sas_target->handle);
sas_device_priv_data->block = 1;
r = scsi_internal_device_block_nowait(sdev); if (r)
sdev_printk(KERN_WARNING, sdev, "retried device_block " "failed with return(%d) for handle(0x%04x)\n",
r, sas_device_priv_data->sas_target->handle);
sas_device_priv_data->block = 0;
r = scsi_internal_device_unblock_nowait(sdev, SDEV_RUNNING); if (r)
sdev_printk(KERN_WARNING, sdev, "retried device_unblock" " failed with return(%d) for handle(0x%04x)\n",
r, sas_device_priv_data->sas_target->handle);
}
}
/** * _scsih_ublock_io_all_device - unblock every device * @ioc: per adapter object * * change the device state from block to running
*/ staticvoid
_scsih_ublock_io_all_device(struct MPT3SAS_ADAPTER *ioc)
{ struct MPT3SAS_DEVICE *sas_device_priv_data; struct scsi_device *sdev;
shost_for_each_device(sdev, ioc->shost) {
sas_device_priv_data = sdev->hostdata; if (!sas_device_priv_data) continue; if (!sas_device_priv_data->block) continue;
/** * _scsih_ublock_io_device - prepare device to be deleted * @ioc: per adapter object * @sas_address: sas address * @port: hba port entry * * unblock then put device in offline state
*/ staticvoid
_scsih_ublock_io_device(struct MPT3SAS_ADAPTER *ioc,
u64 sas_address, struct hba_port *port)
{ struct MPT3SAS_DEVICE *sas_device_priv_data; struct scsi_device *sdev;
shost_for_each_device(sdev, ioc->shost) {
sas_device_priv_data = sdev->hostdata; if (!sas_device_priv_data || !sas_device_priv_data->sas_target) continue; if (sas_device_priv_data->sas_target->sas_address
!= sas_address) continue; if (sas_device_priv_data->sas_target->port != port) continue; if (sas_device_priv_data->block)
_scsih_internal_device_unblock(sdev,
sas_device_priv_data);
}
}
/** * _scsih_block_io_all_device - set the device state to SDEV_BLOCK * @ioc: per adapter object * * During device pull we need to appropriately set the sdev state.
*/ staticvoid
_scsih_block_io_all_device(struct MPT3SAS_ADAPTER *ioc)
{ struct MPT3SAS_DEVICE *sas_device_priv_data; struct scsi_device *sdev;
shost_for_each_device(sdev, ioc->shost) {
sas_device_priv_data = sdev->hostdata; if (!sas_device_priv_data) continue; if (sas_device_priv_data->block) continue; if (sas_device_priv_data->ignore_delay_remove) {
sdev_printk(KERN_INFO, sdev, "%s skip device_block for SES handle(0x%04x)\n",
__func__, sas_device_priv_data->sas_target->handle); continue;
}
_scsih_internal_device_block(sdev, sas_device_priv_data);
}
}
/** * _scsih_block_io_device - set the device state to SDEV_BLOCK * @ioc: per adapter object * @handle: device handle * * During device pull we need to appropriately set the sdev state.
*/ staticvoid
_scsih_block_io_device(struct MPT3SAS_ADAPTER *ioc, u16 handle)
{ struct MPT3SAS_DEVICE *sas_device_priv_data; struct scsi_device *sdev; struct _sas_device *sas_device;
shost_for_each_device(sdev, ioc->shost) {
sas_device_priv_data = sdev->hostdata; if (!sas_device_priv_data) continue; if (sas_device_priv_data->sas_target->handle != handle) continue; if (sas_device_priv_data->block) continue; if (sas_device && sas_device->pend_sas_rphy_add) continue; if (sas_device_priv_data->ignore_delay_remove) {
sdev_printk(KERN_INFO, sdev, "%s skip device_block for SES handle(0x%04x)\n",
__func__, sas_device_priv_data->sas_target->handle); continue;
}
_scsih_internal_device_block(sdev, sas_device_priv_data);
}
if (sas_device)
sas_device_put(sas_device);
}
/** * _scsih_block_io_to_children_attached_to_ex * @ioc: per adapter object * @sas_expander: the sas_device object * * This routine set sdev state to SDEV_BLOCK for all devices * attached to this expander. This function called when expander is * pulled.
*/ staticvoid
_scsih_block_io_to_children_attached_to_ex(struct MPT3SAS_ADAPTER *ioc, struct _sas_node *sas_expander)
{ struct _sas_port *mpt3sas_port; struct _sas_device *sas_device; struct _sas_node *expander_sibling; unsignedlong flags;
/** * _scsih_block_io_to_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.
*/ staticvoid
_scsih_block_io_to_children_attached_directly(struct MPT3SAS_ADAPTER *ioc,
Mpi2EventDataSasTopologyChangeList_t *event_data)
{ int i;
u16 handle;
u16 reason_code;
for (i = 0; i < event_data->NumEntries; i++) {
handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle); if (!handle) continue;
reason_code = event_data->PHY[i].PhyStatus &
MPI2_EVENT_SAS_TOPO_RC_MASK; if (reason_code == MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING)
_scsih_block_io_device(ioc, handle);
}
}
/** * _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.
*/ staticvoid
_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 * Context: interrupt time. * * This code is to initiate the device removal handshake protocol * with controller firmware. This function will issue target reset * using high priority request queue. It will send a sas iounit * control request (MPI2_SAS_OP_REMOVE_DEVICE) from this completion. * * This is designed to send muliple task management request at the same * time to the fifo. If the fifo is full, we will append the request, * and process it in a future completion.
*/ staticvoid
_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; unsignedlong flags; struct _tr_list *delayed_tr;
u32 ioc_state;
u8 tr_method = 0; struct hba_port *port = NULL;
if (ioc->pci_error_recovery) {
dewtprintk(ioc,
ioc_info(ioc, "%s: host in pci error recovery: handle(0x%04x)\n",
__func__, handle)); return;
}
ioc_state = mpt3sas_base_get_iocstate(ioc, 1); if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
dewtprintk(ioc,
ioc_info(ioc, "%s: host is not operational: handle(0x%04x)\n",
__func__, handle)); return;
}
/* if PD, then return */ if (test_bit(handle, ioc->pd_handles)) return;
out: if (sas_device)
sas_device_put(sas_device); if (pcie_device)
pcie_device_put(pcie_device);
}
/** * _scsih_tm_tr_complete - * @ioc: per adapter object * @smid: system request message index * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * Context: interrupt time. * * This is the target reset completion routine. * This code is part of the code to initiate the device removal * handshake protocol with controller firmware. * It will send a sas iounit control request (MPI2_SAS_OP_REMOVE_DEVICE) * * Return: 1 meaning mf should be freed from _base_interrupt * 0 means the mf is freed from this function.
*/ static u8
_scsih_tm_tr_complete(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
u32 reply)
{
u16 handle;
Mpi2SCSITaskManagementRequest_t *mpi_request_tm;
Mpi2SCSITaskManagementReply_t *mpi_reply =
mpt3sas_base_get_reply_virt_addr(ioc, reply);
Mpi2SasIoUnitControlRequest_t *mpi_request;
u16 smid_sas_ctrl;
u32 ioc_state; struct _sc_list *delayed_sc;
if (ioc->pci_error_recovery) {
dewtprintk(ioc,
ioc_info(ioc, "%s: host in pci error recovery\n",
__func__)); return 1;
}
ioc_state = mpt3sas_base_get_iocstate(ioc, 1); if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
dewtprintk(ioc,
ioc_info(ioc, "%s: host is not operational\n",
__func__)); return 1;
} if (unlikely(!mpi_reply)) {
ioc_err(ioc, "mpi_reply not valid at %s:%d/%s()!\n",
__FILE__, __LINE__, __func__); return 1;
}
mpi_request_tm = mpt3sas_base_get_msg_frame(ioc, smid);
handle = le16_to_cpu(mpi_request_tm->DevHandle); if (handle != le16_to_cpu(mpi_reply->DevHandle)) {
dewtprintk(ioc,
ioc_err(ioc, "spurious interrupt: handle(0x%04x:0x%04x), smid(%d)!!!\n",
handle,
le16_to_cpu(mpi_reply->DevHandle), smid)); return 0;
}
/** _scsih_allow_scmd_to_device - check whether scmd needs to * issue to IOC or not. * @ioc: per adapter object * @scmd: pointer to scsi command object * * Returns true if scmd can be issued to IOC otherwise returns false.
*/ inlinebool _scsih_allow_scmd_to_device(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd)
{
if (ioc->pci_error_recovery) returnfalse;
if (ioc->hba_mpi_version_belonged == MPI2_VERSION) { if (ioc->remove_host) returnfalse;
returntrue;
}
if (ioc->remove_host) {
switch (scmd->cmnd[0]) { case SYNCHRONIZE_CACHE: case START_STOP: returntrue; default: returnfalse;
}
}
returntrue;
}
/** * _scsih_sas_control_complete - completion routine * @ioc: per adapter object * @smid: system request message index * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * Context: interrupt time. * * This is the sas iounit control completion routine. * This code is part of the code to initiate the device removal * handshake protocol with controller firmware. * * Return: 1 meaning mf should be freed from _base_interrupt * 0 means the mf is freed from this function.
*/ static u8
_scsih_sas_control_complete(struct MPT3SAS_ADAPTER *ioc, u16 smid,
u8 msix_index, u32 reply)
{
Mpi2SasIoUnitControlReply_t *mpi_reply =
mpt3sas_base_get_reply_virt_addr(ioc, reply);
if (likely(mpi_reply)) {
dewtprintk(ioc,
ioc_info(ioc, "sc_complete:handle(0x%04x), (open) smid(%d), ioc_status(0x%04x), loginfo(0x%08x)\n",
le16_to_cpu(mpi_reply->DevHandle), smid,
le16_to_cpu(mpi_reply->IOCStatus),
le32_to_cpu(mpi_reply->IOCLogInfo))); if (le16_to_cpu(mpi_reply->IOCStatus) ==
MPI2_IOCSTATUS_SUCCESS) {
clear_bit(le16_to_cpu(mpi_reply->DevHandle),
ioc->device_remove_in_progress);
}
} else {
ioc_err(ioc, "mpi_reply not valid at %s:%d/%s()!\n",
__FILE__, __LINE__, __func__);
} return mpt3sas_check_for_pending_internal_cmds(ioc, smid);
}
/** * _scsih_tm_tr_volume_send - send target reset request for volumes * @ioc: per adapter object * @handle: device handle * Context: interrupt time. * * This is designed to send muliple task management request at the same * time to the fifo. If the fifo is full, we will append the request, * and process it in a future completion.
*/ staticvoid
_scsih_tm_tr_volume_send(struct MPT3SAS_ADAPTER *ioc, u16 handle)
{
Mpi2SCSITaskManagementRequest_t *mpi_request;
u16 smid; struct _tr_list *delayed_tr;
if (ioc->pci_error_recovery) {
dewtprintk(ioc,
ioc_info(ioc, "%s: host reset in progress!\n",
__func__)); return;
}
/** * _scsih_issue_delayed_event_ack - issue delayed Event ACK messages * @ioc: per adapter object * @smid: system request message index * @event: Event ID * @event_context: used to track events uniquely * * Context - processed in interrupt context.
*/ staticvoid
_scsih_issue_delayed_event_ack(struct MPT3SAS_ADAPTER *ioc, u16 smid, U16 event,
U32 event_context)
{
Mpi2EventAckRequest_t *ack_request; int i = smid - ioc->internal_smid; unsignedlong flags;
/* Without releasing the smid just update the * call back index and reuse the same smid for * processing this delayed request
*/
spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
ioc->internal_lookup[i].cb_idx = ioc->base_cb_idx;
spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
/** * _scsih_issue_delayed_sas_io_unit_ctrl - issue delayed * sas_io_unit_ctrl messages * @ioc: per adapter object * @smid: system request message index * @handle: device handle * * Context - processed in interrupt context.
*/ staticvoid
_scsih_issue_delayed_sas_io_unit_ctrl(struct MPT3SAS_ADAPTER *ioc,
u16 smid, u16 handle)
{
Mpi2SasIoUnitControlRequest_t *mpi_request;
u32 ioc_state; int i = smid - ioc->internal_smid; unsignedlong flags;
if (ioc->remove_host) {
dewtprintk(ioc,
ioc_info(ioc, "%s: host has been removed\n",
__func__)); return;
} elseif (ioc->pci_error_recovery) {
dewtprintk(ioc,
ioc_info(ioc, "%s: host in pci error recovery\n",
__func__)); return;
}
ioc_state = mpt3sas_base_get_iocstate(ioc, 1); if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
dewtprintk(ioc,
ioc_info(ioc, "%s: host is not operational\n",
__func__)); return;
}
/* Without releasing the smid just update the * call back index and reuse the same smid for * processing this delayed request
*/
spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
ioc->internal_lookup[i].cb_idx = ioc->tm_sas_control_cb_idx;
spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
/** * mpt3sas_check_for_pending_internal_cmds - check for pending internal messages * @ioc: per adapter object * @smid: system request message index * * Context: Executed in interrupt context * * This will check delayed internal messages list, and process the * next request. * * Return: 1 meaning mf should be freed from _base_interrupt * 0 means the mf is freed from this function.
*/
u8
mpt3sas_check_for_pending_internal_cmds(struct MPT3SAS_ADAPTER *ioc, u16 smid)
{ struct _sc_list *delayed_sc; struct _event_ack_list *delayed_event_ack;
/** * _scsih_check_for_pending_tm - check for pending task management * @ioc: per adapter object * @smid: system request message index * * This will check delayed target reset list, and feed the * next reqeust. * * Return: 1 meaning mf should be freed from _base_interrupt * 0 means the mf is freed from this function.
*/ static u8
_scsih_check_for_pending_tm(struct MPT3SAS_ADAPTER *ioc, u16 smid)
{ struct _tr_list *delayed_tr;
/** * _scsih_check_topo_delete_events - sanity check on topo events * @ioc: per adapter object * @event_data: the event data payload * * This routine added to better handle cable breaker. * * This handles the case where driver receives multiple expander * 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.
*/ staticvoid
_scsih_check_topo_delete_events(struct MPT3SAS_ADAPTER *ioc,
Mpi2EventDataSasTopologyChangeList_t *event_data)
{ struct fw_event_work *fw_event;
Mpi2EventDataSasTopologyChangeList_t *local_event_data;
u16 expander_handle; struct _sas_node *sas_expander; unsignedlong flags; int i, reason_code;
u16 handle;
for (i = 0 ; i < event_data->NumEntries; i++) {
handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle); if (!handle) continue;
reason_code = event_data->PHY[i].PhyStatus &
MPI2_EVENT_SAS_TOPO_RC_MASK; if (reason_code == MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING)
_scsih_tm_tr_send(ioc, handle);
}
expander_handle = le16_to_cpu(event_data->ExpanderDevHandle); if (expander_handle < ioc->sas_hba.num_phys) {
_scsih_block_io_to_children_attached_directly(ioc, event_data); return;
} if (event_data->ExpStatus ==
MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING) { /* put expander attached devices into blocking state */
spin_lock_irqsave(&ioc->sas_node_lock, flags);
sas_expander = mpt3sas_scsih_expander_find_by_handle(ioc,
expander_handle);
_scsih_block_io_to_children_attached_to_ex(ioc, sas_expander);
spin_unlock_irqrestore(&ioc->sas_node_lock, flags); do {
handle = find_first_bit(ioc->blocking_handles,
ioc->facts.MaxDevHandle); if (handle < ioc->facts.MaxDevHandle)
_scsih_block_io_device(ioc, handle);
} while (test_and_clear_bit(handle, ioc->blocking_handles));
} elseif (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_RESPONDING)
_scsih_block_io_to_children_attached_directly(ioc, event_data);
if (event_data->ExpStatus != 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_SAS_TOPOLOGY_CHANGE_LIST ||
fw_event->ignore) continue;
local_event_data = (Mpi2EventDataSasTopologyChangeList_t *)
fw_event->event_data; if (local_event_data->ExpStatus ==
MPI2_EVENT_SAS_TOPO_ES_ADDED ||
local_event_data->ExpStatus ==
MPI2_EVENT_SAS_TOPO_ES_RESPONDING) { if (le16_to_cpu(local_event_data->ExpanderDevHandle) ==
expander_handle) {
dewtprintk(ioc,
ioc_info(ioc, "setting ignoring flag\n"));
fw_event->ignore = 1;
}
}
}
spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
}
/** * _scsih_check_pcie_topo_remove_events - sanity check on topo * events * @ioc: per adapter object * @event_data: the event data payload * * This handles the case where driver receives multiple switch * or device add and delete events in a single shot. When there * is a delete event the routine will void any pending add * events waiting in the event queue.
*/ staticvoid
_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; unsignedlong 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,
ioc_info(ioc, "setting ignoring flag for switch event\n"));
fw_event->ignore = 1;
}
}
}
spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
}
/** * _scsih_set_volume_handle_for_tr - set handle for target reset to volume * @handle: input handle * @a: handle for volume a * @b: handle for volume b * * IR firmware only supports two raid volumes. The purpose of this * routine is to set the volume handle in either a or b. When the given * input handle is non-zero, or when a and b have not been set before.
*/ staticvoid
_scsih_set_volume_handle_for_tr(u16 handle, u16 *a, u16 *b)
{ if (!handle || handle == *a || handle == *b) return; if (!*a)
*a = handle; elseif (!*b)
*b = handle;
}
/** * _scsih_check_ir_config_unhide_events - check for UNHIDE events * @ioc: per adapter object * @event_data: the event data payload * Context: interrupt time. * * This routine will send target reset to volume, followed by target * resets to the PDs. This is called when a PD has been removed, or * volume has been deleted or removed. When the target reset is sent * to volume, the PD target resets need to be queued to start upon * completion of the volume target reset.
*/ staticvoid
_scsih_check_ir_config_unhide_events(struct MPT3SAS_ADAPTER *ioc,
Mpi2EventDataIrConfigChangeList_t *event_data)
{
Mpi2EventIrConfigElement_t *element; int i;
u16 handle, volume_handle, a, b; struct _tr_list *delayed_tr;
a = 0;
b = 0;
if (ioc->is_warpdrive) return;
/* Volume Resets for Deleted or Removed */
element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; for (i = 0; i < event_data->NumElements; i++, element++) { if (le32_to_cpu(event_data->Flags) &
MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) continue; if (element->ReasonCode ==
MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED ||
element->ReasonCode ==
MPI2_EVENT_IR_CHANGE_RC_REMOVED) {
volume_handle = le16_to_cpu(element->VolDevHandle);
_scsih_set_volume_delete_flag(ioc, volume_handle);
_scsih_set_volume_handle_for_tr(volume_handle, &a, &b);
}
}
/* Volume Resets for UNHIDE events */
element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; for (i = 0; i < event_data->NumElements; i++, element++) { if (le32_to_cpu(event_data->Flags) &
MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) continue; if (element->ReasonCode == MPI2_EVENT_IR_CHANGE_RC_UNHIDE) {
volume_handle = le16_to_cpu(element->VolDevHandle);
_scsih_set_volume_handle_for_tr(volume_handle, &a, &b);
}
}
if (a)
_scsih_tm_tr_volume_send(ioc, a); if (b)
_scsih_tm_tr_volume_send(ioc, b);
/* PD target resets */
element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; for (i = 0; i < event_data->NumElements; i++, element++) { if (element->ReasonCode != MPI2_EVENT_IR_CHANGE_RC_UNHIDE) continue;
handle = le16_to_cpu(element->PhysDiskDevHandle);
volume_handle = le16_to_cpu(element->VolDevHandle);
clear_bit(handle, ioc->pd_handles); if (!volume_handle)
_scsih_tm_tr_send(ioc, handle); elseif (volume_handle == a || volume_handle == b) {
delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC);
BUG_ON(!delayed_tr);
INIT_LIST_HEAD(&delayed_tr->list);
delayed_tr->handle = handle;
list_add_tail(&delayed_tr->list, &ioc->delayed_tr_list);
dewtprintk(ioc,
ioc_info(ioc, "DELAYED:tr:handle(0x%04x), (open)\n",
handle));
} else
_scsih_tm_tr_send(ioc, handle);
}
}
/** * _scsih_check_volume_delete_events - set delete flag for volumes * @ioc: per adapter object * @event_data: the event data payload * Context: interrupt time. * * This will handle the case when the cable connected to entire volume is * pulled. We will take care of setting the deleted flag so normal IO will * not be sent.
*/ staticvoid
_scsih_check_volume_delete_events(struct MPT3SAS_ADAPTER *ioc,
Mpi2EventDataIrVolume_t *event_data)
{
u32 state;
if (event_data->ReasonCode != MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED) return;
state = le32_to_cpu(event_data->NewValue); if (state == MPI2_RAID_VOL_STATE_MISSING || state ==
MPI2_RAID_VOL_STATE_FAILED)
_scsih_set_volume_delete_flag(ioc,
le16_to_cpu(event_data->VolDevHandle));
}
/** * _scsih_flush_running_cmds - completing outstanding commands. * @ioc: per adapter object * * The flushing out of all pending scmd commands following host reset, * where all IO is dropped to the floor.
*/ staticvoid
_scsih_flush_running_cmds(struct MPT3SAS_ADAPTER *ioc)
{ struct scsi_cmnd *scmd; struct scsiio_tracker *st;
u16 smid; int count = 0;
if (ioc->shost_recovery || ioc->ioc_link_reset_in_progress) { /* host recovery or link resets sent via IOCTLs */ return SCSI_MLQUEUE_HOST_BUSY;
} elseif (sas_target_priv_data->deleted) { /* device has been deleted */
scmd->result = DID_NO_CONNECT << 16;
scsi_done(scmd); return 0;
} elseif (sas_target_priv_data->tm_busy ||
sas_device_priv_data->block) { /* device busy with task management */ return SCSI_MLQUEUE_DEVICE_BUSY;
}
/* * Bug work around for firmware SATL handling. The loop * is based on atomic operations and ensures consistency * since we're lockless at this point
*/ do { if (test_bit(0, &sas_device_priv_data->ata_command_pending)) return SCSI_MLQUEUE_DEVICE_BUSY;
} while (_scsih_set_satl_pending(scmd, true));
/* set tags */
mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ; /* NCQ Prio supported, make sure control indicated high priority */ if (sas_device_priv_data->ncq_prio_enable) { class = IOPRIO_PRIO_CLASS(req_get_ioprio(rq)); if (class == IOPRIO_CLASS_RT)
mpi_control |= 1 << MPI2_SCSIIO_CONTROL_CMDPRI_SHIFT;
} /* 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))
&& !scsih_is_nvme(&scmd->device->sdev_gendev))
&& sas_is_tlr_enabled(scmd->device) && scmd->cmd_len != 32)
mpi_control |= MPI2_SCSIIO_CONTROL_TLR_ON;
/** * _scsih_normalize_sense - normalize descriptor and fixed format sense data * @sense_buffer: sense data returned by target * @data: normalized skey/asc/ascq
*/ staticvoid
_scsih_normalize_sense(char *sense_buffer, struct sense_info *data)
{ if ((sense_buffer[0] & 0x7F) >= 0x72) { /* descriptor format */
data->skey = sense_buffer[1] & 0x0F;
data->asc = sense_buffer[2];
data->ascq = sense_buffer[3];
} else { /* fixed format */
data->skey = sense_buffer[2] & 0x0F;
data->asc = sense_buffer[12];
data->ascq = sense_buffer[13];
}
}
/** * _scsih_scsi_ioc_info - translated non-successful SCSI_IO request * @ioc: per adapter object * @scmd: pointer to scsi command object * @mpi_reply: reply mf payload returned from firmware * @smid: ? * * scsi_status - SCSI Status code returned from target device * scsi_state - state info associated with SCSI_IO determined by ioc * ioc_status - ioc supplied status info
*/ staticvoid
_scsih_scsi_ioc_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
Mpi2SCSIIOReply_t *mpi_reply, u16 smid)
{
u32 response_info;
u8 *response_bytes;
u16 ioc_status = le16_to_cpu(mpi_reply->IOCStatus) &
MPI2_IOCSTATUS_MASK;
u8 scsi_state = mpi_reply->SCSIState;
u8 scsi_status = mpi_reply->SCSIStatus; char *desc_ioc_state = NULL; char *desc_scsi_status = NULL; 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;
if (!priv_target) return; if (ioc->hide_ir_msg)
device_str = "WarpDrive"; else
device_str = "volume";
if (log_info == 0x31170000) return;
switch (ioc_status) { case MPI2_IOCSTATUS_SUCCESS:
desc_ioc_state = "success"; break; case MPI2_IOCSTATUS_INVALID_FUNCTION:
desc_ioc_state = "invalid function"; break; case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR:
desc_ioc_state = "scsi recovered error"; break; case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE:
desc_ioc_state = "scsi invalid dev handle"; break; case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE:
desc_ioc_state = "scsi device not there"; break; case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN:
desc_ioc_state = "scsi data overrun"; break; case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN:
desc_ioc_state = "scsi data underrun"; break; case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR:
desc_ioc_state = "scsi io data error"; break; case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR:
desc_ioc_state = "scsi protocol error"; break; case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED:
desc_ioc_state = "scsi task terminated"; break; case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH:
desc_ioc_state = "scsi residual mismatch"; break; case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED:
desc_ioc_state = "scsi task mgmt failed"; break; case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED:
desc_ioc_state = "scsi ioc terminated"; break; case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED:
desc_ioc_state = "scsi ext terminated"; break; case MPI2_IOCSTATUS_EEDP_GUARD_ERROR:
desc_ioc_state = "eedp guard error"; break; case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR:
desc_ioc_state = "eedp ref tag error"; break; case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR:
desc_ioc_state = "eedp app tag error"; break; case MPI2_IOCSTATUS_INSUFFICIENT_POWER:
desc_ioc_state = "insufficient power"; break; default:
desc_ioc_state = "unknown"; break;
}
switch (scsi_status) { case MPI2_SCSI_STATUS_GOOD:
desc_scsi_status = "good"; break; case MPI2_SCSI_STATUS_CHECK_CONDITION:
desc_scsi_status = "check condition"; break; case MPI2_SCSI_STATUS_CONDITION_MET:
desc_scsi_status = "condition met"; break; case MPI2_SCSI_STATUS_BUSY:
desc_scsi_status = "busy"; break; case MPI2_SCSI_STATUS_INTERMEDIATE:
desc_scsi_status = "intermediate"; break; case MPI2_SCSI_STATUS_INTERMEDIATE_CONDMET:
desc_scsi_status = "intermediate condmet"; break; case MPI2_SCSI_STATUS_RESERVATION_CONFLICT:
desc_scsi_status = "reservation conflict"; break; case MPI2_SCSI_STATUS_COMMAND_TERMINATED:
desc_scsi_status = "command terminated"; break; case MPI2_SCSI_STATUS_TASK_SET_FULL:
desc_scsi_status = "task set full"; break; case MPI2_SCSI_STATUS_ACA_ACTIVE:
desc_scsi_status = "aca active"; break; case MPI2_SCSI_STATUS_TASK_ABORTED:
desc_scsi_status = "task aborted"; break; default:
desc_scsi_status = "unknown"; break;
}
desc_scsi_state[0] = '\0'; if (!scsi_state)
desc_scsi_state = " "; if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID)
strcat(desc_scsi_state, "response info "); if (scsi_state & MPI2_SCSI_STATE_TERMINATED)
strcat(desc_scsi_state, "state terminated "); if (scsi_state & MPI2_SCSI_STATE_NO_SCSI_STATUS)
strcat(desc_scsi_state, "no status "); if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_FAILED)
strcat(desc_scsi_state, "autosense failed "); if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID)
strcat(desc_scsi_state, "autosense valid ");
/** * _scsih_turn_off_pfa_led - turn off Fault LED * @ioc: per adapter object * @sas_device: sas device whose PFA LED has to turned off * Context: process
*/ staticvoid
_scsih_turn_off_pfa_led(struct MPT3SAS_ADAPTER *ioc, struct _sas_device *sas_device)
{
Mpi2SepReply_t mpi_reply;
Mpi2SepRequest_t mpi_request;
case MPI2_IOCSTATUS_EEDP_GUARD_ERROR: case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR: case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR:
_scsih_eedp_error_handling(scmd, ioc_status); break;
case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR: case MPI2_IOCSTATUS_INVALID_FUNCTION: case MPI2_IOCSTATUS_INVALID_SGL: case MPI2_IOCSTATUS_INTERNAL_ERROR: case MPI2_IOCSTATUS_INVALID_FIELD: case MPI2_IOCSTATUS_INVALID_STATE: case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR: case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED: case MPI2_IOCSTATUS_INSUFFICIENT_POWER: default:
scmd->result = DID_SOFT_ERROR << 16; break;
/* * Mark all the vphys objects as dirty.
*/
list_for_each_entry_safe(port, port_next,
&ioc->port_table_list, list) { if (!port->vphys_mask) continue;
list_for_each_entry_safe(vphy, vphy_next,
&port->vphys_list, list) {
vphy->flags |= MPT_VPHY_FLAG_DIRTY_PHY;
}
}
/* * Read SASIOUnitPage0 to get each HBA Phy's data.
*/
sz = struct_size(sas_iounit_pg0, PhyData, ioc->sas_hba.num_phys);
sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL); if (!sas_iounit_pg0) {
ioc_err(ioc, "failure at %s:%d/%s()!\n",
__FILE__, __LINE__, __func__); return;
} if ((mpt3sas_config_get_sas_iounit_pg0(ioc, &mpi_reply,
sas_iounit_pg0, sz)) != 0) goto out;
ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; if (ioc_status != MPI2_IOCSTATUS_SUCCESS) goto out; /* * Loop over each HBA Phy.
*/ for (i = 0; i < ioc->sas_hba.num_phys; i++) { /* * Check whether Phy's Negotiation Link Rate is > 1.5G or not.
*/ if ((sas_iounit_pg0->PhyData[i].NegotiatedLinkRate >> 4) <
MPI2_SAS_NEG_LINK_RATE_1_5) continue; /* * Check whether Phy is connected to SEP device or not, * if it is SEP device then read the Phy's SASPHYPage0 data to * determine whether Phy is a virtual Phy or not. if it is * virtual phy then it is conformed that the attached remote * device is a HBA's vSES device.
*/ if (!(le32_to_cpu(
sas_iounit_pg0->PhyData[i].ControllerPhyDeviceInfo) &
MPI2_SAS_DEVICE_INFO_SEP)) continue;
if ((mpt3sas_config_get_phy_pg0(ioc, &mpi_reply, &phy_pg0,
i))) {
ioc_err(ioc, "failure at %s:%d/%s()!\n",
__FILE__, __LINE__, __func__); continue;
}
if (!(le32_to_cpu(phy_pg0.PhyInfo) &
MPI2_SAS_PHYINFO_VIRTUAL_PHY)) continue; /* * Get the vSES device's SAS Address.
*/
attached_handle = le16_to_cpu(
sas_iounit_pg0->PhyData[i].AttachedDevHandle); if (_scsih_get_sas_address(ioc, attached_handle,
&attached_sas_addr) != 0) {
ioc_err(ioc, "failure at %s:%d/%s()!\n",
__FILE__, __LINE__, __func__); continue;
}
found = 0;
port = port_next = NULL; /* * Loop over each virtual_phy object from * each port's vphys_list.
*/
list_for_each_entry_safe(port,
port_next, &ioc->port_table_list, list) { if (!port->vphys_mask) continue;
list_for_each_entry_safe(vphy, vphy_next,
&port->vphys_list, list) { /* * Continue with next virtual_phy object * if the object is not marked as dirty.
*/ if (!(vphy->flags & MPT_VPHY_FLAG_DIRTY_PHY)) continue;
/* * Continue with next virtual_phy object * if the object's SAS Address is not equals * to current Phy's vSES device SAS Address.
*/ if (vphy->sas_address != attached_sas_addr) continue; /* * Enable current Phy number bit in object's * phy_mask field.
*/ if (!(vphy->phy_mask & (1 << i)))
vphy->phy_mask = (1 << i); /* * Get hba_port object from hba_port table * corresponding to current phy's Port ID. * if there is no hba_port object corresponding * to Phy's Port ID then create a new hba_port * object & add to hba_port table.
*/
port_id = sas_iounit_pg0->PhyData[i].Port;
mport = mpt3sas_get_port_by_id(ioc, port_id, 1); if (!mport) {
mport = kzalloc( sizeof(struct hba_port), GFP_KERNEL); if (!mport) break;
mport->port_id = port_id;
ioc_info(ioc, "%s: hba_port entry: %p, port: %d is added to hba_port list\n",
__func__, mport, mport->port_id);
list_add_tail(&mport->list,
&ioc->port_table_list);
} /* * If mport & port pointers are not pointing to * same hba_port object then it means that vSES * device's Port ID got changed after reset and * hence move current virtual_phy object from * port's vphys_list to mport's vphys_list.
*/ if (port != mport) { if (!mport->vphys_mask)
INIT_LIST_HEAD(
&mport->vphys_list);
mport->vphys_mask |= (1 << i);
port->vphys_mask &= ~(1 << i);
list_move(&vphy->list,
&mport->vphys_list);
sas_device = mpt3sas_get_sdev_by_addr(
ioc, attached_sas_addr, port); if (sas_device)
sas_device->port = mport;
} /* * Earlier while updating the hba_port table, * it is determined that there is no other * direct attached device with mport's Port ID, * Hence mport was marked as dirty. Only vSES * device has this Port ID, so unmark the mport * as dirt.
*/ if (mport->flags & HBA_PORT_FLAG_DIRTY_PORT) {
mport->sas_address = 0;
mport->phy_mask = 0;
mport->flags &=
~HBA_PORT_FLAG_DIRTY_PORT;
} /* * Unmark current virtual_phy object as dirty.
*/
vphy->flags &= ~MPT_VPHY_FLAG_DIRTY_PHY;
found = 1; break;
} if (found) break;
}
}
out:
kfree(sas_iounit_pg0);
}
/** * _scsih_get_port_table_after_reset - Construct temporary port table * @ioc: per adapter object * @port_table: address where port table needs to be constructed * * return number of HBA port entries available after reset.
*/ staticint
_scsih_get_port_table_after_reset(struct MPT3SAS_ADAPTER *ioc, struct hba_port *port_table)
{
u16 sz, ioc_status; int i, j;
Mpi2ConfigReply_t mpi_reply;
Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL;
u16 attached_handle;
u64 attached_sas_addr;
u8 found = 0, port_count = 0, port_id;
/** * _scsih_look_and_get_matched_port_entry - Get matched hba port entry * from HBA port table * @ioc: per adapter object * @port_entry: hba port entry from temporary port table which needs to be * searched for matched entry in the HBA port table * @matched_port_entry: save matched hba port entry here * @count: count of matched entries * * return type of matched entry found.
*/ staticenum hba_port_matched_codes
_scsih_look_and_get_matched_port_entry(struct MPT3SAS_ADAPTER *ioc, struct hba_port *port_entry, struct hba_port **matched_port_entry, int *count)
{ struct hba_port *port_table_entry, *matched_port = NULL; enum hba_port_matched_codes matched_code = NOT_MATCHED; int lcount = 0;
*matched_port_entry = NULL;
list_for_each_entry(port_table_entry, &ioc->port_table_list, list) { if (!(port_table_entry->flags & HBA_PORT_FLAG_DIRTY_PORT)) continue;
/** * _scsih_del_phy_part_of_anther_port - remove phy if it * is a part of anther port *@ioc: per adapter object *@port_table: port table after reset *@index: hba port entry index *@port_count: number of ports available after host reset *@offset: HBA phy bit offset *
*/ staticvoid
_scsih_del_phy_part_of_anther_port(struct MPT3SAS_ADAPTER *ioc, struct hba_port *port_table, int index, u8 port_count, int offset)
{ struct _sas_node *sas_node = &ioc->sas_hba;
u32 i, found = 0;
for (i = 0; i < port_count; i++) { if (i == index) continue;
if (port_table[i].phy_mask & (1 << offset)) {
mpt3sas_transport_del_phy_from_an_existing_port(
ioc, sas_node, &sas_node->phy[offset]);
found = 1; break;
}
} if (!found)
port_table[index].phy_mask |= (1 << offset);
}
/** * _scsih_add_or_del_phys_from_existing_port - add/remove phy to/from * right port *@ioc: per adapter object *@hba_port_entry: hba port table entry *@port_table: temporary port table *@index: hba port entry index *@port_count: number of ports available after host reset *
*/ staticvoid
_scsih_add_or_del_phys_from_existing_port(struct MPT3SAS_ADAPTER *ioc, struct hba_port *hba_port_entry, struct hba_port *port_table, int index, int port_count)
{
u32 phy_mask, offset = 0; struct _sas_node *sas_node = &ioc->sas_hba;
list_for_each_entry_safe(port, port_next,
&ioc->port_table_list, list) { if (!port->vphys_mask) continue;
list_for_each_entry_safe(vphy, vphy_next,
&port->vphys_list, list) { if (vphy->flags & MPT_VPHY_FLAG_DIRTY_PHY) {
drsprintk(ioc, ioc_info(ioc, "Deleting vphy %p entry from port id: %d\t, Phy_mask 0x%08x\n",
vphy, port->port_id,
vphy->phy_mask));
port->vphys_mask &= ~vphy->phy_mask;
list_del(&vphy->list);
kfree(vphy);
}
} if (!port->vphys_mask && !port->sas_address)
port->flags |= HBA_PORT_FLAG_DIRTY_PORT;
}
}
/** * _scsih_del_dirty_port_entries - delete dirty port entries from port list * after host reset *@ioc: per adapter object *
*/ staticvoid
_scsih_del_dirty_port_entries(struct MPT3SAS_ADAPTER *ioc)
{ struct hba_port *port, *port_next;
for (j = 0; j < port_count; j++) {
ret = _scsih_look_and_get_matched_port_entry(ioc,
&port_table[j], &port_entry, &count); if (!port_entry) {
drsprintk(ioc, ioc_info(ioc, "No Matched entry for sas_addr(0x%16llx), Port:%d\n",
port_table[j].sas_address,
port_table[j].port_id)); continue;
}
switch (ret) { case MATCHED_WITH_ADDR_SUBPHYMASK_AND_PORT: case MATCHED_WITH_ADDR_AND_SUBPHYMASK:
_scsih_add_or_del_phys_from_existing_port(ioc,
port_entry, port_table, j, port_count); break; case MATCHED_WITH_ADDR:
sas_addr = port_table[j].sas_address; for (i = 0; i < port_count; i++) { if (port_table[i].sas_address == sas_addr)
lcount++;
}
/** * _scsih_alloc_vphy - allocate virtual_phy object * @ioc: per adapter object * @port_id: Port ID number * @phy_num: HBA Phy number * * Returns allocated virtual_phy object.
*/ staticstruct virtual_phy *
_scsih_alloc_vphy(struct MPT3SAS_ADAPTER *ioc, u8 port_id, u8 phy_num)
{ struct virtual_phy *vphy; struct hba_port *port;
port = mpt3sas_get_port_by_id(ioc, port_id, 0); if (!port) return NULL;
vphy = mpt3sas_get_vphy_by_phy(ioc, port, phy_num); if (!vphy) {
vphy = kzalloc(sizeof(struct virtual_phy), GFP_KERNEL); if (!vphy) return NULL;
if (!port->vphys_mask)
INIT_LIST_HEAD(&port->vphys_list);
/* * Enable bit corresponding to HBA phy number on its * parent hba_port object's vphys_mask field.
*/
port->vphys_mask |= (1 << phy_num);
vphy->phy_mask |= (1 << phy_num);
list_add_tail(&vphy->list, &port->vphys_list);
ioc_info(ioc, "vphy entry: %p, port id: %d, phy:%d is added to port's vphys_list\n",
vphy, port->port_id, phy_num);
} return vphy;
}
/** * _scsih_sas_host_refresh - refreshing sas host object contents * @ioc: per adapter object * Context: user * * During port enable, fw will send topology events for every device. Its * possible that the handles may change from the previous setting, so this * code keeping handles updating if changed.
*/ staticvoid
_scsih_sas_host_refresh(struct MPT3SAS_ADAPTER *ioc)
{
u16 sz;
u16 ioc_status; int i;
Mpi2ConfigReply_t mpi_reply;
Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL;
u16 attached_handle;
u8 link_rate, port_id; struct hba_port *port;
Mpi2SasPhyPage0_t phy_pg0;
dtmprintk(ioc,
ioc_info(ioc, "updating handles for sas_host(0x%016llx)\n",
(u64)ioc->sas_hba.sas_address));
/** * _scsih_done - internal SCSI_IO callback handler. * @ioc: per adapter object * @smid: system request message index * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * * Callback handler when sending internal generated SCSI_IO. * The callback index passed is `ioc->scsih_cb_idx` * * Return: 1 meaning mf should be freed from _base_interrupt * 0 means the mf is freed from this function.
*/ static u8
_scsih_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
{
MPI2DefaultReply_t *mpi_reply;
/** * _scsih_check_access_status - check access flags * @ioc: per adapter object * @sas_address: sas address * @handle: sas device handle * @access_status: errors returned during discovery of the device * * Return: 0 for success, else failure
*/ static u8
_scsih_check_access_status(struct MPT3SAS_ADAPTER *ioc, u64 sas_address,
u16 handle, u8 access_status)
{
u8 rc = 1; char *desc = NULL;
switch (access_status) { case MPI2_SAS_DEVICE0_ASTATUS_NO_ERRORS: case MPI2_SAS_DEVICE0_ASTATUS_SATA_NEEDS_INITIALIZATION:
rc = 0; break; case MPI2_SAS_DEVICE0_ASTATUS_SATA_CAPABILITY_FAILED:
desc = "sata capability failed"; break; case MPI2_SAS_DEVICE0_ASTATUS_SATA_AFFILIATION_CONFLICT:
desc = "sata affiliation conflict"; break; case MPI2_SAS_DEVICE0_ASTATUS_ROUTE_NOT_ADDRESSABLE:
desc = "route not addressable"; break; case MPI2_SAS_DEVICE0_ASTATUS_SMP_ERROR_NOT_ADDRESSABLE:
desc = "smp error not addressable"; break; case MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED:
desc = "device blocked"; break; case MPI2_SAS_DEVICE0_ASTATUS_SATA_INIT_FAILED: case MPI2_SAS_DEVICE0_ASTATUS_SIF_UNKNOWN: case MPI2_SAS_DEVICE0_ASTATUS_SIF_AFFILIATION_CONFLICT: case MPI2_SAS_DEVICE0_ASTATUS_SIF_DIAG: case MPI2_SAS_DEVICE0_ASTATUS_SIF_IDENTIFICATION: case MPI2_SAS_DEVICE0_ASTATUS_SIF_CHECK_POWER: case MPI2_SAS_DEVICE0_ASTATUS_SIF_PIO_SN: case MPI2_SAS_DEVICE0_ASTATUS_SIF_MDMA_SN: case MPI2_SAS_DEVICE0_ASTATUS_SIF_UDMA_SN: case MPI2_SAS_DEVICE0_ASTATUS_SIF_ZONING_VIOLATION: case MPI2_SAS_DEVICE0_ASTATUS_SIF_NOT_ADDRESSABLE: case MPI2_SAS_DEVICE0_ASTATUS_SIF_MAX:
desc = "sata initialization failed"; break; default:
desc = "unknown"; break;
}
if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) return;
ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; if (ioc_status != MPI2_IOCSTATUS_SUCCESS) return;
/* wide port handling ~ we need only handle device once for the phy that * is matched in sas device page zero
*/ if (phy_number != sas_device_pg0.PhyNum) return;
/* check if this is end device */
device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); if (!(_scsih_is_end_device(device_info))) return;
spin_lock_irqsave(&ioc->sas_device_lock, flags);
sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
port = mpt3sas_get_port_by_id(ioc, sas_device_pg0.PhysicalPort, 0); if (!port) goto out_unlock;
sas_device = __mpt3sas_get_sdev_by_addr(ioc,
sas_address, port);
/* check if device is present */ if (!(le16_to_cpu(sas_device_pg0.Flags) &
MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)) {
ioc_err(ioc, "device is not present handle(0x%04x), flags!!!\n",
handle); goto out_unlock;
}
/* check if there were any issues with discovery */ if (_scsih_check_access_status(ioc, sas_address, handle,
sas_device_pg0.AccessStatus)) goto out_unlock;
if (sas_device)
sas_device_put(sas_device); return;
out_unlock:
spin_unlock_irqrestore(&ioc->sas_device_lock, flags); if (sas_device)
sas_device_put(sas_device);
}
/** * _scsih_add_device - creating sas device object * @ioc: per adapter object * @handle: sas device handle * @phy_num: phy number end device attached to * @is_pd: is this hidden raid component * * Creating end device object, stored in ioc->sas_device_list. * * Return: 0 for success, non-zero for failure.
*/ staticint
_scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num,
u8 is_pd)
{
Mpi2ConfigReply_t mpi_reply;
Mpi2SasDevicePage0_t sas_device_pg0; struct _sas_device *sas_device; struct _enclosure_node *enclosure_dev = NULL;
u32 ioc_status;
u64 sas_address;
u32 device_info;
u8 port_id;
if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
ioc_err(ioc, "failure at %s:%d/%s()!\n",
__FILE__, __LINE__, __func__); return -1;
}
ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
MPI2_IOCSTATUS_MASK; if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
ioc_err(ioc, "failure at %s:%d/%s()!\n",
__FILE__, __LINE__, __func__); return -1;
}
/* check if this is end device */
device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); if (!(_scsih_is_end_device(device_info))) return -1;
set_bit(handle, ioc->pend_os_device_add);
sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
/* check if device is present */ if (!(le16_to_cpu(sas_device_pg0.Flags) &
MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)) {
ioc_err(ioc, "device is not present handle(0x04%x)!!!\n",
handle); return -1;
}
/* check if there were any issues with discovery */ if (_scsih_check_access_status(ioc, sas_address, handle,
sas_device_pg0.AccessStatus)) return -1;
/* In MPI Revision K (0xC), the internal device reset complete was * implemented, so avoid setting tm_busy flag for older firmware.
*/ if ((ioc->facts.HeaderVersion >> 8) < 0xC) return;
if (event_data->ReasonCode !=
MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET &&
event_data->ReasonCode !=
MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET) return;
/* check if device is present */ if (!(le32_to_cpu(pcie_device_pg0.Flags) &
MPI26_PCIEDEV0_FLAGS_DEVICE_PRESENT)) {
ioc_info(ioc, "device is not present handle(0x%04x), flags!!!\n",
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;
}
/* check if device is present */ if (!(le32_to_cpu(pcie_device_pg0.Flags) &
MPI26_PCIEDEV0_FLAGS_DEVICE_PRESENT)) {
ioc_err(ioc, "device is not present handle(0x04%x)!!!\n",
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_pciescsi_device(le32_to_cpu
(pcie_device_pg0.DeviceInfo)))) return 0;
/* PCIe Device Page 2 contains read-only information about a * specific NVMe device; therefore, this page is only * valid for NVMe devices and skip for pcie devices of type scsi.
*/ if (!(mpt3sas_scsih_is_pcie_scsi_device(
le32_to_cpu(pcie_device_pg0.DeviceInfo)))) { if (mpt3sas_config_get_pcie_device_pg2(ioc, &mpi_reply,
&pcie_device_pg2, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE,
handle)) {
ioc_err(ioc, "failure at %s:%d/%s()!\n", __FILE__,
__LINE__, __func__); return 0;
}
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,
ioc_info(ioc, "handle(0x%04x) device not found: convert event to a device add\n",
handle));
event_data->PortEntry[i].PortStatus &= 0xF0;
event_data->PortEntry[i].PortStatus |=
MPI26_EVENT_PCIE_TOPO_PS_DEV_ADDED;
fallthrough; 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;
}
}
}
if (ioc->broadcast_aen_pending) {
dewtprintk(ioc,
ioc_info(ioc, "%s: loop back due to pending AEN\n",
__func__));
ioc->broadcast_aen_pending = 0; goto broadcast_aen_retry;
}
if (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED &&
!ioc->sas_hba.num_phys) { if (disable_discovery > 0 && ioc->shost_recovery) { /* Wait for the reset to complete */ while (ioc->shost_recovery)
ssleep(1);
}
_scsih_sas_host_add(ioc);
}
}
switch (event_data->ReasonCode) { case MPI25_EVENT_SAS_DISC_ERR_SMP_FAILED:
ioc_warn(ioc, "SMP command sent to the expander (handle:0x%04x, sas_address:0x%016llx, physical_port:0x%02x) has failed\n",
le16_to_cpu(event_data->DevHandle),
(u64)le64_to_cpu(event_data->SASAddress),
event_data->PhysicalPort); break; case MPI25_EVENT_SAS_DISC_ERR_SMP_TIMEOUT:
ioc_warn(ioc, "SMP command sent to the expander (handle:0x%04x, sas_address:0x%016llx, physical_port:0x%02x) has timed out\n",
le16_to_cpu(event_data->DevHandle),
(u64)le64_to_cpu(event_data->SASAddress),
event_data->PhysicalPort); break; default: break;
}
}
element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; if (ioc->shost_recovery &&
ioc->hba_mpi_version_belonged != MPI2_VERSION) { for (i = 0; i < event_data->NumElements; i++, element++) { if (element->ReasonCode == MPI2_EVENT_IR_CHANGE_RC_HIDE)
_scsih_ir_fastpath(ioc,
le16_to_cpu(element->PhysDiskDevHandle),
element->PhysDiskNum);
} return;
}
for (i = 0; i < event_data->NumElements; i++, element++) {
switch (element->ReasonCode) { case MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED: case MPI2_EVENT_IR_CHANGE_RC_ADDED: if (!foreign_config)
_scsih_sas_volume_add(ioc, element); break; case MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED: case MPI2_EVENT_IR_CHANGE_RC_REMOVED: if (!foreign_config)
_scsih_sas_volume_delete(ioc,
le16_to_cpu(element->VolDevHandle)); break; case MPI2_EVENT_IR_CHANGE_RC_PD_CREATED: if (!ioc->is_warpdrive)
_scsih_sas_pd_hide(ioc, element); break; case MPI2_EVENT_IR_CHANGE_RC_PD_DELETED: if (!ioc->is_warpdrive)
_scsih_sas_pd_expose(ioc, element); break; case MPI2_EVENT_IR_CHANGE_RC_HIDE: if (!ioc->is_warpdrive)
_scsih_sas_pd_add(ioc, element); break; case MPI2_EVENT_IR_CHANGE_RC_UNHIDE: if (!ioc->is_warpdrive)
_scsih_sas_pd_delete(ioc, element); break;
}
}
}
switch (state) { case MPI2_RAID_PD_STATE_ONLINE: case MPI2_RAID_PD_STATE_DEGRADED: case MPI2_RAID_PD_STATE_REBUILDING: case MPI2_RAID_PD_STATE_OPTIMAL: case MPI2_RAID_PD_STATE_HOT_SPARE:
if (!ioc->is_warpdrive)
set_bit(handle, ioc->pd_handles);
sas_device = mpt3sas_get_sdev_by_handle(ioc, handle); if (sas_device) {
sas_device_put(sas_device); return;
}
if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply,
&sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE,
handle))) {
ioc_err(ioc, "failure at %s:%d/%s()!\n",
__FILE__, __LINE__, __func__); return;
}
ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
MPI2_IOCSTATUS_MASK; if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
ioc_err(ioc, "failure at %s:%d/%s()!\n",
__FILE__, __LINE__, __func__); return;
}
/** * _scsih_prep_device_scan - initialize parameters prior to device scan * @ioc: per adapter object * * Set the deleted flag prior to device scan. If the device is found during * the scan, then we clear the deleted flag.
*/ staticvoid
_scsih_prep_device_scan(struct MPT3SAS_ADAPTER *ioc)
{ struct MPT3SAS_DEVICE *sas_device_priv_data; struct scsi_device *sdev;
/* * WARPDRIVE: The handles of the PDs might have changed * across the host reset so re-initialize the * required data for Direct IO
*/
mpt3sas_init_warpdrive_properties(ioc, raid_device);
spin_lock_irqsave(&ioc->raid_device_lock, flags); if (raid_device->handle == handle) {
spin_unlock_irqrestore(&ioc->raid_device_lock,
flags); return;
}
pr_info("\thandle changed from(0x%04x)!!!\n",
raid_device->handle);
raid_device->handle = handle; if (sas_target_priv_data)
sas_target_priv_data->handle = handle;
spin_unlock_irqrestore(&ioc->raid_device_lock, flags); return;
}
}
spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
}
/** * _scsih_search_responding_raid_devices - * @ioc: per adapter object * * After host reset, find out whether devices are still responding. * If not remove.
*/ staticvoid
_scsih_search_responding_raid_devices(struct MPT3SAS_ADAPTER *ioc)
{
Mpi2RaidVolPage1_t volume_pg1;
Mpi2RaidVolPage0_t volume_pg0;
Mpi2RaidPhysDiskPage0_t pd_pg0;
Mpi2ConfigReply_t mpi_reply;
u16 ioc_status;
u16 handle;
u8 phys_disk_num;
if (!ioc->ir_firmware) return;
ioc_info(ioc, "search for raid volumes: start\n");
/* removing unresponding end devices */
ioc_info(ioc, "removing unresponding devices: end-devices\n"); /* * Iterate, pulling off devices marked as non-responding. We become the * owner for the reference the list had on any object we prune.
*/
spin_lock_irqsave(&ioc->sas_device_lock, flags);
/* * Clean up the sas_device_init_list list as * driver goes for fresh scan as part of diag reset.
*/
list_for_each_entry_safe(sas_device, sas_device_next,
&ioc->sas_device_init_list, list) {
list_del_init(&sas_device->list);
sas_device_put(sas_device);
}
/* * Now, uninitialize and remove the unresponding devices we pruned.
*/
list_for_each_entry_safe(sas_device, sas_device_next, &head, list) {
_scsih_remove_device(ioc, sas_device);
list_del_init(&sas_device->list);
sas_device_put(sas_device);
}
ioc_info(ioc, "Removing unresponding devices: pcie end-devices\n");
INIT_LIST_HEAD(&head);
spin_lock_irqsave(&ioc->pcie_device_lock, flags); /* * Clean up the pcie_device_init_list list as * driver goes for fresh scan as part of diag reset.
*/
list_for_each_entry_safe(pcie_device, pcie_device_next,
&ioc->pcie_device_init_list, list) {
list_del_init(&pcie_device->list);
pcie_device_put(pcie_device);
}
for (i = 0 ; i < sas_expander->num_phys ; i++) { if ((mpt3sas_config_get_expander_pg1(ioc, &mpi_reply,
&expander_pg1, i, handle))) {
ioc_err(ioc, "failure at %s:%d/%s()!\n",
__FILE__, __LINE__, __func__); return;
}
mpt3sas_transport_update_links(ioc, sas_expander->sas_address,
le16_to_cpu(expander_pg1.AttachedDevHandle), i,
expander_pg1.NegotiatedLinkRate >> 4,
sas_expander->port);
}
}
/* the queue is being flushed so ignore this event */ if (ioc->remove_host || ioc->pci_error_recovery) {
fw_event_work_put(fw_event);
ioc->current_event = NULL; return;
}
switch (fw_event->event) { case MPT3SAS_PROCESS_TRIGGER_DIAG:
mpt3sas_process_trigger_data(ioc,
(struct SL_WH_TRIGGERS_EVENT_DATA_T *)
fw_event->event_data); break; case MPT3SAS_REMOVE_UNRESPONDING_DEVICES: while (scsi_host_in_recovery(ioc->shost) ||
ioc->shost_recovery) { /* * If we're unloading or cancelling the work, bail. * Otherwise, this can become an infinite loop.
*/ if (ioc->remove_host || ioc->fw_events_cleanup) goto out;
ssleep(1);
}
_scsih_remove_unresponding_devices(ioc);
_scsih_del_dirty_vphy(ioc);
_scsih_del_dirty_port_entries(ioc); if (ioc->is_gen35_ioc)
_scsih_update_device_qdepth(ioc);
_scsih_scan_for_devices_after_reset(ioc); /* * If diag reset has occurred during the driver load * then driver has to complete the driver load operation * by executing the following items: *- Register the devices from sas_device_init_list to SML *- clear is_driver_loading flag, *- start the watchdog thread. * In happy driver load path, above things are taken care of when * driver executes scsih_scan_finished().
*/ if (ioc->is_driver_loading)
_scsih_complete_devices_scanning(ioc);
_scsih_set_nvme_max_shutdown_latency(ioc); break; case MPT3SAS_PORT_ENABLE_COMPLETE:
ioc->start_scan = 0; if (missing_delay[0] != -1 && missing_delay[1] != -1)
mpt3sas_base_update_missing_delay(ioc, missing_delay[0],
missing_delay[1]);
dewtprintk(ioc,
ioc_info(ioc, "port enable: complete from worker thread\n")); break; case MPT3SAS_TURN_ON_PFA_LED:
_scsih_turn_on_pfa_led(ioc, fw_event->device_handle); break; case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
_scsih_sas_topology_change_event(ioc, fw_event); break; case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE: if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
_scsih_sas_device_status_change_event_debug(ioc,
(Mpi2EventDataSasDeviceStatusChange_t *)
fw_event->event_data); break; case MPI2_EVENT_SAS_DISCOVERY:
_scsih_sas_discovery_event(ioc, fw_event); break; case MPI2_EVENT_SAS_DEVICE_DISCOVERY_ERROR:
_scsih_sas_device_discovery_error_event(ioc, fw_event); break; case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE:
_scsih_sas_broadcast_primitive_event(ioc, fw_event); break; case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE:
_scsih_sas_enclosure_dev_status_change_event(ioc,
fw_event); break; case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST:
_scsih_sas_ir_config_change_event(ioc, fw_event); break; case MPI2_EVENT_IR_VOLUME:
_scsih_sas_ir_volume_event(ioc, fw_event); break; case MPI2_EVENT_IR_PHYSICAL_DISK:
_scsih_sas_ir_physical_disk_event(ioc, fw_event); break; 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); break;
}
out:
fw_event_work_put(fw_event);
ioc->current_event = NULL;
}
/** * _firmware_event_work * @work: The fw_event_work object * Context: user. * * wrappers for the work thread handling firmware events
*/
/** * mpt3sas_scsih_event_callback - firmware event handler (called at ISR time) * @ioc: per adapter object * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * Context: interrupt. * * This function merely adds a new work task into ioc->firmware_event_thread. * The tasks are worked from _firmware_event_work in user context. * * Return: 1 meaning mf should be freed from _base_interrupt * 0 means the mf is freed from this function.
*/
u8
mpt3sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index,
u32 reply)
{ struct fw_event_work *fw_event;
Mpi2EventNotificationReply_t *mpi_reply;
u16 event;
u16 sz;
Mpi26EventDataActiveCableExcept_t *ActiveCableEventData;
/* events turned off due to host reset */ if (ioc->pci_error_recovery) return 1;
case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
_scsih_check_topo_delete_events(ioc,
(Mpi2EventDataSasTopologyChangeList_t *)
mpi_reply->EventData); /* * No need to add the topology change list * event to fw event work queue when * diag reset is going on. Since during diag * reset driver scan the devices by reading * sas device page0's not by processing the * events.
*/ if (ioc->shost_recovery) return 1; break; case MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST:
_scsih_check_pcie_topo_remove_events(ioc,
(Mpi26EventDataPCIeTopologyChangeList_t *)
mpi_reply->EventData); if (ioc->shost_recovery) return 1; break; case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST:
_scsih_check_ir_config_unhide_events(ioc,
(Mpi2EventDataIrConfigChangeList_t *)
mpi_reply->EventData); break; case MPI2_EVENT_IR_VOLUME:
_scsih_check_volume_delete_events(ioc,
(Mpi2EventDataIrVolume_t *)
mpi_reply->EventData); break; case MPI2_EVENT_LOG_ENTRY_ADDED:
{
Mpi2EventDataLogEntryAdded_t *log_entry;
u32 log_code;
if (le16_to_cpu(log_entry->LogEntryQualifier)
!= MPT2_WARPDRIVE_LOGENTRY) break;
switch (log_code) { case MPT2_WARPDRIVE_LC_SSDT:
ioc_warn(ioc, "WarpDrive Warning: IO Throttling has occurred in the WarpDrive subsystem. Check WarpDrive documentation for additional details.\n"); break; case MPT2_WARPDRIVE_LC_SSDLW:
ioc_warn(ioc, "WarpDrive Warning: Program/Erase Cycles for the WarpDrive subsystem in degraded range. Check WarpDrive documentation for additional details.\n"); break; case MPT2_WARPDRIVE_LC_SSDLF:
ioc_err(ioc, "WarpDrive Fatal Error: There are no Program/Erase Cycles for the WarpDrive subsystem. The storage device will be in read-only mode. Check WarpDrive documentation for additional details.\n"); break; case MPT2_WARPDRIVE_LC_BRMF:
ioc_err(ioc, "WarpDrive Fatal Error: The Backup Rail Monitor has failed on the WarpDrive subsystem. Check WarpDrive documentation for additional details.\n"); break;
}
break;
} case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE:
_scsih_sas_device_status_change_event(ioc,
(Mpi2EventDataSasDeviceStatusChange_t *)
mpi_reply->EventData); break; case MPI2_EVENT_IR_OPERATION_STATUS: case MPI2_EVENT_SAS_DISCOVERY: case MPI2_EVENT_SAS_DEVICE_DISCOVERY_ERROR: 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:
_scsih_temp_threshold_events(ioc,
(Mpi2EventDataTemperature_t *)
mpi_reply->EventData); break; case MPI2_EVENT_ACTIVE_CABLE_EXCEPTION:
ActiveCableEventData =
(Mpi26EventDataActiveCableExcept_t *) mpi_reply->EventData; switch (ActiveCableEventData->ReasonCode) { case MPI26_EVENT_ACTIVE_CABLE_INSUFFICIENT_POWER:
ioc_notice(ioc, "Currently an active cable with ReceptacleID %d\n",
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",
le32_to_cpu(
ActiveCableEventData->ActiveCablePowerRequirement)); break;
case MPI26_EVENT_ACTIVE_CABLE_DEGRADED:
ioc_notice(ioc, "Currently a cable with ReceptacleID %d\n",
ActiveCableEventData->ReceptacleID);
pr_notice( "is not running at optimal speed(12 Gb/s rate)\n"); break;
}
/** * _scsih_nvme_shutdown - NVMe shutdown notification * @ioc: per adapter object * * Sending IoUnitControl request with shutdown operation code to alert IOC that * the host system is shutting down so that IOC can issue NVMe shutdown to * NVMe drives attached to it.
*/ staticvoid
_scsih_nvme_shutdown(struct MPT3SAS_ADAPTER *ioc)
{
Mpi26IoUnitControlRequest_t *mpi_request;
Mpi26IoUnitControlReply_t *mpi_reply;
u16 smid;
/* are there any NVMe devices ? */ if (list_empty(&ioc->pcie_device_list)) return;
mutex_lock(&ioc->scsih_cmds.mutex);
if (ioc->scsih_cmds.status != MPT3_CMD_NOT_USED) {
ioc_err(ioc, "%s: scsih_cmd in use\n", __func__); goto out;
}
ioc->scsih_cmds.status = MPT3_CMD_PENDING;
smid = mpt3sas_base_get_smid(ioc, ioc->scsih_cb_idx); if (!smid) {
ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
ioc->scsih_cmds.status = MPT3_CMD_NOT_USED; goto out;
}
if (ioc->scsih_cmds.status & MPT3_CMD_REPLY_VALID) {
mpi_reply = ioc->scsih_cmds.reply;
ioc_info(ioc, "Io Unit Control shutdown (complete):" "ioc_status(0x%04x), loginfo(0x%08x)\n",
le16_to_cpu(mpi_reply->IOCStatus),
le32_to_cpu(mpi_reply->IOCLogInfo));
}
out:
ioc->scsih_cmds.status = MPT3_CMD_NOT_USED;
mutex_unlock(&ioc->scsih_cmds.mutex);
}
/** * _scsih_ir_shutdown - IR shutdown notification * @ioc: per adapter object * * Sending RAID Action to alert the Integrated RAID subsystem of the IOC that * the host system is shutting down.
*/ staticvoid
_scsih_ir_shutdown(struct MPT3SAS_ADAPTER *ioc)
{
Mpi2RaidActionRequest_t *mpi_request;
Mpi2RaidActionReply_t *mpi_reply;
u16 smid;
/* is IR firmware build loaded ? */ if (!ioc->ir_firmware) return;
/* are there any volumes ? */ if (list_empty(&ioc->raid_device_list)) return;
mutex_lock(&ioc->scsih_cmds.mutex);
if (ioc->scsih_cmds.status != MPT3_CMD_NOT_USED) {
ioc_err(ioc, "%s: scsih_cmd in use\n", __func__); goto out;
}
ioc->scsih_cmds.status = MPT3_CMD_PENDING;
smid = mpt3sas_base_get_smid(ioc, ioc->scsih_cb_idx); if (!smid) {
ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
ioc->scsih_cmds.status = MPT3_CMD_NOT_USED; goto out;
}
/** * _scsih_get_shost_and_ioc - get shost and ioc * and verify whether they are NULL or not * @pdev: PCI device struct * @shost: address of scsi host pointer * @ioc: address of HBA adapter pointer * * Return zero if *shost and *ioc are not NULL otherwise return error number.
*/ staticint
_scsih_get_shost_and_ioc(struct pci_dev *pdev, struct Scsi_Host **shost, struct MPT3SAS_ADAPTER **ioc)
{
*shost = pci_get_drvdata(pdev); if (*shost == NULL) {
dev_err(&pdev->dev, "pdev's driver data is null\n"); return -ENXIO;
}
*ioc = shost_priv(*shost); if (*ioc == NULL) {
dev_err(&pdev->dev, "shost's private data is null\n"); return -ENXIO;
}
if (_scsih_get_shost_and_ioc(pdev, &shost, &ioc)) return;
ioc->remove_host = 1;
if (!pci_device_is_present(pdev)) {
mpt3sas_base_pause_mq_polling(ioc);
_scsih_flush_running_cmds(ioc);
}
_scsih_fw_event_cleanup_queue(ioc);
spin_lock_irqsave(&ioc->fw_event_lock, flags);
wq = ioc->firmware_event_thread;
ioc->firmware_event_thread = NULL;
spin_unlock_irqrestore(&ioc->fw_event_lock, flags); if (wq)
destroy_workqueue(wq); /* * Copy back the unmodified ioc page1 so that on next driver load, * current modified changes on ioc page1 won't take effect.
*/ if (ioc->is_aero_ioc)
mpt3sas_config_set_ioc_pg1(ioc, &mpi_reply,
&ioc->ioc_pg1_copy);
if (channel == RAID_CHANNEL) {
raid_device = device; /* * If this boot vd is already registered with SML then * no need to register it again as part of device scanning * after diag reset during driver load operation.
*/ if (raid_device->starget) return;
rc = scsi_add_device(ioc->shost, RAID_CHANNEL,
raid_device->id, 0); if (rc)
_scsih_raid_device_remove(ioc, raid_device);
} elseif (channel == PCIE_CHANNEL) {
pcie_device = device; /* * If this boot NVMe device is already registered with SML then * no need to register it again as part of device scanning * after diag reset during driver load operation.
*/ if (pcie_device->starget) return;
spin_lock_irqsave(&ioc->pcie_device_lock, flags);
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 {
sas_device = device; /* * If this boot sas/sata device is already registered with SML * then no need to register it again as part of device scanning * after diag reset during driver load operation.
*/ if (sas_device->starget) return;
spin_lock_irqsave(&ioc->sas_device_lock, flags);
handle = sas_device->handle;
sas_address_parent = sas_device->sas_address_parent;
sas_address = sas_device->sas_address;
port = sas_device->port;
list_move_tail(&sas_device->list, &ioc->sas_device_list);
spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
/** * _scsih_probe_raid - reporting raid volumes to scsi-ml * @ioc: per adapter object * * Called during initial loading of the driver.
*/ staticvoid
_scsih_probe_raid(struct MPT3SAS_ADAPTER *ioc)
{ struct _raid_device *raid_device, *raid_next; int rc;
list_for_each_entry_safe(raid_device, raid_next,
&ioc->raid_device_list, list) { if (raid_device->starget) continue;
rc = scsi_add_device(ioc->shost, RAID_CHANNEL,
raid_device->id, 0); if (rc)
_scsih_raid_device_remove(ioc, raid_device);
}
}
/* * Since we dropped the lock during the call to port_add(), we need to * be careful here that somebody else didn't move or delete this item * while we were busy with other things. * * If it was on the list, we need a put() for the reference the list * had. Either way, we need a get() for the destination list.
*/ if (!list_empty(&sas_device->list)) {
list_del_init(&sas_device->list);
sas_device_put(sas_device);
}
/** * _scsih_probe_sas - reporting sas devices to sas transport * @ioc: per adapter object * * Called during initial loading of the driver.
*/ staticvoid
_scsih_probe_sas(struct MPT3SAS_ADAPTER *ioc)
{ struct _sas_device *sas_device;
if (ioc->hide_drives) return;
while ((sas_device = get_next_sas_device(ioc))) { if (!mpt3sas_transport_port_add(ioc, sas_device->handle,
sas_device->sas_address_parent, sas_device->port)) {
_scsih_sas_device_remove(ioc, sas_device);
sas_device_put(sas_device); continue;
} elseif (!sas_device->starget) { /* * When asyn 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) {
mpt3sas_transport_port_remove(ioc,
sas_device->sas_address,
sas_device->sas_address_parent,
sas_device->port);
_scsih_sas_device_remove(ioc, sas_device);
sas_device_put(sas_device); continue;
}
}
sas_device_make_active(ioc, sas_device);
sas_device_put(sas_device);
}
}
/** * get_next_pcie_device - Get the next pcie device * @ioc: per adapter object * * Get the next pcie device from pcie_device_init_list list. * * Return: pcie device structure if pcie_device_init_list list is not empty * otherwise returns NULL
*/ staticstruct _pcie_device *get_next_pcie_device(struct MPT3SAS_ADAPTER *ioc)
{ struct _pcie_device *pcie_device = NULL; unsignedlong flags;
/** * 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
*/ staticvoid pcie_device_make_active(struct MPT3SAS_ADAPTER *ioc, struct _pcie_device *pcie_device)
{ unsignedlong 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);
/** * _scsih_probe_pcie - reporting PCIe devices to scsi-ml * @ioc: per adapter object * * Called during initial loading of the driver.
*/ staticvoid
_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;
} if (pcie_device->access_status ==
MPI26_PCIEDEV0_ASTATUS_DEVICE_BLOCKED) {
pcie_device_make_active(ioc, pcie_device);
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;
} elseif (!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 * * Called during initial loading of the driver.
*/ staticvoid
_scsih_probe_devices(struct MPT3SAS_ADAPTER *ioc)
{
u16 volume_mapping_flags;
if (!(ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR)) return; /* return when IOC doesn't support initiator mode */
/** * scsih_scan_start - scsi lld callback for .scan_start * @shost: SCSI host pointer * * The shost has the ability to discover targets on its own instead * of scanning the entire bus. In our implemention, we will kick off * firmware discovery.
*/ staticvoid
scsih_scan_start(struct Scsi_Host *shost)
{ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); int rc; if (diag_buffer_enable != -1 && diag_buffer_enable != 0)
mpt3sas_enable_diag_buffer(ioc, diag_buffer_enable); elseif (ioc->manu_pg11.HostTraceBufferMaxSizeKB != 0)
mpt3sas_enable_diag_buffer(ioc, 1);
/** * scsih_scan_finished - scsi lld callback for .scan_finished * @shost: SCSI host pointer * @time: elapsed time of the scan in jiffies * * This function will be called periodicallyn until it returns 1 with the * scsi_host and the elapsed time of the scan in jiffies. In our implemention, * we wait for firmware discovery to complete, then return 1.
*/ staticint
scsih_scan_finished(struct Scsi_Host *shost, unsignedlong time)
{ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
u32 ioc_state; int issue_hard_reset = 0;
out: if (issue_hard_reset) {
ioc->port_enable_cmds.status = MPT3_CMD_NOT_USED; if (mpt3sas_base_hard_reset_handler(ioc, SOFT_RESET))
ioc->is_driver_loading = 0;
} return 1;
}
/** * scsih_map_queues - map reply queues with request queues * @shost: SCSI host pointer
*/ staticvoid scsih_map_queues(struct Scsi_Host *shost)
{ struct MPT3SAS_ADAPTER *ioc =
(struct MPT3SAS_ADAPTER *)shost->hostdata; struct blk_mq_queue_map *map; int i, qoff, offset; int nr_msix_vectors = ioc->iopoll_q_start_index; int iopoll_q_count = ioc->reply_queue_count - nr_msix_vectors;
if (shost->nr_hw_queues == 1) return;
for (i = 0, qoff = 0; i < shost->nr_maps; i++) {
map = &shost->tag_set.map[i];
map->nr_queues = 0;
offset = 0; if (i == HCTX_TYPE_DEFAULT) {
map->nr_queues =
nr_msix_vectors - ioc->high_iops_queues;
offset = ioc->high_iops_queues;
} elseif (i == HCTX_TYPE_POLL)
map->nr_queues = iopoll_q_count;
if (!map->nr_queues)
BUG_ON(i == HCTX_TYPE_DEFAULT);
/* * The poll queue(s) doesn't have an IRQ (and hence IRQ * affinity), so use the regular blk-mq cpu mapping
*/
map->queue_offset = qoff; if (i != HCTX_TYPE_POLL)
blk_mq_map_hw_queues(map, &ioc->pdev->dev, offset); else
blk_mq_map_queues(map);
/* raid transport support for SAS 3.0 HBA devices */ staticstruct raid_function_template mpt3sas_raid_functions = {
.cookie = &mpt3sas_driver_template,
.is_raid = scsih_is_raid,
.get_resync = scsih_get_resync,
.get_state = scsih_get_state,
};
/** * _scsih_determine_hba_mpi_version - determine in which MPI version class * this device belongs to. * @pdev: PCI device struct * * return MPI2_VERSION for SAS 2.0 HBA devices, * MPI25_VERSION for SAS 3.0 HBA devices, and * MPI26 VERSION for Cutlass & Invader SAS 3.0 HBA devices
*/ static u16
_scsih_determine_hba_mpi_version(struct pci_dev *pdev)
{
switch (pdev->device) { case MPI2_MFGPAGE_DEVID_SSS6200: case MPI2_MFGPAGE_DEVID_SAS2004: case MPI2_MFGPAGE_DEVID_SAS2008: case MPI2_MFGPAGE_DEVID_SAS2108_1: case MPI2_MFGPAGE_DEVID_SAS2108_2: case MPI2_MFGPAGE_DEVID_SAS2108_3: case MPI2_MFGPAGE_DEVID_SAS2116_1: case MPI2_MFGPAGE_DEVID_SAS2116_2: case MPI2_MFGPAGE_DEVID_SAS2208_1: case MPI2_MFGPAGE_DEVID_SAS2208_2: case MPI2_MFGPAGE_DEVID_SAS2208_3: case MPI2_MFGPAGE_DEVID_SAS2208_4: case MPI2_MFGPAGE_DEVID_SAS2208_5: case MPI2_MFGPAGE_DEVID_SAS2208_6: case MPI2_MFGPAGE_DEVID_SAS2308_1: case MPI2_MFGPAGE_DEVID_SAS2308_2: case MPI2_MFGPAGE_DEVID_SAS2308_3: case MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP: case MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP_1: return MPI2_VERSION; case MPI25_MFGPAGE_DEVID_SAS3004: case MPI25_MFGPAGE_DEVID_SAS3008: case MPI25_MFGPAGE_DEVID_SAS3108_1: case MPI25_MFGPAGE_DEVID_SAS3108_2: case MPI25_MFGPAGE_DEVID_SAS3108_5: case MPI25_MFGPAGE_DEVID_SAS3108_6: return MPI25_VERSION; case MPI26_MFGPAGE_DEVID_SAS3216: case MPI26_MFGPAGE_DEVID_SAS3224: case MPI26_MFGPAGE_DEVID_SAS3316_1: case MPI26_MFGPAGE_DEVID_SAS3316_2: case MPI26_MFGPAGE_DEVID_SAS3316_3: case MPI26_MFGPAGE_DEVID_SAS3316_4: case MPI26_MFGPAGE_DEVID_SAS3324_1: case MPI26_MFGPAGE_DEVID_SAS3324_2: case MPI26_MFGPAGE_DEVID_SAS3324_3: case MPI26_MFGPAGE_DEVID_SAS3324_4: case MPI26_MFGPAGE_DEVID_SAS3508: case MPI26_MFGPAGE_DEVID_SAS3508_1: case MPI26_MFGPAGE_DEVID_SAS3408: case MPI26_MFGPAGE_DEVID_SAS3516: case MPI26_MFGPAGE_DEVID_SAS3516_1: case MPI26_MFGPAGE_DEVID_SAS3416: case MPI26_MFGPAGE_DEVID_SAS3616: case MPI26_ATLAS_PCIe_SWITCH_DEVID: case MPI26_MFGPAGE_DEVID_CFG_SEC_3916: case MPI26_MFGPAGE_DEVID_HARD_SEC_3916: case MPI26_MFGPAGE_DEVID_CFG_SEC_3816: case MPI26_MFGPAGE_DEVID_HARD_SEC_3816: case MPI26_MFGPAGE_DEVID_INVALID0_3916: case MPI26_MFGPAGE_DEVID_INVALID1_3916: case MPI26_MFGPAGE_DEVID_INVALID0_3816: case MPI26_MFGPAGE_DEVID_INVALID1_3816: return MPI26_VERSION;
} return 0;
}
/* Determine in which MPI version class this pci device belongs */
hba_mpi_version = _scsih_determine_hba_mpi_version(pdev); if (hba_mpi_version == 0) return -ENODEV;
/* Enumerate only SAS 2.0 HBA's if hbas_to_enumerate is one, * for other generation HBA's return with -ENODEV
*/ if ((hbas_to_enumerate == 1) && (hba_mpi_version != MPI2_VERSION)) return -ENODEV;
/* Enumerate only SAS 3.0 HBA's if hbas_to_enumerate is two, * for other generation HBA's return with -ENODEV
*/ if ((hbas_to_enumerate == 2) && (!(hba_mpi_version == MPI25_VERSION
|| hba_mpi_version == MPI26_VERSION))) return -ENODEV;
switch (hba_mpi_version) { case MPI2_VERSION:
pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S |
PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_CLKPM); /* Use mpt2sas driver host template for SAS 2.0 HBA's */
shost = scsi_host_alloc(&mpt2sas_driver_template, sizeof(struct MPT3SAS_ADAPTER)); if (!shost) return -ENODEV;
ioc = shost_priv(shost);
memset(ioc, 0, sizeof(struct MPT3SAS_ADAPTER));
ioc->hba_mpi_version_belonged = hba_mpi_version;
ioc->id = mpt2_ids++;
sprintf(ioc->driver_name, "%s", MPT2SAS_DRIVER_NAME); switch (pdev->device) { case MPI2_MFGPAGE_DEVID_SSS6200:
ioc->is_warpdrive = 1;
ioc->hide_ir_msg = 1; break; case MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP: case MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP_1:
ioc->is_mcpu_endpoint = 1; break; default:
ioc->mfg_pg10_hide_flag = MFG_PAGE10_EXPOSE_ALL_DISKS; break;
}
break; case MPI25_VERSION: case MPI26_VERSION: /* Use mpt3sas driver host template for SAS 3.0 HBA's */
shost = scsi_host_alloc(&mpt3sas_driver_template, sizeof(struct MPT3SAS_ADAPTER)); if (!shost) return -ENODEV;
ioc = shost_priv(shost);
memset(ioc, 0, sizeof(struct MPT3SAS_ADAPTER));
ioc->hba_mpi_version_belonged = hba_mpi_version;
ioc->id = mpt3_ids++;
sprintf(ioc->driver_name, "%s", MPT3SAS_DRIVER_NAME); switch (pdev->device) { case MPI26_MFGPAGE_DEVID_SAS3508: case MPI26_MFGPAGE_DEVID_SAS3508_1: case MPI26_MFGPAGE_DEVID_SAS3408: case MPI26_MFGPAGE_DEVID_SAS3516: case MPI26_MFGPAGE_DEVID_SAS3516_1: case MPI26_MFGPAGE_DEVID_SAS3416: case MPI26_MFGPAGE_DEVID_SAS3616: case MPI26_ATLAS_PCIe_SWITCH_DEVID:
ioc->is_gen35_ioc = 1; break; case MPI26_MFGPAGE_DEVID_INVALID0_3816: case MPI26_MFGPAGE_DEVID_INVALID0_3916:
dev_err(&pdev->dev, "HBA with DeviceId 0x%04x, sub VendorId 0x%04x, sub DeviceId 0x%04x is Invalid",
pdev->device, pdev->subsystem_vendor,
pdev->subsystem_device); return 1; case MPI26_MFGPAGE_DEVID_INVALID1_3816: case MPI26_MFGPAGE_DEVID_INVALID1_3916:
dev_err(&pdev->dev, "HBA with DeviceId 0x%04x, sub VendorId 0x%04x, sub DeviceId 0x%04x is Tampered",
pdev->device, pdev->subsystem_vendor,
pdev->subsystem_device); return 1; case MPI26_MFGPAGE_DEVID_CFG_SEC_3816: case MPI26_MFGPAGE_DEVID_CFG_SEC_3916:
dev_info(&pdev->dev, "HBA is in Configurable Secure mode\n");
fallthrough; case MPI26_MFGPAGE_DEVID_HARD_SEC_3816: case MPI26_MFGPAGE_DEVID_HARD_SEC_3916:
ioc->is_aero_ioc = ioc->is_gen35_ioc = 1; break; default:
ioc->is_gen35_ioc = ioc->is_aero_ioc = 0;
} if ((ioc->hba_mpi_version_belonged == MPI25_VERSION &&
pdev->revision >= SAS3_PCI_DEVICE_C0_REVISION) ||
(ioc->hba_mpi_version_belonged == MPI26_VERSION)) {
ioc->combined_reply_queue = 1; if (ioc->is_gen35_ioc)
ioc->combined_reply_index_count =
MPT3_SUP_REPLY_POST_HOST_INDEX_REG_COUNT_G35; else
ioc->combined_reply_index_count =
MPT3_SUP_REPLY_POST_HOST_INDEX_REG_COUNT_G3;
}
if (ioc->is_mcpu_endpoint) { /* mCPU MPI support 64K max IO */
shost->max_sectors = 128;
ioc_info(ioc, "The max_sectors value is set to %d\n",
shost->max_sectors);
} else { if (max_sectors != 0xFFFF) { if (max_sectors < 64) {
shost->max_sectors = 64;
ioc_warn(ioc, "Invalid value %d passed for max_sectors, range is 64 to 32767. Assigning value of 64.\n",
max_sectors);
} elseif (max_sectors > 32767) {
shost->max_sectors = 32767;
ioc_warn(ioc, "Invalid value %d passed for max_sectors, range is 64 to 32767.Assigning default value of 32767.\n",
max_sectors);
} else {
shost->max_sectors = max_sectors & 0xFFFE;
ioc_info(ioc, "The max_sectors value is set to %d\n",
shost->max_sectors);
}
}
} /* register EEDP capabilities with SCSI layer */ if (prot_mask >= 0)
scsi_host_set_prot(shost, (prot_mask & 0x07)); else
scsi_host_set_prot(shost, SHOST_DIF_TYPE1_PROTECTION
| SHOST_DIF_TYPE2_PROTECTION
| SHOST_DIF_TYPE3_PROTECTION);
r = _scsih_get_shost_and_ioc(pdev, &shost, &ioc); if (r) return r;
ioc_info(ioc, "pdev=0x%p, slot=%s, previous operating state [D%d]\n",
pdev, pci_name(pdev), device_state);
ioc->pdev = pdev;
r = mpt3sas_base_map_resources(ioc); if (r) return r;
ioc_info(ioc, "Issuing Hard Reset as part of OS Resume\n");
mpt3sas_base_hard_reset_handler(ioc, SOFT_RESET);
scsi_unblock_requests(shost);
mpt3sas_base_start_watchdog(ioc); return 0;
}
/** * scsih_pci_error_detected - Called when a PCI error is detected. * @pdev: PCI device struct * @state: PCI channel state * * Description: Called when a PCI error is detected. * * Return: PCI_ERS_RESULT_NEED_RESET or PCI_ERS_RESULT_DISCONNECT.
*/ static pci_ers_result_t
scsih_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
{ struct Scsi_Host *shost; struct MPT3SAS_ADAPTER *ioc;
if (_scsih_get_shost_and_ioc(pdev, &shost, &ioc)) return PCI_ERS_RESULT_DISCONNECT;
switch (state) { case pci_channel_io_normal: return PCI_ERS_RESULT_CAN_RECOVER; case pci_channel_io_frozen: /* Fatal error, prepare for slot reset */
ioc->pci_error_recovery = 1;
scsi_block_requests(ioc->shost);
mpt3sas_base_stop_watchdog(ioc);
mpt3sas_base_free_resources(ioc); return PCI_ERS_RESULT_NEED_RESET; case pci_channel_io_perm_failure: /* Permanent error, prepare for device removal */
ioc->pci_error_recovery = 1;
mpt3sas_base_stop_watchdog(ioc);
mpt3sas_base_pause_mq_polling(ioc);
_scsih_flush_running_cmds(ioc); return PCI_ERS_RESULT_DISCONNECT;
} return PCI_ERS_RESULT_NEED_RESET;
}
/** * scsih_pci_slot_reset - Called when PCI slot has been reset. * @pdev: PCI device struct * * Description: This routine is called by the pci error recovery * code after the PCI slot has been reset, just before we * should resume normal operations.
*/ static pci_ers_result_t
scsih_pci_slot_reset(struct pci_dev *pdev)
{ struct Scsi_Host *shost; struct MPT3SAS_ADAPTER *ioc; int rc;
if (_scsih_get_shost_and_ioc(pdev, &shost, &ioc)) return PCI_ERS_RESULT_DISCONNECT;
if (!rc) return PCI_ERS_RESULT_RECOVERED; else return PCI_ERS_RESULT_DISCONNECT;
}
/** * scsih_pci_resume() - resume normal ops after PCI reset * @pdev: pointer to PCI device * * Called when the error recovery driver tells us that its * OK to resume normal operation. Use completion to allow * halted scsi ops to resume.
*/ staticvoid
scsih_pci_resume(struct pci_dev *pdev)
{ struct Scsi_Host *shost; struct MPT3SAS_ADAPTER *ioc;
if (_scsih_get_shost_and_ioc(pdev, &shost, &ioc)) return;
/* This called only if scsih_pci_error_detected returns * PCI_ERS_RESULT_CAN_RECOVER. Read/write to the device still * works, no need to reset slot.
*/ return PCI_ERS_RESULT_RECOVERED;
}
/* raid transport support */ if (hbas_to_enumerate != 1)
raid_class_release(mpt3sas_raid_template); if (hbas_to_enumerate != 2)
raid_class_release(mpt2sas_raid_template);
sas_release_transport(mpt3sas_transport_template);
mpt3sas_exit_debugfs();
}
/** * _mpt3sas_init - main entry point for this driver. * * Return: 0 success, anything else error.
*/ staticint __init
_mpt3sas_init(void)
{ int error;
pr_info("%s version %s loaded\n", MPT3SAS_DRIVER_NAME,
MPT3SAS_DRIVER_VERSION);
mpt3sas_transport_template =
sas_attach_transport(&mpt3sas_transport_functions); if (!mpt3sas_transport_template) return -ENODEV;
/* No need attach mpt3sas raid functions template * if hbas_to_enumarate value is one.
*/ if (hbas_to_enumerate != 1) {
mpt3sas_raid_template =
raid_class_attach(&mpt3sas_raid_functions); if (!mpt3sas_raid_template) {
sas_release_transport(mpt3sas_transport_template); return -ENODEV;
}
}
/* No need to attach mpt2sas raid functions template * if hbas_to_enumarate value is two
*/ if (hbas_to_enumerate != 2) {
mpt2sas_raid_template =
raid_class_attach(&mpt2sas_raid_functions); if (!mpt2sas_raid_template) {
sas_release_transport(mpt3sas_transport_template); return -ENODEV;
}
}
error = scsih_init(); if (error) {
scsih_exit(); return error;
}
mpt3sas_ctl_init(hbas_to_enumerate);
error = pci_register_driver(&mpt3sas_driver); if (error) {
mpt3sas_ctl_exit(hbas_to_enumerate);
scsih_exit();
}
return error;
}
/** * _mpt3sas_exit - exit point for this driver (when it is a module). *
*/ staticvoid __exit
_mpt3sas_exit(void)
{
pr_info("mpt3sas version %s unloading\n",
MPT3SAS_DRIVER_VERSION);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.