// SPDX-License-Identifier: GPL-2.0-or-later /* * Driver for Broadcom MPI3 Storage Controllers * * Copyright (C) 2017-2023 Broadcom Inc. * (mailto: [email protected]) * */ #include "mpi3mr.h" #include <linux/idr.h> /* global driver scop variables */ LIST_HEAD(…); DEFINE_SPINLOCK(…); static DEFINE_IDA(mrioc_ida); static int warn_non_secure_ctlr; atomic64_t event_counter; MODULE_AUTHOR(…); MODULE_DESCRIPTION(…); MODULE_LICENSE(…); MODULE_VERSION(…); /* Module parameters*/ int prot_mask = …; module_param(prot_mask, int, 0); MODULE_PARM_DESC(…) …; static int prot_guard_mask = …; module_param(prot_guard_mask, int, 0); MODULE_PARM_DESC(…) …; static int logging_level; module_param(logging_level, int, 0); MODULE_PARM_DESC(…) …; static int max_sgl_entries = …; module_param(max_sgl_entries, int, 0444); MODULE_PARM_DESC(…) …; /* Forward declarations*/ static void mpi3mr_send_event_ack(struct mpi3mr_ioc *mrioc, u8 event, struct mpi3mr_drv_cmd *cmdparam, u32 event_ctx); #define MPI3MR_DRIVER_EVENT_TG_QD_REDUCTION … #define MPI3_EVENT_WAIT_FOR_DEVICES_TO_REFRESH … /** * mpi3mr_host_tag_for_scmd - Get host tag for a scmd * @mrioc: Adapter instance reference * @scmd: SCSI command reference * * Calculate the host tag based on block tag for a given scmd. * * Return: Valid host tag or MPI3MR_HOSTTAG_INVALID. */ static u16 mpi3mr_host_tag_for_scmd(struct mpi3mr_ioc *mrioc, struct scsi_cmnd *scmd) { … } /** * mpi3mr_scmd_from_host_tag - Get SCSI command from host tag * @mrioc: Adapter instance reference * @host_tag: Host tag * @qidx: Operational queue index * * Identify the block tag from the host tag and queue index and * retrieve associated scsi command using scsi_host_find_tag(). * * Return: SCSI command reference or NULL. */ static struct scsi_cmnd *mpi3mr_scmd_from_host_tag( struct mpi3mr_ioc *mrioc, u16 host_tag, u16 qidx) { … } /** * mpi3mr_clear_scmd_priv - Cleanup SCSI command private date * @mrioc: Adapter instance reference * @scmd: SCSI command reference * * Invalidate the SCSI command private data to mark the command * is not in LLD scope anymore. * * Return: Nothing. */ static void mpi3mr_clear_scmd_priv(struct mpi3mr_ioc *mrioc, struct scsi_cmnd *scmd) { … } static void mpi3mr_dev_rmhs_send_tm(struct mpi3mr_ioc *mrioc, u16 handle, struct mpi3mr_drv_cmd *cmdparam, u8 iou_rc); static void mpi3mr_fwevt_worker(struct work_struct *work); /** * mpi3mr_fwevt_free - firmware event memory dealloctor * @r: k reference pointer of the firmware event * * Free firmware event memory when no reference. */ static void mpi3mr_fwevt_free(struct kref *r) { … } /** * mpi3mr_fwevt_get - k reference incrementor * @fwevt: Firmware event reference * * Increment firmware event reference count. */ static void mpi3mr_fwevt_get(struct mpi3mr_fwevt *fwevt) { … } /** * mpi3mr_fwevt_put - k reference decrementor * @fwevt: Firmware event reference * * decrement firmware event reference count. */ static void mpi3mr_fwevt_put(struct mpi3mr_fwevt *fwevt) { … } /** * mpi3mr_alloc_fwevt - Allocate firmware event * @len: length of firmware event data to allocate * * Allocate firmware event with required length and initialize * the reference counter. * * Return: firmware event reference. */ static struct mpi3mr_fwevt *mpi3mr_alloc_fwevt(int len) { … } /** * mpi3mr_fwevt_add_to_list - Add firmware event to the list * @mrioc: Adapter instance reference * @fwevt: Firmware event reference * * Add the given firmware event to the firmware event list. * * Return: Nothing. */ static void mpi3mr_fwevt_add_to_list(struct mpi3mr_ioc *mrioc, struct mpi3mr_fwevt *fwevt) { … } /** * mpi3mr_hdb_trigger_data_event - Add hdb trigger data event to * the list * @mrioc: Adapter instance reference * @event_data: Event data * * Add the given hdb trigger data event to the firmware event * list. * * Return: Nothing. */ void mpi3mr_hdb_trigger_data_event(struct mpi3mr_ioc *mrioc, struct trigger_event_data *event_data) { … } /** * mpi3mr_fwevt_del_from_list - Delete firmware event from list * @mrioc: Adapter instance reference * @fwevt: Firmware event reference * * Delete the given firmware event from the firmware event list. * * Return: Nothing. */ static void mpi3mr_fwevt_del_from_list(struct mpi3mr_ioc *mrioc, struct mpi3mr_fwevt *fwevt) { … } /** * mpi3mr_dequeue_fwevt - Dequeue firmware event from the list * @mrioc: Adapter instance reference * * Dequeue a firmware event from the firmware event list. * * Return: firmware event. */ static struct mpi3mr_fwevt *mpi3mr_dequeue_fwevt( struct mpi3mr_ioc *mrioc) { … } /** * mpi3mr_cancel_work - cancel firmware event * @fwevt: fwevt object which needs to be canceled * * Return: Nothing. */ static void mpi3mr_cancel_work(struct mpi3mr_fwevt *fwevt) { … } /** * mpi3mr_cleanup_fwevt_list - Cleanup firmware event list * @mrioc: Adapter instance reference * * Flush all pending firmware events from the firmware event * list. * * Return: Nothing. */ void mpi3mr_cleanup_fwevt_list(struct mpi3mr_ioc *mrioc) { … } /** * mpi3mr_queue_qd_reduction_event - Queue TG QD reduction event * @mrioc: Adapter instance reference * @tg: Throttle group information pointer * * Accessor to queue on synthetically generated driver event to * the event worker thread, the driver event will be used to * reduce the QD of all VDs in the TG from the worker thread. * * Return: None. */ static void mpi3mr_queue_qd_reduction_event(struct mpi3mr_ioc *mrioc, struct mpi3mr_throttle_group_info *tg) { … } /** * mpi3mr_invalidate_devhandles -Invalidate device handles * @mrioc: Adapter instance reference * * Invalidate the device handles in the target device structures * . Called post reset prior to reinitializing the controller. * * Return: Nothing. */ void mpi3mr_invalidate_devhandles(struct mpi3mr_ioc *mrioc) { … } /** * mpi3mr_print_scmd - print individual SCSI command * @rq: Block request * @data: Adapter instance reference * * Print the SCSI command details if it is in LLD scope. * * Return: true always. */ static bool mpi3mr_print_scmd(struct request *rq, void *data) { … } /** * mpi3mr_flush_scmd - Flush individual SCSI command * @rq: Block request * @data: Adapter instance reference * * Return the SCSI command to the upper layers if it is in LLD * scope. * * Return: true always. */ static bool mpi3mr_flush_scmd(struct request *rq, void *data) { … } /** * mpi3mr_count_dev_pending - Count commands pending for a lun * @rq: Block request * @data: SCSI device reference * * This is an iterator function called for each SCSI command in * a host and if the command is pending in the LLD for the * specific device(lun) then device specific pending I/O counter * is updated in the device structure. * * Return: true always. */ static bool mpi3mr_count_dev_pending(struct request *rq, void *data) { … } /** * mpi3mr_count_tgt_pending - Count commands pending for target * @rq: Block request * @data: SCSI target reference * * This is an iterator function called for each SCSI command in * a host and if the command is pending in the LLD for the * specific target then target specific pending I/O counter is * updated in the target structure. * * Return: true always. */ static bool mpi3mr_count_tgt_pending(struct request *rq, void *data) { … } /** * mpi3mr_flush_host_io - Flush host I/Os * @mrioc: Adapter instance reference * * Flush all of the pending I/Os by calling * blk_mq_tagset_busy_iter() for each possible tag. This is * executed post controller reset * * Return: Nothing. */ void mpi3mr_flush_host_io(struct mpi3mr_ioc *mrioc) { … } /** * mpi3mr_flush_cmds_for_unrecovered_controller - Flush all pending cmds * @mrioc: Adapter instance reference * * This function waits for currently running IO poll threads to * exit and then flushes all host I/Os and any internal pending * cmds. This is executed after controller is marked as * unrecoverable. * * Return: Nothing. */ void mpi3mr_flush_cmds_for_unrecovered_controller(struct mpi3mr_ioc *mrioc) { … } /** * mpi3mr_alloc_tgtdev - target device allocator * * Allocate target device instance and initialize the reference * count * * Return: target device instance. */ static struct mpi3mr_tgt_dev *mpi3mr_alloc_tgtdev(void) { … } /** * mpi3mr_tgtdev_add_to_list -Add tgtdevice to the list * @mrioc: Adapter instance reference * @tgtdev: Target device * * Add the target device to the target device list * * Return: Nothing. */ static void mpi3mr_tgtdev_add_to_list(struct mpi3mr_ioc *mrioc, struct mpi3mr_tgt_dev *tgtdev) { … } /** * mpi3mr_tgtdev_del_from_list -Delete tgtdevice from the list * @mrioc: Adapter instance reference * @tgtdev: Target device * @must_delete: Must delete the target device from the list irrespective * of the device state. * * Remove the target device from the target device list * * Return: Nothing. */ static void mpi3mr_tgtdev_del_from_list(struct mpi3mr_ioc *mrioc, struct mpi3mr_tgt_dev *tgtdev, bool must_delete) { … } /** * __mpi3mr_get_tgtdev_by_handle -Get tgtdev from device handle * @mrioc: Adapter instance reference * @handle: Device handle * * Accessor to retrieve target device from the device handle. * Non Lock version * * Return: Target device reference. */ static struct mpi3mr_tgt_dev *__mpi3mr_get_tgtdev_by_handle( struct mpi3mr_ioc *mrioc, u16 handle) { … } /** * mpi3mr_get_tgtdev_by_handle -Get tgtdev from device handle * @mrioc: Adapter instance reference * @handle: Device handle * * Accessor to retrieve target device from the device handle. * Lock version * * Return: Target device reference. */ struct mpi3mr_tgt_dev *mpi3mr_get_tgtdev_by_handle( struct mpi3mr_ioc *mrioc, u16 handle) { … } /** * __mpi3mr_get_tgtdev_by_perst_id -Get tgtdev from persist ID * @mrioc: Adapter instance reference * @persist_id: Persistent ID * * Accessor to retrieve target device from the Persistent ID. * Non Lock version * * Return: Target device reference. */ static struct mpi3mr_tgt_dev *__mpi3mr_get_tgtdev_by_perst_id( struct mpi3mr_ioc *mrioc, u16 persist_id) { … } /** * mpi3mr_get_tgtdev_by_perst_id -Get tgtdev from persistent ID * @mrioc: Adapter instance reference * @persist_id: Persistent ID * * Accessor to retrieve target device from the Persistent ID. * Lock version * * Return: Target device reference. */ static struct mpi3mr_tgt_dev *mpi3mr_get_tgtdev_by_perst_id( struct mpi3mr_ioc *mrioc, u16 persist_id) { … } /** * __mpi3mr_get_tgtdev_from_tgtpriv -Get tgtdev from tgt private * @mrioc: Adapter instance reference * @tgt_priv: Target private data * * Accessor to return target device from the target private * data. Non Lock version * * Return: Target device reference. */ static struct mpi3mr_tgt_dev *__mpi3mr_get_tgtdev_from_tgtpriv( struct mpi3mr_ioc *mrioc, struct mpi3mr_stgt_priv_data *tgt_priv) { … } /** * mpi3mr_set_io_divert_for_all_vd_in_tg -set divert for TG VDs * @mrioc: Adapter instance reference * @tg: Throttle group information pointer * @divert_value: 1 or 0 * * Accessor to set io_divert flag for each device associated * with the given throttle group with the given value. * * Return: None. */ static void mpi3mr_set_io_divert_for_all_vd_in_tg(struct mpi3mr_ioc *mrioc, struct mpi3mr_throttle_group_info *tg, u8 divert_value) { … } /** * mpi3mr_print_device_event_notice - print notice related to post processing of * device event after controller reset. * * @mrioc: Adapter instance reference * @device_add: true for device add event and false for device removal event * * Return: None. */ void mpi3mr_print_device_event_notice(struct mpi3mr_ioc *mrioc, bool device_add) { … } /** * mpi3mr_remove_tgtdev_from_host - Remove dev from upper layers * @mrioc: Adapter instance reference * @tgtdev: Target device structure * * Checks whether the device is exposed to upper layers and if it * is then remove the device from upper layers by calling * scsi_remove_target(). * * Return: 0 on success, non zero on failure. */ void mpi3mr_remove_tgtdev_from_host(struct mpi3mr_ioc *mrioc, struct mpi3mr_tgt_dev *tgtdev) { … } /** * mpi3mr_report_tgtdev_to_host - Expose device to upper layers * @mrioc: Adapter instance reference * @perst_id: Persistent ID of the device * * Checks whether the device can be exposed to upper layers and * if it is not then expose the device to upper layers by * calling scsi_scan_target(). * * Return: 0 on success, non zero on failure. */ static int mpi3mr_report_tgtdev_to_host(struct mpi3mr_ioc *mrioc, u16 perst_id) { … } /** * mpi3mr_change_queue_depth- Change QD callback handler * @sdev: SCSI device reference * @q_depth: Queue depth * * Validate and limit QD and call scsi_change_queue_depth. * * Return: return value of scsi_change_queue_depth */ static int mpi3mr_change_queue_depth(struct scsi_device *sdev, int q_depth) { … } static void mpi3mr_configure_nvme_dev(struct mpi3mr_tgt_dev *tgt_dev, struct queue_limits *lim) { … } static void mpi3mr_configure_tgt_dev(struct mpi3mr_tgt_dev *tgt_dev, struct queue_limits *lim) { … } /** * mpi3mr_update_sdev - Update SCSI device information * @sdev: SCSI device reference * @data: target device reference * * This is an iterator function called for each SCSI device in a * target to update the target specific information into each * SCSI device. * * Return: Nothing. */ static void mpi3mr_update_sdev(struct scsi_device *sdev, void *data) { … } /** * mpi3mr_refresh_tgtdevs - Refresh target device exposure * @mrioc: Adapter instance reference * * This is executed post controller reset to identify any * missing devices during reset and remove from the upper layers * or expose any newly detected device to the upper layers. * * Return: Nothing. */ static void mpi3mr_refresh_tgtdevs(struct mpi3mr_ioc *mrioc) { … } /** * mpi3mr_update_tgtdev - DevStatusChange evt bottomhalf * @mrioc: Adapter instance reference * @tgtdev: Target device internal structure * @dev_pg0: New device page0 * @is_added: Flag to indicate the device is just added * * Update the information from the device page0 into the driver * cached target device structure. * * Return: Nothing. */ static void mpi3mr_update_tgtdev(struct mpi3mr_ioc *mrioc, struct mpi3mr_tgt_dev *tgtdev, struct mpi3_device_page0 *dev_pg0, bool is_added) { … } /** * mpi3mr_devstatuschg_evt_bh - DevStatusChange evt bottomhalf * @mrioc: Adapter instance reference * @fwevt: Firmware event information. * * Process Device status Change event and based on device's new * information, either expose the device to the upper layers, or * remove the device from upper layers. * * Return: Nothing. */ static void mpi3mr_devstatuschg_evt_bh(struct mpi3mr_ioc *mrioc, struct mpi3mr_fwevt *fwevt) { … } /** * mpi3mr_devinfochg_evt_bh - DeviceInfoChange evt bottomhalf * @mrioc: Adapter instance reference * @dev_pg0: New device page0 * * Process Device Info Change event and based on device's new * information, either expose the device to the upper layers, or * remove the device from upper layers or update the details of * the device. * * Return: Nothing. */ static void mpi3mr_devinfochg_evt_bh(struct mpi3mr_ioc *mrioc, struct mpi3_device_page0 *dev_pg0) { … } /** * mpi3mr_free_enclosure_list - release enclosures * @mrioc: Adapter instance reference * * Free memory allocated during encloure add. * * Return nothing. */ void mpi3mr_free_enclosure_list(struct mpi3mr_ioc *mrioc) { … } /** * mpi3mr_enclosure_find_by_handle - enclosure search by handle * @mrioc: Adapter instance reference * @handle: Firmware device handle of the enclosure * * This searches for enclosure device based on handle, then returns the * enclosure object. * * Return: Enclosure object reference or NULL */ struct mpi3mr_enclosure_node *mpi3mr_enclosure_find_by_handle( struct mpi3mr_ioc *mrioc, u16 handle) { … } /** * mpi3mr_process_trigger_data_event_bh - Process trigger event * data * @mrioc: Adapter instance reference * @event_data: Event data * * This function releases diage buffers or issues diag fault * based on trigger conditions * * Return: Nothing */ static void mpi3mr_process_trigger_data_event_bh(struct mpi3mr_ioc *mrioc, struct trigger_event_data *event_data) { … } /** * mpi3mr_encldev_add_chg_evt_debug - debug for enclosure event * @mrioc: Adapter instance reference * @encl_pg0: Enclosure page 0. * @is_added: Added event or not * * Return nothing. */ static void mpi3mr_encldev_add_chg_evt_debug(struct mpi3mr_ioc *mrioc, struct mpi3_enclosure_page0 *encl_pg0, u8 is_added) { … } /** * mpi3mr_encldev_add_chg_evt_bh - Enclosure evt bottomhalf * @mrioc: Adapter instance reference * @fwevt: Firmware event reference * * Prints information about the Enclosure device status or * Enclosure add events if logging is enabled and add or remove * the enclosure from the controller's internal list of * enclosures. * * Return: Nothing. */ static void mpi3mr_encldev_add_chg_evt_bh(struct mpi3mr_ioc *mrioc, struct mpi3mr_fwevt *fwevt) { … } /** * mpi3mr_sastopochg_evt_debug - SASTopoChange details * @mrioc: Adapter instance reference * @event_data: SAS topology change list event data * * Prints information about the SAS topology change event. * * Return: Nothing. */ static void mpi3mr_sastopochg_evt_debug(struct mpi3mr_ioc *mrioc, struct mpi3_event_data_sas_topology_change_list *event_data) { … } /** * mpi3mr_sastopochg_evt_bh - SASTopologyChange evt bottomhalf * @mrioc: Adapter instance reference * @fwevt: Firmware event reference * * Prints information about the SAS topology change event and * for "not responding" event code, removes the device from the * upper layers. * * Return: Nothing. */ static void mpi3mr_sastopochg_evt_bh(struct mpi3mr_ioc *mrioc, struct mpi3mr_fwevt *fwevt) { … } /** * mpi3mr_pcietopochg_evt_debug - PCIeTopoChange details * @mrioc: Adapter instance reference * @event_data: PCIe topology change list event data * * Prints information about the PCIe topology change event. * * Return: Nothing. */ static void mpi3mr_pcietopochg_evt_debug(struct mpi3mr_ioc *mrioc, struct mpi3_event_data_pcie_topology_change_list *event_data) { … } /** * mpi3mr_pcietopochg_evt_bh - PCIeTopologyChange evt bottomhalf * @mrioc: Adapter instance reference * @fwevt: Firmware event reference * * Prints information about the PCIe topology change event and * for "not responding" event code, removes the device from the * upper layers. * * Return: Nothing. */ static void mpi3mr_pcietopochg_evt_bh(struct mpi3mr_ioc *mrioc, struct mpi3mr_fwevt *fwevt) { … } /** * mpi3mr_logdata_evt_bh - Log data event bottomhalf * @mrioc: Adapter instance reference * @fwevt: Firmware event reference * * Extracts the event data and calls application interfacing * function to process the event further. * * Return: Nothing. */ static void mpi3mr_logdata_evt_bh(struct mpi3mr_ioc *mrioc, struct mpi3mr_fwevt *fwevt) { … } /** * mpi3mr_update_sdev_qd - Update SCSI device queue depath * @sdev: SCSI device reference * @data: Queue depth reference * * This is an iterator function called for each SCSI device in a * target to update the QD of each SCSI device. * * Return: Nothing. */ static void mpi3mr_update_sdev_qd(struct scsi_device *sdev, void *data) { … } /** * mpi3mr_set_qd_for_all_vd_in_tg -set QD for TG VDs * @mrioc: Adapter instance reference * @tg: Throttle group information pointer * * Accessor to reduce QD for each device associated with the * given throttle group. * * Return: None. */ static void mpi3mr_set_qd_for_all_vd_in_tg(struct mpi3mr_ioc *mrioc, struct mpi3mr_throttle_group_info *tg) { … } /** * mpi3mr_fwevt_bh - Firmware event bottomhalf handler * @mrioc: Adapter instance reference * @fwevt: Firmware event reference * * Identifies the firmware event and calls corresponding bottomg * half handler and sends event acknowledgment if required. * * Return: Nothing. */ static void mpi3mr_fwevt_bh(struct mpi3mr_ioc *mrioc, struct mpi3mr_fwevt *fwevt) { … } /** * mpi3mr_fwevt_worker - Firmware event worker * @work: Work struct containing firmware event * * Extracts the firmware event and calls mpi3mr_fwevt_bh. * * Return: Nothing. */ static void mpi3mr_fwevt_worker(struct work_struct *work) { … } /** * mpi3mr_create_tgtdev - Create and add a target device * @mrioc: Adapter instance reference * @dev_pg0: Device Page 0 data * * If the device specified by the device page 0 data is not * present in the driver's internal list, allocate the memory * for the device, populate the data and add to the list, else * update the device data. The key is persistent ID. * * Return: 0 on success, -ENOMEM on memory allocation failure */ static int mpi3mr_create_tgtdev(struct mpi3mr_ioc *mrioc, struct mpi3_device_page0 *dev_pg0) { … } /** * mpi3mr_flush_delayed_cmd_lists - Flush pending commands * @mrioc: Adapter instance reference * * Flush pending commands in the delayed lists due to a * controller reset or driver removal as a cleanup. * * Return: Nothing */ void mpi3mr_flush_delayed_cmd_lists(struct mpi3mr_ioc *mrioc) { … } /** * mpi3mr_dev_rmhs_complete_iou - Device removal IOUC completion * @mrioc: Adapter instance reference * @drv_cmd: Internal command tracker * * Issues a target reset TM to the firmware from the device * removal TM pend list or retry the removal handshake sequence * based on the IOU control request IOC status. * * Return: Nothing */ static void mpi3mr_dev_rmhs_complete_iou(struct mpi3mr_ioc *mrioc, struct mpi3mr_drv_cmd *drv_cmd) { … } /** * mpi3mr_dev_rmhs_complete_tm - Device removal TM completion * @mrioc: Adapter instance reference * @drv_cmd: Internal command tracker * * Issues a target reset TM to the firmware from the device * removal TM pend list or issue IO unit control request as * part of device removal or hidden acknowledgment handshake. * * Return: Nothing */ static void mpi3mr_dev_rmhs_complete_tm(struct mpi3mr_ioc *mrioc, struct mpi3mr_drv_cmd *drv_cmd) { … } /** * mpi3mr_dev_rmhs_send_tm - Issue TM for device removal * @mrioc: Adapter instance reference * @handle: Device handle * @cmdparam: Internal command tracker * @iou_rc: IO unit reason code * * Issues a target reset TM to the firmware or add it to a pend * list as part of device removal or hidden acknowledgment * handshake. * * Return: Nothing */ static void mpi3mr_dev_rmhs_send_tm(struct mpi3mr_ioc *mrioc, u16 handle, struct mpi3mr_drv_cmd *cmdparam, u8 iou_rc) { … } /** * mpi3mr_complete_evt_ack - event ack request completion * @mrioc: Adapter instance reference * @drv_cmd: Internal command tracker * * This is the completion handler for non blocking event * acknowledgment sent to the firmware and this will issue any * pending event acknowledgment request. * * Return: Nothing */ static void mpi3mr_complete_evt_ack(struct mpi3mr_ioc *mrioc, struct mpi3mr_drv_cmd *drv_cmd) { … } /** * mpi3mr_send_event_ack - Issue event acknwoledgment request * @mrioc: Adapter instance reference * @event: MPI3 event id * @cmdparam: Internal command tracker * @event_ctx: event context * * Issues event acknowledgment request to the firmware if there * is a free command to send the event ack else it to a pend * list so that it will be processed on a completion of a prior * event acknowledgment . * * Return: Nothing */ static void mpi3mr_send_event_ack(struct mpi3mr_ioc *mrioc, u8 event, struct mpi3mr_drv_cmd *cmdparam, u32 event_ctx) { … } /** * mpi3mr_pcietopochg_evt_th - PCIETopologyChange evt tophalf * @mrioc: Adapter instance reference * @event_reply: event data * * Checks for the reason code and based on that either block I/O * to device, or unblock I/O to the device, or start the device * removal handshake with reason as remove with the firmware for * PCIe devices. * * Return: Nothing */ static void mpi3mr_pcietopochg_evt_th(struct mpi3mr_ioc *mrioc, struct mpi3_event_notification_reply *event_reply) { … } /** * mpi3mr_sastopochg_evt_th - SASTopologyChange evt tophalf * @mrioc: Adapter instance reference * @event_reply: event data * * Checks for the reason code and based on that either block I/O * to device, or unblock I/O to the device, or start the device * removal handshake with reason as remove with the firmware for * SAS/SATA devices. * * Return: Nothing */ static void mpi3mr_sastopochg_evt_th(struct mpi3mr_ioc *mrioc, struct mpi3_event_notification_reply *event_reply) { … } /** * mpi3mr_devstatuschg_evt_th - DeviceStatusChange evt tophalf * @mrioc: Adapter instance reference * @event_reply: event data * * Checks for the reason code and based on that either block I/O * to device, or unblock I/O to the device, or start the device * removal handshake with reason as remove/hide acknowledgment * with the firmware. * * Return: Nothing */ static void mpi3mr_devstatuschg_evt_th(struct mpi3mr_ioc *mrioc, struct mpi3_event_notification_reply *event_reply) { … } /** * mpi3mr_preparereset_evt_th - Prepare for reset event tophalf * @mrioc: Adapter instance reference * @event_reply: event data * * Blocks and unblocks host level I/O based on the reason code * * Return: Nothing */ static void mpi3mr_preparereset_evt_th(struct mpi3mr_ioc *mrioc, struct mpi3_event_notification_reply *event_reply) { … } /** * mpi3mr_energypackchg_evt_th - Energy pack change evt tophalf * @mrioc: Adapter instance reference * @event_reply: event data * * Identifies the new shutdown timeout value and update. * * Return: Nothing */ static void mpi3mr_energypackchg_evt_th(struct mpi3mr_ioc *mrioc, struct mpi3_event_notification_reply *event_reply) { … } /** * mpi3mr_cablemgmt_evt_th - Cable management event tophalf * @mrioc: Adapter instance reference * @event_reply: event data * * Displays Cable manegemt event details. * * Return: Nothing */ static void mpi3mr_cablemgmt_evt_th(struct mpi3mr_ioc *mrioc, struct mpi3_event_notification_reply *event_reply) { … } /** * mpi3mr_add_event_wait_for_device_refresh - Add Wait for Device Refresh Event * @mrioc: Adapter instance reference * * Add driver specific event to make sure that the driver won't process the * events until all the devices are refreshed during soft reset. * * Return: Nothing */ void mpi3mr_add_event_wait_for_device_refresh(struct mpi3mr_ioc *mrioc) { … } /** * mpi3mr_os_handle_events - Firmware event handler * @mrioc: Adapter instance reference * @event_reply: event data * * Identify whteher the event has to handled and acknowledged * and either process the event in the tophalf and/or schedule a * bottom half through mpi3mr_fwevt_worker. * * Return: Nothing */ void mpi3mr_os_handle_events(struct mpi3mr_ioc *mrioc, struct mpi3_event_notification_reply *event_reply) { … } /** * mpi3mr_setup_eedp - Setup EEDP information in MPI3 SCSI IO * @mrioc: Adapter instance reference * @scmd: SCSI command reference * @scsiio_req: MPI3 SCSI IO request * * Identifies the protection information flags from the SCSI * command and set appropriate flags in the MPI3 SCSI IO * request. * * Return: Nothing */ static void mpi3mr_setup_eedp(struct mpi3mr_ioc *mrioc, struct scsi_cmnd *scmd, struct mpi3_scsi_io_request *scsiio_req) { … } /** * mpi3mr_build_sense_buffer - Map sense information * @desc: Sense type * @buf: Sense buffer to populate * @key: Sense key * @asc: Additional sense code * @ascq: Additional sense code qualifier * * Maps the given sense information into either descriptor or * fixed format sense data. * * Return: Nothing */ static inline void mpi3mr_build_sense_buffer(int desc, u8 *buf, u8 key, u8 asc, u8 ascq) { … } /** * mpi3mr_map_eedp_error - Map EEDP errors from IOC status * @scmd: SCSI command reference * @ioc_status: status of MPI3 request * * Maps the EEDP error status of the SCSI IO request to sense * data. * * Return: Nothing */ static void mpi3mr_map_eedp_error(struct scsi_cmnd *scmd, u16 ioc_status) { … } /** * mpi3mr_process_op_reply_desc - reply descriptor handler * @mrioc: Adapter instance reference * @reply_desc: Operational reply descriptor * @reply_dma: place holder for reply DMA address * @qidx: Operational queue index * * Process the operational reply descriptor and identifies the * descriptor type. Based on the descriptor map the MPI3 request * status to a SCSI command status and calls scsi_done call * back. * * Return: Nothing */ void mpi3mr_process_op_reply_desc(struct mpi3mr_ioc *mrioc, struct mpi3_default_reply_descriptor *reply_desc, u64 *reply_dma, u16 qidx) { … } /** * mpi3mr_get_chain_idx - get free chain buffer index * @mrioc: Adapter instance reference * * Try to get a free chain buffer index from the free pool. * * Return: -1 on failure or the free chain buffer index */ static int mpi3mr_get_chain_idx(struct mpi3mr_ioc *mrioc) { … } /** * mpi3mr_prepare_sg_scmd - build scatter gather list * @mrioc: Adapter instance reference * @scmd: SCSI command reference * @scsiio_req: MPI3 SCSI IO request * * This function maps SCSI command's data and protection SGEs to * MPI request SGEs. If required additional 4K chain buffer is * used to send the SGEs. * * Return: 0 on success, -ENOMEM on dma_map_sg failure */ static int mpi3mr_prepare_sg_scmd(struct mpi3mr_ioc *mrioc, struct scsi_cmnd *scmd, struct mpi3_scsi_io_request *scsiio_req) { … } /** * mpi3mr_build_sg_scmd - build scatter gather list for SCSI IO * @mrioc: Adapter instance reference * @scmd: SCSI command reference * @scsiio_req: MPI3 SCSI IO request * * This function calls mpi3mr_prepare_sg_scmd for constructing * both data SGEs and protection information SGEs in the MPI * format from the SCSI Command as appropriate . * * Return: return value of mpi3mr_prepare_sg_scmd. */ static int mpi3mr_build_sg_scmd(struct mpi3mr_ioc *mrioc, struct scsi_cmnd *scmd, struct mpi3_scsi_io_request *scsiio_req) { … } /** * mpi3mr_tm_response_name - get TM response as a string * @resp_code: TM response code * * Convert known task management response code as a readable * string. * * Return: response code string. */ static const char *mpi3mr_tm_response_name(u8 resp_code) { … } inline void mpi3mr_poll_pend_io_completions(struct mpi3mr_ioc *mrioc) { … } /** * mpi3mr_issue_tm - Issue Task Management request * @mrioc: Adapter instance reference * @tm_type: Task Management type * @handle: Device handle * @lun: lun ID * @htag: Host tag of the TM request * @timeout: TM timeout value * @drv_cmd: Internal command tracker * @resp_code: Response code place holder * @scmd: SCSI command * * Issues a Task Management Request to the controller for a * specified target, lun and command and wait for its completion * and check TM response. Recover the TM if it timed out by * issuing controller reset. * * Return: 0 on success, non-zero on errors */ int mpi3mr_issue_tm(struct mpi3mr_ioc *mrioc, u8 tm_type, u16 handle, uint lun, u16 htag, ulong timeout, struct mpi3mr_drv_cmd *drv_cmd, u8 *resp_code, struct scsi_cmnd *scmd) { … } /** * mpi3mr_bios_param - BIOS param callback * @sdev: SCSI device reference * @bdev: Block device reference * @capacity: Capacity in logical sectors * @params: Parameter array * * Just the parameters with heads/secots/cylinders. * * Return: 0 always */ static int mpi3mr_bios_param(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int params[]) { … } /** * mpi3mr_map_queues - Map queues callback handler * @shost: SCSI host reference * * Maps default and poll queues. * * Return: return zero. */ static void mpi3mr_map_queues(struct Scsi_Host *shost) { … } /** * mpi3mr_get_fw_pending_ios - Calculate pending I/O count * @mrioc: Adapter instance reference * * Calculate the pending I/Os for the controller and return. * * Return: Number of pending I/Os */ static inline int mpi3mr_get_fw_pending_ios(struct mpi3mr_ioc *mrioc) { … } /** * mpi3mr_print_pending_host_io - print pending I/Os * @mrioc: Adapter instance reference * * Print number of pending I/Os and each I/O details prior to * reset for debug purpose. * * Return: Nothing */ static void mpi3mr_print_pending_host_io(struct mpi3mr_ioc *mrioc) { … } /** * mpi3mr_wait_for_host_io - block for I/Os to complete * @mrioc: Adapter instance reference * @timeout: time out in seconds * Waits for pending I/Os for the given adapter to complete or * to hit the timeout. * * Return: Nothing */ void mpi3mr_wait_for_host_io(struct mpi3mr_ioc *mrioc, u32 timeout) { … } /** * mpi3mr_setup_divert_ws - Setup Divert IO flag for write same * @mrioc: Adapter instance reference * @scmd: SCSI command reference * @scsiio_req: MPI3 SCSI IO request * @scsiio_flags: Pointer to MPI3 SCSI IO Flags * @wslen: write same max length * * Gets values of unmap, ndob and number of blocks from write * same scsi io and based on these values it sets divert IO flag * and reason for diverting IO to firmware. * * Return: Nothing */ static inline void mpi3mr_setup_divert_ws(struct mpi3mr_ioc *mrioc, struct scsi_cmnd *scmd, struct mpi3_scsi_io_request *scsiio_req, u32 *scsiio_flags, u16 wslen) { … } /** * mpi3mr_eh_host_reset - Host reset error handling callback * @scmd: SCSI command reference * * Issue controller reset * * Return: SUCCESS of successful reset else FAILED */ static int mpi3mr_eh_host_reset(struct scsi_cmnd *scmd) { … } /** * mpi3mr_eh_bus_reset - Bus reset error handling callback * @scmd: SCSI command reference * * Checks whether pending I/Os are present for the RAID volume; * if not there's no need to reset the adapter. * * Return: SUCCESS of successful reset else FAILED */ static int mpi3mr_eh_bus_reset(struct scsi_cmnd *scmd) { … } /** * mpi3mr_eh_target_reset - Target reset error handling callback * @scmd: SCSI command reference * * Issue Target reset Task Management and verify the scmd is * terminated successfully and return status accordingly. * * Return: SUCCESS of successful termination of the scmd else * FAILED */ static int mpi3mr_eh_target_reset(struct scsi_cmnd *scmd) { … } /** * mpi3mr_eh_dev_reset- Device reset error handling callback * @scmd: SCSI command reference * * Issue lun reset Task Management and verify the scmd is * terminated successfully and return status accordingly. * * Return: SUCCESS of successful termination of the scmd else * FAILED */ static int mpi3mr_eh_dev_reset(struct scsi_cmnd *scmd) { … } /** * mpi3mr_scan_start - Scan start callback handler * @shost: SCSI host reference * * Issue port enable request asynchronously. * * Return: Nothing */ static void mpi3mr_scan_start(struct Scsi_Host *shost) { … } /** * mpi3mr_scan_finished - Scan finished callback handler * @shost: SCSI host reference * @time: Jiffies from the scan start * * Checks whether the port enable is completed or timedout or * failed and set the scan status accordingly after taking any * recovery if required. * * Return: 1 on scan finished or timed out, 0 for in progress */ static int mpi3mr_scan_finished(struct Scsi_Host *shost, unsigned long time) { … } /** * mpi3mr_slave_destroy - Slave destroy callback handler * @sdev: SCSI device reference * * Cleanup and free per device(lun) private data. * * Return: Nothing. */ static void mpi3mr_slave_destroy(struct scsi_device *sdev) { … } /** * mpi3mr_target_destroy - Target destroy callback handler * @starget: SCSI target reference * * Cleanup and free per target private data. * * Return: Nothing. */ static void mpi3mr_target_destroy(struct scsi_target *starget) { … } /** * mpi3mr_device_configure - Slave configure callback handler * @sdev: SCSI device reference * @lim: queue limits * * Configure queue depth, max hardware sectors and virt boundary * as required * * Return: 0 always. */ static int mpi3mr_device_configure(struct scsi_device *sdev, struct queue_limits *lim) { … } /** * mpi3mr_slave_alloc -Slave alloc callback handler * @sdev: SCSI device reference * * Allocate per device(lun) private data and initialize it. * * Return: 0 on success -ENOMEM on memory allocation failure. */ static int mpi3mr_slave_alloc(struct scsi_device *sdev) { … } /** * mpi3mr_target_alloc - Target alloc callback handler * @starget: SCSI target reference * * Allocate per target private data and initialize it. * * Return: 0 on success -ENOMEM on memory allocation failure. */ static int mpi3mr_target_alloc(struct scsi_target *starget) { … } /** * mpi3mr_check_return_unmap - Whether an unmap is allowed * @mrioc: Adapter instance reference * @scmd: SCSI Command reference * * The controller hardware cannot handle certain unmap commands * for NVMe drives, this routine checks those and return true * and completes the SCSI command with proper status and sense * data. * * Return: TRUE for not allowed unmap, FALSE otherwise. */ static bool mpi3mr_check_return_unmap(struct mpi3mr_ioc *mrioc, struct scsi_cmnd *scmd) { … } /** * mpi3mr_allow_scmd_to_fw - Command is allowed during shutdown * @scmd: SCSI Command reference * * Checks whether a cdb is allowed during shutdown or not. * * Return: TRUE for allowed commands, FALSE otherwise. */ inline bool mpi3mr_allow_scmd_to_fw(struct scsi_cmnd *scmd) { … } /** * mpi3mr_qcmd - I/O request despatcher * @shost: SCSI Host reference * @scmd: SCSI Command reference * * Issues the SCSI Command as an MPI3 request. * * Return: 0 on successful queueing of the request or if the * request is completed with failure. * SCSI_MLQUEUE_DEVICE_BUSY when the device is busy. * SCSI_MLQUEUE_HOST_BUSY when the host queue is full. */ static int mpi3mr_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) { … } static const struct scsi_host_template mpi3mr_driver_template = …; /** * mpi3mr_init_drv_cmd - Initialize internal command tracker * @cmdptr: Internal command tracker * @host_tag: Host tag used for the specific command * * Initialize the internal command tracker structure with * specified host tag. * * Return: Nothing. */ static inline void mpi3mr_init_drv_cmd(struct mpi3mr_drv_cmd *cmdptr, u16 host_tag) { … } /** * osintfc_mrioc_security_status -Check controller secure status * @pdev: PCI device instance * * Read the Device Serial Number capability from PCI config * space and decide whether the controller is secure or not. * * Return: 0 on success, non-zero on failure. */ static int osintfc_mrioc_security_status(struct pci_dev *pdev) { … } /** * mpi3mr_probe - PCI probe callback * @pdev: PCI device instance * @id: PCI device ID details * * controller initialization routine. Checks the security status * of the controller and if it is invalid or tampered return the * probe without initializing the controller. Otherwise, * allocate per adapter instance through shost_priv and * initialize controller specific data structures, initializae * the controller hardware, add shost to the SCSI subsystem. * * Return: 0 on success, non-zero on failure. */ static int mpi3mr_probe(struct pci_dev *pdev, const struct pci_device_id *id) { … } /** * mpi3mr_remove - PCI remove callback * @pdev: PCI device instance * * Cleanup the IOC by issuing MUR and shutdown notification. * Free up all memory and resources associated with the * controllerand target devices, unregister the shost. * * Return: Nothing. */ static void mpi3mr_remove(struct pci_dev *pdev) { … } /** * mpi3mr_shutdown - PCI shutdown callback * @pdev: PCI device instance * * Free up all memory and resources associated with the * controller * * Return: Nothing. */ static void mpi3mr_shutdown(struct pci_dev *pdev) { … } /** * mpi3mr_suspend - PCI power management suspend callback * @dev: Device struct * * Change the power state to the given value and cleanup the IOC * by issuing MUR and shutdown notification * * Return: 0 always. */ static int __maybe_unused mpi3mr_suspend(struct device *dev) { … } /** * mpi3mr_resume - PCI power management resume callback * @dev: Device struct * * Restore the power state to D0 and reinitialize the controller * and resume I/O operations to the target devices * * Return: 0 on success, non-zero on failure */ static int __maybe_unused mpi3mr_resume(struct device *dev) { … } /** * mpi3mr_pcierr_error_detected - PCI error detected callback * @pdev: PCI device instance * @state: channel state * * This function is called by the PCI error recovery driver and * based on the state passed the driver decides what actions to * be recommended back to PCI driver. * * For all of the states if there is no valid mrioc or scsi host * references in the PCI device then this function will return * the result as disconnect. * * For normal state, this function will return the result as can * recover. * * For frozen state, this function will block for any pending * controller initialization or re-initialization to complete, * stop any new interactions with the controller and return * status as reset required. * * For permanent failure state, this function will mark the * controller as unrecoverable and return status as disconnect. * * Returns: PCI_ERS_RESULT_NEED_RESET or CAN_RECOVER or * DISCONNECT based on the controller state. */ static pci_ers_result_t mpi3mr_pcierr_error_detected(struct pci_dev *pdev, pci_channel_state_t state) { … } /** * mpi3mr_pcierr_slot_reset - Post slot reset callback * @pdev: PCI device instance * * This function is called by the PCI error recovery driver * after a slot or link reset issued by it for the recovery, the * driver is expected to bring back the controller and * initialize it. * * This function restores PCI state and reinitializes controller * resources and the controller, this blocks for any pending * reset to complete. * * Returns: PCI_ERS_RESULT_DISCONNECT on failure or * PCI_ERS_RESULT_RECOVERED */ static pci_ers_result_t mpi3mr_pcierr_slot_reset(struct pci_dev *pdev) { … } /** * mpi3mr_pcierr_resume - PCI error recovery resume * callback * @pdev: PCI device instance * * This function enables all I/O and IOCTLs post reset issued as * part of the PCI error recovery * * Return: Nothing. */ static void mpi3mr_pcierr_resume(struct pci_dev *pdev) { … } /** * mpi3mr_pcierr_mmio_enabled - PCI error recovery callback * @pdev: PCI device instance * * This is called only if mpi3mr_pcierr_error_detected returns * PCI_ERS_RESULT_CAN_RECOVER. * * Return: PCI_ERS_RESULT_DISCONNECT when the controller is * unrecoverable or when the shost/mrioc reference cannot be * found, else return PCI_ERS_RESULT_RECOVERED */ static pci_ers_result_t mpi3mr_pcierr_mmio_enabled(struct pci_dev *pdev) { … } static const struct pci_device_id mpi3mr_pci_id_table[] = …; MODULE_DEVICE_TABLE(pci, mpi3mr_pci_id_table); static struct pci_error_handlers mpi3mr_err_handler = …; static SIMPLE_DEV_PM_OPS(mpi3mr_pm_ops, mpi3mr_suspend, mpi3mr_resume); static struct pci_driver mpi3mr_pci_driver = …; static ssize_t event_counter_show(struct device_driver *dd, char *buf) { … } static DRIVER_ATTR_RO(event_counter); static int __init mpi3mr_init(void) { … } static void __exit mpi3mr_exit(void) { … } module_init(…) …; module_exit(mpi3mr_exit);