// SPDX-License-Identifier: GPL-2.0-or-later /* * Linux MegaRAID driver for SAS based RAID controllers * * Copyright (c) 2003-2013 LSI Corporation * Copyright (c) 2013-2016 Avago Technologies * Copyright (c) 2016-2018 Broadcom Inc. * * Authors: Broadcom Inc. * Sreenivas Bagalkote * Sumant Patro * Bo Yang * Adam Radford * Kashyap Desai <[email protected]> * Sumit Saxena <[email protected]> * * Send feedback to: [email protected] */ #include <linux/kernel.h> #include <linux/types.h> #include <linux/pci.h> #include <linux/list.h> #include <linux/moduleparam.h> #include <linux/module.h> #include <linux/spinlock.h> #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/uio.h> #include <linux/slab.h> #include <linux/uaccess.h> #include <asm/unaligned.h> #include <linux/fs.h> #include <linux/compat.h> #include <linux/blkdev.h> #include <linux/mutex.h> #include <linux/poll.h> #include <linux/vmalloc.h> #include <linux/irq_poll.h> #include <linux/blk-mq-pci.h> #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> #include <scsi/scsi_tcq.h> #include <scsi/scsi_dbg.h> #include "megaraid_sas_fusion.h" #include "megaraid_sas.h" /* * Number of sectors per IO command * Will be set in megasas_init_mfi if user does not provide */ static unsigned int max_sectors; module_param_named(max_sectors, max_sectors, int, 0444); MODULE_PARM_DESC(…) …; static int msix_disable; module_param(msix_disable, int, 0444); MODULE_PARM_DESC(…) …; static unsigned int msix_vectors; module_param(msix_vectors, int, 0444); MODULE_PARM_DESC(…) …; static int allow_vf_ioctls; module_param(allow_vf_ioctls, int, 0444); MODULE_PARM_DESC(…) …; static unsigned int throttlequeuedepth = …; module_param(throttlequeuedepth, int, 0444); MODULE_PARM_DESC(…) …; unsigned int resetwaittime = …; module_param(resetwaittime, int, 0444); MODULE_PARM_DESC(…) …; static int smp_affinity_enable = …; module_param(smp_affinity_enable, int, 0444); MODULE_PARM_DESC(…) …; static int rdpq_enable = …; module_param(rdpq_enable, int, 0444); MODULE_PARM_DESC(…) …; unsigned int dual_qdepth_disable; module_param(dual_qdepth_disable, int, 0444); MODULE_PARM_DESC(…) …; static unsigned int scmd_timeout = …; module_param(scmd_timeout, int, 0444); MODULE_PARM_DESC(…) …; int perf_mode = …; module_param(perf_mode, int, 0444); MODULE_PARM_DESC(…) …; int event_log_level = …; module_param(event_log_level, int, 0644); MODULE_PARM_DESC(…) …; unsigned int enable_sdev_max_qd; module_param(enable_sdev_max_qd, int, 0444); MODULE_PARM_DESC(…) …; int poll_queues; module_param(poll_queues, int, 0444); MODULE_PARM_DESC(…) …; int host_tagset_enable = …; module_param(host_tagset_enable, int, 0444); MODULE_PARM_DESC(…) …; MODULE_LICENSE(…) …; MODULE_VERSION(…); MODULE_AUTHOR(…) …; MODULE_DESCRIPTION(…) …; int megasas_transition_to_ready(struct megasas_instance *instance, int ocr); static int megasas_get_pd_list(struct megasas_instance *instance); static int megasas_ld_list_query(struct megasas_instance *instance, u8 query_type); static int megasas_issue_init_mfi(struct megasas_instance *instance); static int megasas_register_aen(struct megasas_instance *instance, u32 seq_num, u32 class_locale_word); static void megasas_get_pd_info(struct megasas_instance *instance, struct scsi_device *sdev); static void megasas_set_ld_removed_by_fw(struct megasas_instance *instance); /* * PCI ID table for all supported controllers */ static struct pci_device_id megasas_pci_table[] = …; MODULE_DEVICE_TABLE(pci, megasas_pci_table); static int megasas_mgmt_majorno; struct megasas_mgmt_info megasas_mgmt_info; static struct fasync_struct *megasas_async_queue; static DEFINE_MUTEX(megasas_async_queue_mutex); static int megasas_poll_wait_aen; static DECLARE_WAIT_QUEUE_HEAD(megasas_poll_wait); static u32 support_poll_for_event; u32 megasas_dbg_lvl; static u32 support_device_change; static bool support_nvme_encapsulation; static bool support_pci_lane_margining; /* define lock for aen poll */ static DEFINE_SPINLOCK(poll_aen_lock); extern struct dentry *megasas_debugfs_root; extern int megasas_blk_mq_poll(struct Scsi_Host *shost, unsigned int queue_num); void megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, u8 alt_status); static u32 megasas_read_fw_status_reg_gen2(struct megasas_instance *instance); static int megasas_adp_reset_gen2(struct megasas_instance *instance, struct megasas_register_set __iomem *reg_set); static irqreturn_t megasas_isr(int irq, void *devp); static u32 megasas_init_adapter_mfi(struct megasas_instance *instance); u32 megasas_build_and_issue_cmd(struct megasas_instance *instance, struct scsi_cmnd *scmd); static void megasas_complete_cmd_dpc(unsigned long instance_addr); int wait_and_poll(struct megasas_instance *instance, struct megasas_cmd *cmd, int seconds); void megasas_fusion_ocr_wq(struct work_struct *work); static int megasas_get_ld_vf_affiliation(struct megasas_instance *instance, int initial); static int megasas_set_dma_mask(struct megasas_instance *instance); static int megasas_alloc_ctrl_mem(struct megasas_instance *instance); static inline void megasas_free_ctrl_mem(struct megasas_instance *instance); static inline int megasas_alloc_ctrl_dma_buffers(struct megasas_instance *instance); static inline void megasas_free_ctrl_dma_buffers(struct megasas_instance *instance); static inline void megasas_init_ctrl_params(struct megasas_instance *instance); u32 megasas_readl(struct megasas_instance *instance, const volatile void __iomem *addr) { … } /** * megasas_set_dma_settings - Populate DMA address, length and flags for DCMDs * @instance: Adapter soft state * @dcmd: DCMD frame inside MFI command * @dma_addr: DMA address of buffer to be passed to FW * @dma_len: Length of DMA buffer to be passed to FW * @return: void */ void megasas_set_dma_settings(struct megasas_instance *instance, struct megasas_dcmd_frame *dcmd, dma_addr_t dma_addr, u32 dma_len) { … } static void megasas_issue_dcmd(struct megasas_instance *instance, struct megasas_cmd *cmd) { … } /** * megasas_get_cmd - Get a command from the free pool * @instance: Adapter soft state * * Returns a free command from the pool */ struct megasas_cmd *megasas_get_cmd(struct megasas_instance *instance) { … } /** * megasas_return_cmd - Return a cmd to free command pool * @instance: Adapter soft state * @cmd: Command packet to be returned to free command pool */ void megasas_return_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd) { … } static const char * format_timestamp(uint32_t timestamp) { … } static const char * format_class(int8_t class) { … } /** * megasas_decode_evt: Decode FW AEN event and print critical event * for information. * @instance: Adapter soft state */ static void megasas_decode_evt(struct megasas_instance *instance) { … } /* * The following functions are defined for xscale * (deviceid : 1064R, PERC5) controllers */ /** * megasas_enable_intr_xscale - Enables interrupts * @instance: Adapter soft state */ static inline void megasas_enable_intr_xscale(struct megasas_instance *instance) { … } /** * megasas_disable_intr_xscale -Disables interrupt * @instance: Adapter soft state */ static inline void megasas_disable_intr_xscale(struct megasas_instance *instance) { … } /** * megasas_read_fw_status_reg_xscale - returns the current FW status value * @instance: Adapter soft state */ static u32 megasas_read_fw_status_reg_xscale(struct megasas_instance *instance) { … } /** * megasas_clear_intr_xscale - Check & clear interrupt * @instance: Adapter soft state */ static int megasas_clear_intr_xscale(struct megasas_instance *instance) { … } /** * megasas_fire_cmd_xscale - Sends command to the FW * @instance: Adapter soft state * @frame_phys_addr : Physical address of cmd * @frame_count : Number of frames for the command * @regs : MFI register set */ static inline void megasas_fire_cmd_xscale(struct megasas_instance *instance, dma_addr_t frame_phys_addr, u32 frame_count, struct megasas_register_set __iomem *regs) { … } /** * megasas_adp_reset_xscale - For controller reset * @instance: Adapter soft state * @regs: MFI register set */ static int megasas_adp_reset_xscale(struct megasas_instance *instance, struct megasas_register_set __iomem *regs) { … } /** * megasas_check_reset_xscale - For controller reset check * @instance: Adapter soft state * @regs: MFI register set */ static int megasas_check_reset_xscale(struct megasas_instance *instance, struct megasas_register_set __iomem *regs) { … } static struct megasas_instance_template megasas_instance_template_xscale = …; /* * This is the end of set of functions & definitions specific * to xscale (deviceid : 1064R, PERC5) controllers */ /* * The following functions are defined for ppc (deviceid : 0x60) * controllers */ /** * megasas_enable_intr_ppc - Enables interrupts * @instance: Adapter soft state */ static inline void megasas_enable_intr_ppc(struct megasas_instance *instance) { … } /** * megasas_disable_intr_ppc - Disable interrupt * @instance: Adapter soft state */ static inline void megasas_disable_intr_ppc(struct megasas_instance *instance) { … } /** * megasas_read_fw_status_reg_ppc - returns the current FW status value * @instance: Adapter soft state */ static u32 megasas_read_fw_status_reg_ppc(struct megasas_instance *instance) { … } /** * megasas_clear_intr_ppc - Check & clear interrupt * @instance: Adapter soft state */ static int megasas_clear_intr_ppc(struct megasas_instance *instance) { … } /** * megasas_fire_cmd_ppc - Sends command to the FW * @instance: Adapter soft state * @frame_phys_addr: Physical address of cmd * @frame_count: Number of frames for the command * @regs: MFI register set */ static inline void megasas_fire_cmd_ppc(struct megasas_instance *instance, dma_addr_t frame_phys_addr, u32 frame_count, struct megasas_register_set __iomem *regs) { … } /** * megasas_check_reset_ppc - For controller reset check * @instance: Adapter soft state * @regs: MFI register set */ static int megasas_check_reset_ppc(struct megasas_instance *instance, struct megasas_register_set __iomem *regs) { … } static struct megasas_instance_template megasas_instance_template_ppc = …; /** * megasas_enable_intr_skinny - Enables interrupts * @instance: Adapter soft state */ static inline void megasas_enable_intr_skinny(struct megasas_instance *instance) { … } /** * megasas_disable_intr_skinny - Disables interrupt * @instance: Adapter soft state */ static inline void megasas_disable_intr_skinny(struct megasas_instance *instance) { … } /** * megasas_read_fw_status_reg_skinny - returns the current FW status value * @instance: Adapter soft state */ static u32 megasas_read_fw_status_reg_skinny(struct megasas_instance *instance) { … } /** * megasas_clear_intr_skinny - Check & clear interrupt * @instance: Adapter soft state */ static int megasas_clear_intr_skinny(struct megasas_instance *instance) { … } /** * megasas_fire_cmd_skinny - Sends command to the FW * @instance: Adapter soft state * @frame_phys_addr: Physical address of cmd * @frame_count: Number of frames for the command * @regs: MFI register set */ static inline void megasas_fire_cmd_skinny(struct megasas_instance *instance, dma_addr_t frame_phys_addr, u32 frame_count, struct megasas_register_set __iomem *regs) { … } /** * megasas_check_reset_skinny - For controller reset check * @instance: Adapter soft state * @regs: MFI register set */ static int megasas_check_reset_skinny(struct megasas_instance *instance, struct megasas_register_set __iomem *regs) { … } static struct megasas_instance_template megasas_instance_template_skinny = …; /* * The following functions are defined for gen2 (deviceid : 0x78 0x79) * controllers */ /** * megasas_enable_intr_gen2 - Enables interrupts * @instance: Adapter soft state */ static inline void megasas_enable_intr_gen2(struct megasas_instance *instance) { … } /** * megasas_disable_intr_gen2 - Disables interrupt * @instance: Adapter soft state */ static inline void megasas_disable_intr_gen2(struct megasas_instance *instance) { … } /** * megasas_read_fw_status_reg_gen2 - returns the current FW status value * @instance: Adapter soft state */ static u32 megasas_read_fw_status_reg_gen2(struct megasas_instance *instance) { … } /** * megasas_clear_intr_gen2 - Check & clear interrupt * @instance: Adapter soft state */ static int megasas_clear_intr_gen2(struct megasas_instance *instance) { … } /** * megasas_fire_cmd_gen2 - Sends command to the FW * @instance: Adapter soft state * @frame_phys_addr: Physical address of cmd * @frame_count: Number of frames for the command * @regs: MFI register set */ static inline void megasas_fire_cmd_gen2(struct megasas_instance *instance, dma_addr_t frame_phys_addr, u32 frame_count, struct megasas_register_set __iomem *regs) { … } /** * megasas_adp_reset_gen2 - For controller reset * @instance: Adapter soft state * @reg_set: MFI register set */ static int megasas_adp_reset_gen2(struct megasas_instance *instance, struct megasas_register_set __iomem *reg_set) { … } /** * megasas_check_reset_gen2 - For controller reset check * @instance: Adapter soft state * @regs: MFI register set */ static int megasas_check_reset_gen2(struct megasas_instance *instance, struct megasas_register_set __iomem *regs) { … } static struct megasas_instance_template megasas_instance_template_gen2 = …; /* * This is the end of set of functions & definitions * specific to gen2 (deviceid : 0x78, 0x79) controllers */ /* * Template added for TB (Fusion) */ extern struct megasas_instance_template megasas_instance_template_fusion; /** * megasas_issue_polled - Issues a polling command * @instance: Adapter soft state * @cmd: Command packet to be issued * * For polling, MFI requires the cmd_status to be set to MFI_STAT_INVALID_STATUS before posting. */ int megasas_issue_polled(struct megasas_instance *instance, struct megasas_cmd *cmd) { … } /** * megasas_issue_blocked_cmd - Synchronous wrapper around regular FW cmds * @instance: Adapter soft state * @cmd: Command to be issued * @timeout: Timeout in seconds * * This function waits on an event for the command to be returned from ISR. * Max wait time is MEGASAS_INTERNAL_CMD_WAIT_TIME secs * Used to issue ioctl commands. */ int megasas_issue_blocked_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, int timeout) { … } /** * megasas_issue_blocked_abort_cmd - Aborts previously issued cmd * @instance: Adapter soft state * @cmd_to_abort: Previously issued cmd to be aborted * @timeout: Timeout in seconds * * MFI firmware can abort previously issued AEN comamnd (automatic event * notification). The megasas_issue_blocked_abort_cmd() issues such abort * cmd and waits for return status. * Max wait time is MEGASAS_INTERNAL_CMD_WAIT_TIME secs */ static int megasas_issue_blocked_abort_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd_to_abort, int timeout) { … } /** * megasas_make_sgl32 - Prepares 32-bit SGL * @instance: Adapter soft state * @scp: SCSI command from the mid-layer * @mfi_sgl: SGL to be filled in * * If successful, this function returns the number of SG elements. Otherwise, * it returnes -1. */ static int megasas_make_sgl32(struct megasas_instance *instance, struct scsi_cmnd *scp, union megasas_sgl *mfi_sgl) { … } /** * megasas_make_sgl64 - Prepares 64-bit SGL * @instance: Adapter soft state * @scp: SCSI command from the mid-layer * @mfi_sgl: SGL to be filled in * * If successful, this function returns the number of SG elements. Otherwise, * it returnes -1. */ static int megasas_make_sgl64(struct megasas_instance *instance, struct scsi_cmnd *scp, union megasas_sgl *mfi_sgl) { … } /** * megasas_make_sgl_skinny - Prepares IEEE SGL * @instance: Adapter soft state * @scp: SCSI command from the mid-layer * @mfi_sgl: SGL to be filled in * * If successful, this function returns the number of SG elements. Otherwise, * it returnes -1. */ static int megasas_make_sgl_skinny(struct megasas_instance *instance, struct scsi_cmnd *scp, union megasas_sgl *mfi_sgl) { … } /** * megasas_get_frame_count - Computes the number of frames * @frame_type : type of frame- io or pthru frame * @sge_count : number of sg elements * * Returns the number of frames required for numnber of sge's (sge_count) */ static u32 megasas_get_frame_count(struct megasas_instance *instance, u8 sge_count, u8 frame_type) { … } /** * megasas_build_dcdb - Prepares a direct cdb (DCDB) command * @instance: Adapter soft state * @scp: SCSI command * @cmd: Command to be prepared in * * This function prepares CDB commands. These are typcially pass-through * commands to the devices. */ static int megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp, struct megasas_cmd *cmd) { … } /** * megasas_build_ldio - Prepares IOs to logical devices * @instance: Adapter soft state * @scp: SCSI command * @cmd: Command to be prepared * * Frames (and accompanying SGLs) for regular SCSI IOs use this function. */ static int megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp, struct megasas_cmd *cmd) { … } /** * megasas_cmd_type - Checks if the cmd is for logical drive/sysPD * and whether it's RW or non RW * @cmd: SCSI command * */ inline int megasas_cmd_type(struct scsi_cmnd *cmd) { … } /** * megasas_dump_pending_frames - Dumps the frame address of all pending cmds * in FW * @instance: Adapter soft state */ static inline void megasas_dump_pending_frames(struct megasas_instance *instance) { … } u32 megasas_build_and_issue_cmd(struct megasas_instance *instance, struct scsi_cmnd *scmd) { … } /** * megasas_queue_command - Queue entry point * @shost: adapter SCSI host * @scmd: SCSI command to be queued */ static int megasas_queue_command(struct Scsi_Host *shost, struct scsi_cmnd *scmd) { … } static struct megasas_instance *megasas_lookup_instance(u16 host_no) { … } /* * megasas_set_dynamic_target_properties - * Device property set by driver may not be static and it is required to be * updated after OCR * * set tm_capable. * set dma alignment (only for eedp protection enable vd). * * @sdev: OS provided scsi device * * Returns void */ void megasas_set_dynamic_target_properties(struct scsi_device *sdev, struct queue_limits *lim, bool is_target_prop) { … } /* * megasas_set_nvme_device_properties - * set nomerges=2 * set virtual page boundary = 4K (current mr_nvme_pg_size is 4K). * set maximum io transfer = MDTS of NVME device provided by MR firmware. * * MR firmware provides value in KB. Caller of this function converts * kb into bytes. * * e.a MDTS=5 means 2^5 * nvme page size. (In case of 4K page size, * MR firmware provides value 128 as (32 * 4K) = 128K. * * @sdev: scsi device * @max_io_size: maximum io transfer size * */ static inline void megasas_set_nvme_device_properties(struct scsi_device *sdev, struct queue_limits *lim, u32 max_io_size) { … } /* * megasas_set_fw_assisted_qd - * set device queue depth to can_queue * set device queue depth to fw assisted qd * * @sdev: scsi device * @is_target_prop true, if fw provided target properties. */ static void megasas_set_fw_assisted_qd(struct scsi_device *sdev, bool is_target_prop) { … } /* * megasas_set_static_target_properties - * Device property set by driver are static and it is not required to be * updated after OCR. * * set io timeout * set device queue depth * set nvme device properties. see - megasas_set_nvme_device_properties * * @sdev: scsi device * @is_target_prop true, if fw provided target properties. */ static void megasas_set_static_target_properties(struct scsi_device *sdev, struct queue_limits *lim, bool is_target_prop) { … } static int megasas_device_configure(struct scsi_device *sdev, struct queue_limits *lim) { … } static int megasas_slave_alloc(struct scsi_device *sdev) { … } static void megasas_slave_destroy(struct scsi_device *sdev) { … } /* * megasas_complete_outstanding_ioctls - Complete outstanding ioctls after a * kill adapter * @instance: Adapter soft state * */ static void megasas_complete_outstanding_ioctls(struct megasas_instance *instance) { … } void megaraid_sas_kill_hba(struct megasas_instance *instance) { … } /** * megasas_check_and_restore_queue_depth - Check if queue depth needs to be * restored to max value * @instance: Adapter soft state * */ void megasas_check_and_restore_queue_depth(struct megasas_instance *instance) { … } /** * megasas_complete_cmd_dpc - Returns FW's controller structure * @instance_addr: Address of adapter soft state * * Tasklet to complete cmds */ static void megasas_complete_cmd_dpc(unsigned long instance_addr) { … } static void megasas_sriov_heartbeat_handler(struct timer_list *t); /** * megasas_start_timer - Initializes sriov heartbeat timer object * @instance: Adapter soft state * */ void megasas_start_timer(struct megasas_instance *instance) { … } static void megasas_internal_reset_defer_cmds(struct megasas_instance *instance); static void process_fw_state_change_wq(struct work_struct *work); static void megasas_do_ocr(struct megasas_instance *instance) { … } static int megasas_get_ld_vf_affiliation_111(struct megasas_instance *instance, int initial) { … } static int megasas_get_ld_vf_affiliation_12(struct megasas_instance *instance, int initial) { … } /* This function will get the current SR-IOV LD/VF affiliation */ static int megasas_get_ld_vf_affiliation(struct megasas_instance *instance, int initial) { … } /* This function will tell FW to start the SR-IOV heartbeat */ int megasas_sriov_start_heartbeat(struct megasas_instance *instance, int initial) { … } /* Handler for SR-IOV heartbeat */ static void megasas_sriov_heartbeat_handler(struct timer_list *t) { … } /** * megasas_wait_for_outstanding - Wait for all outstanding cmds * @instance: Adapter soft state * * This function waits for up to MEGASAS_RESET_WAIT_TIME seconds for FW to * complete all its outstanding commands. Returns error if one or more IOs * are pending after this time period. It also marks the controller dead. */ static int megasas_wait_for_outstanding(struct megasas_instance *instance) { … } /** * megasas_generic_reset - Generic reset routine * @scmd: Mid-layer SCSI command * * This routine implements a generic reset handler for device, bus and host * reset requests. Device, bus and host specific reset handlers can use this * function after they do their specific tasks. */ static int megasas_generic_reset(struct scsi_cmnd *scmd) { … } /** * megasas_reset_timer - quiesce the adapter if required * @scmd: scsi cmnd * * Sets the FW busy flag and reduces the host->can_queue if the * cmd has not been completed within the timeout period. */ static enum scsi_timeout_action megasas_reset_timer(struct scsi_cmnd *scmd) { … } /** * megasas_dump - This function will print hexdump of provided buffer. * @buf: Buffer to be dumped * @sz: Size in bytes * @format: Different formats of dumping e.g. format=n will * cause only 'n' 32 bit words to be dumped in a single * line. */ inline void megasas_dump(void *buf, int sz, int format) { … } /** * megasas_dump_reg_set - This function will print hexdump of register set * @reg_set: Register set to be dumped */ inline void megasas_dump_reg_set(void __iomem *reg_set) { … } /** * megasas_dump_fusion_io - This function will print key details * of SCSI IO * @scmd: SCSI command pointer of SCSI IO */ void megasas_dump_fusion_io(struct scsi_cmnd *scmd) { … } /* * megasas_dump_sys_regs - This function will dump system registers through * sysfs. * @reg_set: Pointer to System register set. * @buf: Buffer to which output is to be written. * @return: Number of bytes written to buffer. */ static inline ssize_t megasas_dump_sys_regs(void __iomem *reg_set, char *buf) { … } /** * megasas_reset_bus_host - Bus & host reset handler entry point * @scmd: Mid-layer SCSI command */ static int megasas_reset_bus_host(struct scsi_cmnd *scmd) { … } /** * megasas_task_abort - Issues task abort request to firmware * (supported only for fusion adapters) * @scmd: SCSI command pointer */ static int megasas_task_abort(struct scsi_cmnd *scmd) { … } /** * megasas_reset_target: Issues target reset request to firmware * (supported only for fusion adapters) * @scmd: SCSI command pointer */ static int megasas_reset_target(struct scsi_cmnd *scmd) { … } /** * megasas_bios_param - Returns disk geometry for a disk * @sdev: device handle * @bdev: block device * @capacity: drive capacity * @geom: geometry parameters */ static int megasas_bios_param(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[]) { … } static void megasas_map_queues(struct Scsi_Host *shost) { … } static void megasas_aen_polling(struct work_struct *work); /** * megasas_service_aen - Processes an event notification * @instance: Adapter soft state * @cmd: AEN command completed by the ISR * * For AEN, driver sends a command down to FW that is held by the FW till an * event occurs. When an event of interest occurs, FW completes the command * that it was previously holding. * * This routines sends SIGIO signal to processes that have registered with the * driver for AEN. */ static void megasas_service_aen(struct megasas_instance *instance, struct megasas_cmd *cmd) { … } static ssize_t fw_crash_buffer_store(struct device *cdev, struct device_attribute *attr, const char *buf, size_t count) { … } static ssize_t fw_crash_buffer_show(struct device *cdev, struct device_attribute *attr, char *buf) { … } static ssize_t fw_crash_buffer_size_show(struct device *cdev, struct device_attribute *attr, char *buf) { … } static ssize_t fw_crash_state_store(struct device *cdev, struct device_attribute *attr, const char *buf, size_t count) { … } static ssize_t fw_crash_state_show(struct device *cdev, struct device_attribute *attr, char *buf) { … } static ssize_t page_size_show(struct device *cdev, struct device_attribute *attr, char *buf) { … } static ssize_t ldio_outstanding_show(struct device *cdev, struct device_attribute *attr, char *buf) { … } static ssize_t fw_cmds_outstanding_show(struct device *cdev, struct device_attribute *attr, char *buf) { … } static ssize_t enable_sdev_max_qd_show(struct device *cdev, struct device_attribute *attr, char *buf) { … } static ssize_t enable_sdev_max_qd_store(struct device *cdev, struct device_attribute *attr, const char *buf, size_t count) { … } static ssize_t dump_system_regs_show(struct device *cdev, struct device_attribute *attr, char *buf) { … } static ssize_t raid_map_id_show(struct device *cdev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RW(fw_crash_buffer); static DEVICE_ATTR_RO(fw_crash_buffer_size); static DEVICE_ATTR_RW(fw_crash_state); static DEVICE_ATTR_RO(page_size); static DEVICE_ATTR_RO(ldio_outstanding); static DEVICE_ATTR_RO(fw_cmds_outstanding); static DEVICE_ATTR_RW(enable_sdev_max_qd); static DEVICE_ATTR_RO(dump_system_regs); static DEVICE_ATTR_RO(raid_map_id); static struct attribute *megaraid_host_attrs[] = …; ATTRIBUTE_GROUPS(…); /* * Scsi host template for megaraid_sas driver */ static const struct scsi_host_template megasas_template = …; /** * megasas_complete_int_cmd - Completes an internal command * @instance: Adapter soft state * @cmd: Command to be completed * * The megasas_issue_blocked_cmd() function waits for a command to complete * after it issues a command. This function wakes up that waiting routine by * calling wake_up() on the wait queue. */ static void megasas_complete_int_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd) { … } /** * megasas_complete_abort - Completes aborting a command * @instance: Adapter soft state * @cmd: Cmd that was issued to abort another cmd * * The megasas_issue_blocked_abort_cmd() function waits on abort_cmd_wait_q * after it issues an abort on a previously issued command. This function * wakes up all functions waiting on the same wait queue. */ static void megasas_complete_abort(struct megasas_instance *instance, struct megasas_cmd *cmd) { … } static void megasas_set_ld_removed_by_fw(struct megasas_instance *instance) { … } /** * megasas_complete_cmd - Completes a command * @instance: Adapter soft state * @cmd: Command to be completed * @alt_status: If non-zero, use this value as status to * SCSI mid-layer instead of the value returned * by the FW. This should be used if caller wants * an alternate status (as in the case of aborted * commands) */ void megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, u8 alt_status) { … } /** * megasas_issue_pending_cmds_again - issue all pending cmds * in FW again because of the fw reset * @instance: Adapter soft state */ static inline void megasas_issue_pending_cmds_again(struct megasas_instance *instance) { … } /* * Move the internal reset pending commands to a deferred queue. * * We move the commands pending at internal reset time to a * pending queue. This queue would be flushed after successful * completion of the internal reset sequence. if the internal reset * did not complete in time, the kernel reset handler would flush * these commands. */ static void megasas_internal_reset_defer_cmds(struct megasas_instance *instance) { … } static void process_fw_state_change_wq(struct work_struct *work) { … } /** * megasas_deplete_reply_queue - Processes all completed commands * @instance: Adapter soft state * @alt_status: Alternate status to be returned to * SCSI mid-layer instead of the status * returned by the FW * Note: this must be called with hba lock held */ static int megasas_deplete_reply_queue(struct megasas_instance *instance, u8 alt_status) { … } /** * megasas_isr - isr entry point * @irq: IRQ number * @devp: IRQ context address */ static irqreturn_t megasas_isr(int irq, void *devp) { … } /** * megasas_transition_to_ready - Move the FW to READY state * @instance: Adapter soft state * @ocr: Adapter reset state * * During the initialization, FW passes can potentially be in any one of * several possible states. If the FW in operational, waiting-for-handshake * states, driver must take steps to bring it to ready state. Otherwise, it * has to wait for the ready state. */ int megasas_transition_to_ready(struct megasas_instance *instance, int ocr) { … } /** * megasas_teardown_frame_pool - Destroy the cmd frame DMA pool * @instance: Adapter soft state */ static void megasas_teardown_frame_pool(struct megasas_instance *instance) { … } /** * megasas_create_frame_pool - Creates DMA pool for cmd frames * @instance: Adapter soft state * * Each command packet has an embedded DMA memory buffer that is used for * filling MFI frame and the SG list that immediately follows the frame. This * function creates those DMA memory buffers for each command packet by using * PCI pool facility. */ static int megasas_create_frame_pool(struct megasas_instance *instance) { … } /** * megasas_free_cmds - Free all the cmds in the free cmd pool * @instance: Adapter soft state */ void megasas_free_cmds(struct megasas_instance *instance) { … } /** * megasas_alloc_cmds - Allocates the command packets * @instance: Adapter soft state * * Each command that is issued to the FW, whether IO commands from the OS or * internal commands like IOCTLs, are wrapped in local data structure called * megasas_cmd. The frame embedded in this megasas_cmd is actually issued to * the FW. * * Each frame has a 32-bit field called context (tag). This context is used * to get back the megasas_cmd from the frame when a frame gets completed in * the ISR. Typically the address of the megasas_cmd itself would be used as * the context. But we wanted to keep the differences between 32 and 64 bit * systems to the mininum. We always use 32 bit integers for the context. In * this driver, the 32 bit values are the indices into an array cmd_list. * This array is used only to look up the megasas_cmd given the context. The * free commands themselves are maintained in a linked list called cmd_pool. */ int megasas_alloc_cmds(struct megasas_instance *instance) { … } /* * dcmd_timeout_ocr_possible - Check if OCR is possible based on Driver/FW state. * @instance: Adapter soft state * * Return 0 for only Fusion adapter, if driver load/unload is not in progress * or FW is not under OCR. */ inline int dcmd_timeout_ocr_possible(struct megasas_instance *instance) { … } static void megasas_get_pd_info(struct megasas_instance *instance, struct scsi_device *sdev) { … } /* * megasas_get_pd_list_info - Returns FW's pd_list structure * @instance: Adapter soft state * @pd_list: pd_list structure * * Issues an internal command (DCMD) to get the FW's controller PD * list structure. This information is mainly used to find out SYSTEM * supported by the FW. */ static int megasas_get_pd_list(struct megasas_instance *instance) { … } /* * megasas_get_ld_list_info - Returns FW's ld_list structure * @instance: Adapter soft state * @ld_list: ld_list structure * * Issues an internal command (DCMD) to get the FW's controller PD * list structure. This information is mainly used to find out SYSTEM * supported by the FW. */ static int megasas_get_ld_list(struct megasas_instance *instance) { … } /** * megasas_ld_list_query - Returns FW's ld_list structure * @instance: Adapter soft state * @query_type: ld_list structure type * * Issues an internal command (DCMD) to get the FW's controller PD * list structure. This information is mainly used to find out SYSTEM * supported by the FW. */ static int megasas_ld_list_query(struct megasas_instance *instance, u8 query_type) { … } /** * megasas_host_device_list_query * dcmd.opcode - MR_DCMD_CTRL_DEVICE_LIST_GET * dcmd.mbox - reserved * dcmd.sge IN - ptr to return MR_HOST_DEVICE_LIST structure * Desc: This DCMD will return the combined device list * Status: MFI_STAT_OK - List returned successfully * MFI_STAT_INVALID_CMD - Firmware support for the feature has been * disabled * @instance: Adapter soft state * @is_probe: Driver probe check * Return: 0 if DCMD succeeded * non-zero if failed */ static int megasas_host_device_list_query(struct megasas_instance *instance, bool is_probe) { … } /* * megasas_update_ext_vd_details : Update details w.r.t Extended VD * instance : Controller's instance */ static void megasas_update_ext_vd_details(struct megasas_instance *instance) { … } /* * dcmd.opcode - MR_DCMD_CTRL_SNAPDUMP_GET_PROPERTIES * dcmd.hdr.length - number of bytes to read * dcmd.sge - Ptr to MR_SNAPDUMP_PROPERTIES * Desc: Fill in snapdump properties * Status: MFI_STAT_OK- Command successful */ void megasas_get_snapdump_properties(struct megasas_instance *instance) { … } /** * megasas_get_ctrl_info - Returns FW's controller structure * @instance: Adapter soft state * * Issues an internal command (DCMD) to get the FW's controller structure. * This information is mainly used to find out the maximum IO transfer per * command supported by the FW. */ int megasas_get_ctrl_info(struct megasas_instance *instance) { … } /* * megasas_set_crash_dump_params - Sends address of crash dump DMA buffer * to firmware * * @instance: Adapter soft state * @crash_buf_state - tell FW to turn ON/OFF crash dump feature MR_CRASH_BUF_TURN_OFF = 0 MR_CRASH_BUF_TURN_ON = 1 * @return 0 on success non-zero on failure. * Issues an internal command (DCMD) to set parameters for crash dump feature. * Driver will send address of crash dump DMA buffer and set mbox to tell FW * that driver supports crash dump feature. This DCMD will be sent only if * crash dump feature is supported by the FW. * */ int megasas_set_crash_dump_params(struct megasas_instance *instance, u8 crash_buf_state) { … } /** * megasas_issue_init_mfi - Initializes the FW * @instance: Adapter soft state * * Issues the INIT MFI cmd */ static int megasas_issue_init_mfi(struct megasas_instance *instance) { … } static u32 megasas_init_adapter_mfi(struct megasas_instance *instance) { … } static void megasas_setup_irq_poll(struct megasas_instance *instance) { … } /* * megasas_setup_irqs_ioapic - register legacy interrupts. * @instance: Adapter soft state * * Do not enable interrupt, only setup ISRs. * * Return 0 on success. */ static int megasas_setup_irqs_ioapic(struct megasas_instance *instance) { … } /** * megasas_setup_irqs_msix - register MSI-x interrupts. * @instance: Adapter soft state * @is_probe: Driver probe check * * Do not enable interrupt, only setup ISRs. * * Return 0 on success. */ static int megasas_setup_irqs_msix(struct megasas_instance *instance, u8 is_probe) { … } /* * megasas_destroy_irqs- unregister interrupts. * @instance: Adapter soft state * return: void */ static void megasas_destroy_irqs(struct megasas_instance *instance) { … } /** * megasas_setup_jbod_map - setup jbod map for FP seq_number. * @instance: Adapter soft state * * Return 0 on success. */ void megasas_setup_jbod_map(struct megasas_instance *instance) { … } static void megasas_setup_reply_map(struct megasas_instance *instance) { … } /** * megasas_get_device_list - Get the PD and LD device list from FW. * @instance: Adapter soft state * @return: Success or failure * * Issue DCMDs to Firmware to get the PD and LD list. * Based on the FW support, driver sends the HOST_DEVICE_LIST or combination * of PD_LIST/LD_LIST_QUERY DCMDs to get the device list. */ static int megasas_get_device_list(struct megasas_instance *instance) { … } /** * megasas_set_high_iops_queue_affinity_and_hint - Set affinity and hint * for high IOPS queues * @instance: Adapter soft state * return: void */ static inline void megasas_set_high_iops_queue_affinity_and_hint(struct megasas_instance *instance) { … } static int __megasas_alloc_irq_vectors(struct megasas_instance *instance) { … } /** * megasas_alloc_irq_vectors - Allocate IRQ vectors/enable MSI-x vectors * @instance: Adapter soft state * return: void */ static void megasas_alloc_irq_vectors(struct megasas_instance *instance) { … } /** * megasas_init_fw - Initializes the FW * @instance: Adapter soft state * * This is the main function for initializing firmware */ static int megasas_init_fw(struct megasas_instance *instance) { … } /** * megasas_release_mfi - Reverses the FW initialization * @instance: Adapter soft state */ static void megasas_release_mfi(struct megasas_instance *instance) { … } /** * megasas_get_seq_num - Gets latest event sequence numbers * @instance: Adapter soft state * @eli: FW event log sequence numbers information * * FW maintains a log of all events in a non-volatile area. Upper layers would * usually find out the latest sequence number of the events, the seq number at * the boot etc. They would "read" all the events below the latest seq number * by issuing a direct fw cmd (DCMD). For the future events (beyond latest seq * number), they would subsribe to AEN (asynchronous event notification) and * wait for the events to happen. */ static int megasas_get_seq_num(struct megasas_instance *instance, struct megasas_evt_log_info *eli) { … } /** * megasas_register_aen - Registers for asynchronous event notification * @instance: Adapter soft state * @seq_num: The starting sequence number * @class_locale_word: Class of the event * * This function subscribes for AEN for events beyond the @seq_num. It requests * to be notified if and only if the event is of type @class_locale */ static int megasas_register_aen(struct megasas_instance *instance, u32 seq_num, u32 class_locale_word) { … } /* megasas_get_target_prop - Send DCMD with below details to firmware. * * This DCMD will fetch few properties of LD/system PD defined * in MR_TARGET_DEV_PROPERTIES. eg. Queue Depth, MDTS value. * * DCMD send by drivers whenever new target is added to the OS. * * dcmd.opcode - MR_DCMD_DEV_GET_TARGET_PROP * dcmd.mbox.b[0] - DCMD is to be fired for LD or system PD. * 0 = system PD, 1 = LD. * dcmd.mbox.s[1] - TargetID for LD/system PD. * dcmd.sge IN - Pointer to return MR_TARGET_DEV_PROPERTIES. * * @instance: Adapter soft state * @sdev: OS provided scsi device * * Returns 0 on success non-zero on failure. */ int megasas_get_target_prop(struct megasas_instance *instance, struct scsi_device *sdev) { … } /** * megasas_start_aen - Subscribes to AEN during driver load time * @instance: Adapter soft state */ static int megasas_start_aen(struct megasas_instance *instance) { … } /** * megasas_io_attach - Attaches this driver to SCSI mid-layer * @instance: Adapter soft state */ static int megasas_io_attach(struct megasas_instance *instance) { … } /** * megasas_set_dma_mask - Set DMA mask for supported controllers * * @instance: Adapter soft state * Description: * * For Ventura, driver/FW will operate in 63bit DMA addresses. * * For invader- * By default, driver/FW will operate in 32bit DMA addresses * for consistent DMA mapping but if 32 bit consistent * DMA mask fails, driver will try with 63 bit consistent * mask provided FW is true 63bit DMA capable * * For older controllers(Thunderbolt and MFI based adapters)- * driver/FW will operate in 32 bit consistent DMA addresses. */ static int megasas_set_dma_mask(struct megasas_instance *instance) { … } /* * megasas_set_adapter_type - Set adapter type. * Supported controllers can be divided in * different categories- * enum MR_ADAPTER_TYPE { * MFI_SERIES = 1, * THUNDERBOLT_SERIES = 2, * INVADER_SERIES = 3, * VENTURA_SERIES = 4, * AERO_SERIES = 5, * }; * @instance: Adapter soft state * return: void */ static inline void megasas_set_adapter_type(struct megasas_instance *instance) { … } static inline int megasas_alloc_mfi_ctrl_mem(struct megasas_instance *instance) { … } /** * megasas_alloc_ctrl_mem - Allocate per controller memory for core data * structures which are not common across MFI * adapters and fusion adapters. * For MFI based adapters, allocate producer and * consumer buffers. For fusion adapters, allocate * memory for fusion context. * @instance: Adapter soft state * return: 0 for SUCCESS */ static int megasas_alloc_ctrl_mem(struct megasas_instance *instance) { … } /* * megasas_free_ctrl_mem - Free fusion context for fusion adapters and * producer, consumer buffers for MFI adapters * * @instance - Adapter soft instance * */ static inline void megasas_free_ctrl_mem(struct megasas_instance *instance) { … } /** * megasas_alloc_ctrl_dma_buffers - Allocate consistent DMA buffers during * driver load time * * @instance: Adapter soft instance * * @return: O for SUCCESS */ static inline int megasas_alloc_ctrl_dma_buffers(struct megasas_instance *instance) { … } /* * megasas_free_ctrl_dma_buffers - Free consistent DMA buffers allocated * during driver load time * * @instance- Adapter soft instance * */ static inline void megasas_free_ctrl_dma_buffers(struct megasas_instance *instance) { … } /* * megasas_init_ctrl_params - Initialize controller's instance * parameters before FW init * @instance - Adapter soft instance * @return - void */ static inline void megasas_init_ctrl_params(struct megasas_instance *instance) { … } /** * megasas_probe_one - PCI hotplug entry point * @pdev: PCI device structure * @id: PCI ids of supported hotplugged adapter */ static int megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) { … } /** * megasas_flush_cache - Requests FW to flush all its caches * @instance: Adapter soft state */ static void megasas_flush_cache(struct megasas_instance *instance) { … } /** * megasas_shutdown_controller - Instructs FW to shutdown the controller * @instance: Adapter soft state * @opcode: Shutdown/Hibernate */ static void megasas_shutdown_controller(struct megasas_instance *instance, u32 opcode) { … } /** * megasas_suspend - driver suspend entry point * @dev: Device structure */ static int __maybe_unused megasas_suspend(struct device *dev) { … } /** * megasas_resume- driver resume entry point * @dev: Device structure */ static int __maybe_unused megasas_resume(struct device *dev) { … } static inline int megasas_wait_for_adapter_operational(struct megasas_instance *instance) { … } /** * megasas_detach_one - PCI hot"un"plug entry point * @pdev: PCI device structure */ static void megasas_detach_one(struct pci_dev *pdev) { … } /** * megasas_shutdown - Shutdown entry point * @pdev: PCI device structure */ static void megasas_shutdown(struct pci_dev *pdev) { … } /* * megasas_mgmt_open - char node "open" entry point * @inode: char node inode * @filep: char node file */ static int megasas_mgmt_open(struct inode *inode, struct file *filep) { … } /* * megasas_mgmt_fasync - Async notifier registration from applications * @fd: char node file descriptor number * @filep: char node file * @mode: notifier on/off * * This function adds the calling process to a driver global queue. When an * event occurs, SIGIO will be sent to all processes in this queue. */ static int megasas_mgmt_fasync(int fd, struct file *filep, int mode) { … } /* * megasas_mgmt_poll - char node "poll" entry point * @filep: char node file * @wait: Events to poll for */ static __poll_t megasas_mgmt_poll(struct file *file, poll_table *wait) { … } /* * megasas_set_crash_dump_params_ioctl: * Send CRASH_DUMP_MODE DCMD to all controllers * @cmd: MFI command frame */ static int megasas_set_crash_dump_params_ioctl(struct megasas_cmd *cmd) { … } /** * megasas_mgmt_fw_ioctl - Issues management ioctls to FW * @instance: Adapter soft state * @user_ioc: User's ioctl packet * @ioc: ioctl packet */ static int megasas_mgmt_fw_ioctl(struct megasas_instance *instance, struct megasas_iocpacket __user * user_ioc, struct megasas_iocpacket *ioc) { … } static struct megasas_iocpacket * megasas_compat_iocpacket_get_user(void __user *arg) { … } static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg) { … } static int megasas_mgmt_ioctl_aen(struct file *file, unsigned long arg) { … } /** * megasas_mgmt_ioctl - char node ioctl entry point * @file: char device file pointer * @cmd: ioctl command * @arg: ioctl command arguments address */ static long megasas_mgmt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { … } #ifdef CONFIG_COMPAT static long megasas_mgmt_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { … } #endif /* * File operations structure for management interface */ static const struct file_operations megasas_mgmt_fops = …; static SIMPLE_DEV_PM_OPS(megasas_pm_ops, megasas_suspend, megasas_resume); /* * PCI hotplug support registration structure */ static struct pci_driver megasas_pci_driver = …; /* * Sysfs driver attributes */ static ssize_t version_show(struct device_driver *dd, char *buf) { … } static DRIVER_ATTR_RO(version); static ssize_t release_date_show(struct device_driver *dd, char *buf) { … } static DRIVER_ATTR_RO(release_date); static ssize_t support_poll_for_event_show(struct device_driver *dd, char *buf) { … } static DRIVER_ATTR_RO(support_poll_for_event); static ssize_t support_device_change_show(struct device_driver *dd, char *buf) { … } static DRIVER_ATTR_RO(support_device_change); static ssize_t dbg_lvl_show(struct device_driver *dd, char *buf) { … } static ssize_t dbg_lvl_store(struct device_driver *dd, const char *buf, size_t count) { … } static DRIVER_ATTR_RW(dbg_lvl); static ssize_t support_nvme_encapsulation_show(struct device_driver *dd, char *buf) { … } static DRIVER_ATTR_RO(support_nvme_encapsulation); static ssize_t support_pci_lane_margining_show(struct device_driver *dd, char *buf) { … } static DRIVER_ATTR_RO(support_pci_lane_margining); static inline void megasas_remove_scsi_device(struct scsi_device *sdev) { … } /** * megasas_update_device_list - Update the PD and LD device list from FW * after an AEN event notification * @instance: Adapter soft state * @event_type: Indicates type of event (PD or LD event) * * @return: Success or failure * * Issue DCMDs to Firmware to update the internal device list in driver. * Based on the FW support, driver sends the HOST_DEVICE_LIST or combination * of PD_LIST/LD_LIST_QUERY DCMDs to get the device list. */ static int megasas_update_device_list(struct megasas_instance *instance, int event_type) { … } /** * megasas_add_remove_devices - Add/remove devices to SCSI mid-layer * after an AEN event notification * @instance: Adapter soft state * @scan_type: Indicates type of devices (PD/LD) to add * @return void */ static void megasas_add_remove_devices(struct megasas_instance *instance, int scan_type) { … } static void megasas_aen_polling(struct work_struct *work) { … } /** * megasas_init - Driver load entry point */ static int __init megasas_init(void) { … } /** * megasas_exit - Driver unload entry point */ static void __exit megasas_exit(void) { … } module_init(…) …; module_exit(megasas_exit);