diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_cnfg.h b/drivers/scsi/mpi3mr/mpi/mpi30_cnfg.h index 33dd303c97bb..7cf16a5c15b7 100644 --- a/drivers/scsi/mpi3mr/mpi/mpi30_cnfg.h +++ b/drivers/scsi/mpi3mr/mpi/mpi30_cnfg.h @@ -72,6 +72,12 @@ #define MPI3_SECURITY_PGAD_SLOT_GROUP_SHIFT (8) #define MPI3_SECURITY_PGAD_SLOT_MASK (0x000000ff) #define MPI3_INSTANCE_PGAD_INSTANCE_MASK (0x0000ffff) +#define MPI3_INSTANCE_PGAD_INSTANCE_SHIFT (0) +#define MPI3_INTERFACE_PGAD_INTERFACE_MASK (0x0000000f) +#define MPI3_INTERFACE_PGAD_INTERFACE_SHIFT (0) +#define MPI3_INTERFACE_PGAD_INTERFACE_MPI (0) +#define MPI3_INTERFACE_PGAD_INTERFACE_NVME_VD (1) +#define MPI3_INTERFACE_PGAD_INTERFACE_NVME_PD (2) struct mpi3_config_request { __le16 host_tag; u8 ioc_use_only02; @@ -492,10 +498,31 @@ struct mpi3_man10_istwi_ctrlr_entry { }; #define MPI3_MAN10_ISTWI_CTRLR_FLAGS_BUS_SPEED_MASK (0x000c) -#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_BUS_SPEED_100K (0x0000) -#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_BUS_SPEED_400K (0x0004) +#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_BUS_SPEED_100_KHZ (0x0000) +#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_BUS_SPEED_400_KHZ (0x0004) #define MPI3_MAN10_ISTWI_CTRLR_FLAGS_TARGET_ENABLED (0x0002) #define MPI3_MAN10_ISTWI_CTRLR_FLAGS_INITIATOR_ENABLED (0x0001) +#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_I2C_GLITCH_FLTR_MASK (0xc000) +#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_I2C_GLITCH_FLTR_SHIFT (14) +#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_I2C_GLITCH_FLTR_50_NS (0x0000) +#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_I2C_GLITCH_FLTR_10_NS (0x4000) +#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_I2C_GLITCH_FLTR_5_NS (0x8000) +#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_I2C_GLITCH_FLTR_0_NS (0xc000) +#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_BUS_TYPE_MASK (0x3000) +#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_BUS_TYPE_SHIFT (12) +#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_BUS_TYPE_I2C (0x0000) +#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_BUS_TYPE_I3C (0x1000) +#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_BUS_TYPE_AUTO (0x2000) +#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_I3C_MAX_DATA_RATE_MASK (0x0e00) +#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_I3C_MAX_DATA_RATE_SHIFT (9) +#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_I3C_MAX_DATA_RATE_12_5_MHZ (0x0000) +#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_I3C_MAX_DATA_RATE_8_MHZ (0x0200) +#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_I3C_MAX_DATA_RATE_6_MHZ (0x0400) +#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_I3C_MAX_DATA_RATE_4_MHZ (0x0600) +#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_I3C_MAX_DATA_RATE_2_MHZ (0x0800) +#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_BUS_SPEED_MASK (0x000c) +#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_BUS_SPEED_SHIFT (0) + #ifndef MPI3_MAN10_ISTWI_CTRLR_MAX #define MPI3_MAN10_ISTWI_CTRLR_MAX (1) #endif @@ -1027,6 +1054,16 @@ struct mpi3_io_unit_page5 { #define MPI3_IOUNIT5_DEVICE_SHUTDOWN_SWITCH_ATTACHED (0x02) #define MPI3_IOUNIT5_DEVICE_SHUTDOWN_DIRECT_AND_EXPANDER (0x03) #define MPI3_IOUNIT5_DEVICE_SHUTDOWN_DIRECT_AND_SWITCH (0x03) +#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_HDD_SPINDOWN_MASK (0xc000) +#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_HDD_SPINDOWN_NONE (0x0000) +#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_HDD_SPINDOWN_ALL (0x4000) +#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_HDD_SPINDOWN_FILTERED (0x8000) +#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_HDD_SPINDOWN_RESERVED (0xc000) +#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_SYNC_CACHE_MASK (0x3000) +#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_SYNC_CACHE_ALL (0x0000) +#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_SYNC_CACHE_FILTERED (0x1000) +#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_SYNC_CACHE_NONE (0x2000) +#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_SYNC_CACHE_RESERVED (0x3000) #define MPI3_IOUNIT5_DEVICE_SHUTDOWN_SATA_HDD_MASK (0x0300) #define MPI3_IOUNIT5_DEVICE_SHUTDOWN_SATA_HDD_SHIFT (8) #define MPI3_IOUNIT5_DEVICE_SHUTDOWN_SAS_HDD_MASK (0x00c0) @@ -1069,7 +1106,8 @@ struct mpi3_io_unit_page8 { struct mpi3_config_page_header header; u8 sb_mode; u8 sb_state; - __le16 reserved0a; + u8 flags; + u8 reserved0b; u8 num_slots; u8 slots_available; u8 current_key_encryption_algo; @@ -1088,6 +1126,8 @@ struct mpi3_io_unit_page8 { #define MPI3_IOUNIT8_SBSTATE_SVN_UPDATE_PENDING (0x04) #define MPI3_IOUNIT8_SBSTATE_KEY_UPDATE_PENDING (0x02) #define MPI3_IOUNIT8_SBSTATE_SECURE_BOOT_ENABLED (0x01) +#define MPI3_IOUNIT8_FLAGS_FWQR_CAPABLE (0x80) +#define MPI3_IOUNIT8_FLAGS_FWQR_SECURED (0x40) #define MPI3_IOUNIT8_SBMODE_CURRENT_KEY_IOUNIT17 (0x10) #define MPI3_IOUNIT8_SBMODE_HARD_SECURE_RECERTIFIED (0x08) struct mpi3_io_unit_page9 { @@ -1174,10 +1214,16 @@ struct mpi3_io_unit_page12 { #define MPI3_IOUNIT12_FLAGS_NUMPASSES_32 (0x00000200) #define MPI3_IOUNIT12_FLAGS_NUMPASSES_64 (0x00000300) #define MPI3_IOUNIT12_FLAGS_PASSPERIOD_MASK (0x00000003) +#define MPI3_IOUNIT12_FLAGS_PASSPERIOD_SHIFT (0) #define MPI3_IOUNIT12_FLAGS_PASSPERIOD_DISABLED (0x00000000) #define MPI3_IOUNIT12_FLAGS_PASSPERIOD_500US (0x00000001) #define MPI3_IOUNIT12_FLAGS_PASSPERIOD_1MS (0x00000002) #define MPI3_IOUNIT12_FLAGS_PASSPERIOD_2MS (0x00000003) +#define MPI3_IOUNIT12_FLAGS_INTERFACE_MASK (0x0000000c) +#define MPI3_IOUNIT12_FLAGS_INTERFACE_SHIFT (2) +#define MPI3_IOUNIT12_FLAGS_INTERFACE_MPI (0x00000000) +#define MPI3_IOUNIT12_FLAGS_INTERFACE_NVME_VD (0x00000004) +#define MPI3_IOUNIT12_FLAGS_INTERFACE_NVME_PD (0x00000008) #ifndef MPI3_IOUNIT13_FUNC_MAX #define MPI3_IOUNIT13_FUNC_MAX (1) #endif @@ -1238,6 +1284,7 @@ struct mpi3_io_unit_page15 { #define MPI3_IOUNIT15_PAGEVERSION (0x00) #define MPI3_IOUNIT15_FLAGS_EPRINIT_INITREQUIRED (0x04) #define MPI3_IOUNIT15_FLAGS_EPRSUPPORT_MASK (0x03) +#define MPI3_IOUNIT15_FLAGS_EPRSUPPORT_SHIFT (0) #define MPI3_IOUNIT15_FLAGS_EPRSUPPORT_NOT_SUPPORTED (0x00) #define MPI3_IOUNIT15_FLAGS_EPRSUPPORT_WITHOUT_POWER_BRAKE_GPIO (0x01) #define MPI3_IOUNIT15_FLAGS_EPRSUPPORT_WITH_POWER_BRAKE_GPIO (0x02) @@ -1255,6 +1302,9 @@ struct mpi3_io_unit_page17 { __le32 current_key[]; }; #define MPI3_IOUNIT17_PAGEVERSION (0x00) +#define MPI3_IOUNIT17_FLAGS_KEYROOT_MASK (0x01) +#define MPI3_IOUNIT17_FLAGS_KEYROOT_HW (0x00) +#define MPI3_IOUNIT17_FLAGS_KEYROOT_FW (0x01) struct mpi3_io_unit_page18 { struct mpi3_config_page_header header; u8 flags; @@ -1640,11 +1690,28 @@ struct mpi3_security_page3 { }; #define MPI3_SECURITY3_PAGEVERSION (0x00) -#define MPI3_SECURITY3_FLAGS_TYPE_MASK (0x0f) +#define MPI3_SECURITY3_FLAGS_TYPE_MASK (0x1f) #define MPI3_SECURITY3_FLAGS_TYPE_SHIFT (0) #define MPI3_SECURITY3_FLAGS_TYPE_NOT_VALID (0) #define MPI3_SECURITY3_FLAGS_TYPE_MLDSA_PRIVATE (1) #define MPI3_SECURITY3_FLAGS_TYPE_MLDSA_PUBLIC (2) +union mpi3_security_digest { + __le32 dword[16]; + __le16 word[32]; + u8 byte[64]; +}; +struct mpi3_security_page4 { + struct mpi3_config_page_header header; + __le32 reserved08[2]; + union mpi3_security_mac mac; + union mpi3_security_nonce nonce; + u8 num_digests; + u8 hash_algorithm; + __le16 reserved92; + __le32 reserved94[3]; + union mpi3_security_digest digest[]; +}; +#define MPI3_SECURITY4_PAGEVERSION (0x00) struct mpi3_security_page10 { struct mpi3_config_page_header header; __le32 reserved08[2]; @@ -2074,7 +2141,7 @@ struct mpi3_sas_phy3_phy_event_config { #define MPI3_SASPHY3_EVENT_CODE_LCCONN_TIME (0xd5) #define MPI3_SASPHY3_EVENT_CODE_SSP_TX_START_TRANSMIT (0xd6) #define MPI3_SASPHY3_EVENT_CODE_SATA_TX_START (0xd7) -#define MPI3_SASPHY3_EVENT_CODE_SMP_TX_START_TRANSMT (0xd8) +#define MPI3_SASPHY3_EVENT_CODE_SMP_TX_START_TRANSMIT (0xd8) #define MPI3_SASPHY3_EVENT_CODE_TX_SMP_BREAK_CONN (0xd9) #define MPI3_SASPHY3_EVENT_CODE_SSP_RX_START_RECEIVE (0xda) #define MPI3_SASPHY3_EVENT_CODE_SATA_RX_START_RECEIVE (0xdb) diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_image.h b/drivers/scsi/mpi3mr/mpi/mpi30_image.h index 62ddf094d46c..5fa09fa79358 100644 --- a/drivers/scsi/mpi3mr/mpi/mpi30_image.h +++ b/drivers/scsi/mpi3mr/mpi/mpi30_image.h @@ -18,7 +18,7 @@ struct mpi3_hash_exclusion_format { __le32 size; }; -#define MPI3_IMAGE_HASH_EXCUSION_NUM (4) +#define MPI3_IMAGE_HASH_EXCLUSION_NUM (4) struct mpi3_component_image_header { __le32 signature0; __le32 load_address; @@ -42,7 +42,7 @@ struct mpi3_component_image_header { union mpi3_version_union rmc_interface_version; union mpi3_version_union etp_interface_version; struct mpi3_comp_image_version component_image_version; - struct mpi3_hash_exclusion_format hash_exclusion[MPI3_IMAGE_HASH_EXCUSION_NUM]; + struct mpi3_hash_exclusion_format hash_exclusion[MPI3_IMAGE_HASH_EXCLUSION_NUM]; __le32 next_image_header_offset; union mpi3_version_union security_version; __le32 reserved84[31]; @@ -347,7 +347,8 @@ struct mpi3_encrypted_hash_entry { struct mpi3_encrypted_hash_data { u8 image_version; u8 num_hash; - __le16 reserved02; + u8 fw_num_hash; + u8 reserved03; __le32 reserved04; struct mpi3_encrypted_hash_entry encrypted_hash_entry[MPI3_ENCRYPTED_HASH_ENTRY_MAX]; }; diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_ioc.h b/drivers/scsi/mpi3mr/mpi/mpi30_ioc.h index 68efa0d51345..aa42fba7f930 100644 --- a/drivers/scsi/mpi3mr/mpi/mpi30_ioc.h +++ b/drivers/scsi/mpi3mr/mpi/mpi30_ioc.h @@ -428,10 +428,10 @@ struct mpi3_event_data_sas_discovery { #define MPI3_EVENT_SAS_DISC_FLAGS_IN_PROGRESS (0x01) #define MPI3_EVENT_SAS_DISC_RC_STARTED (0x01) #define MPI3_EVENT_SAS_DISC_RC_COMPLETED (0x02) -#define MPI3_SAS_DISC_STATUS_MAX_ENCLOSURES_EXCEED (0x80000000) -#define MPI3_SAS_DISC_STATUS_MAX_EXPANDERS_EXCEED (0x40000000) -#define MPI3_SAS_DISC_STATUS_MAX_DEVICES_EXCEED (0x20000000) -#define MPI3_SAS_DISC_STATUS_MAX_TOPO_PHYS_EXCEED (0x10000000) +#define MPI3_SAS_DISC_STATUS_MAX_ENCLOSURES_EXCEEDED (0x80000000) +#define MPI3_SAS_DISC_STATUS_MAX_EXPANDERS_EXCEEDED (0x40000000) +#define MPI3_SAS_DISC_STATUS_MAX_DEVICES_EXCEEDED (0x20000000) +#define MPI3_SAS_DISC_STATUS_MAX_TOPO_PHYS_EXCEEDED (0x10000000) #define MPI3_SAS_DISC_STATUS_INVALID_CEI (0x00010000) #define MPI3_SAS_DISC_STATUS_FECEI_MISMATCH (0x00008000) #define MPI3_SAS_DISC_STATUS_MULTIPLE_DEVICES_IN_SLOT (0x00004000) @@ -965,7 +965,7 @@ struct mpi3_ci_download_reply { u8 flags; u8 cache_dirty; u8 pending_count; - u8 reserved13; + u8 additional_flags; }; #define MPI3_CI_DOWNLOAD_FLAGS_DOWNLOAD_IN_PROGRESS (0x80) @@ -979,6 +979,11 @@ struct mpi3_ci_download_reply { #define MPI3_CI_DOWNLOAD_FLAGS_ACTIVATION_STATUS_ONLINE_PENDING (0x04) #define MPI3_CI_DOWNLOAD_FLAGS_ACTIVATION_STATUS_OFFLINE_PENDING (0x06) #define MPI3_CI_DOWNLOAD_FLAGS_COMPATIBLE (0x01) +#define MPI3_CI_DOWNLOAD_ADDITIONALFLAGS_REDUNDANCYRESTORATION_MASK (0x03) +#define MPI3_CI_DOWNLOAD_ADDITIONALFLAGS_REDUNDANCYRESTORATION_SHIFT (0) +#define MPI3_CI_DOWNLOAD_ADDITIONALFLAGS_REDUNDANCYRESTORATION_NONE (0x00) +#define MPI3_CI_DOWNLOAD_ADDITIONALFLAGS_REDUNDANCYRESTORATION_PRIMARY (0x01) +#define MPI3_CI_DOWNLOAD_ADDITIONALFLAGS_REDUNDANCYRESTORATION_SECONDARY (0x02) struct mpi3_ci_upload_request { __le16 host_tag; u8 ioc_use_only02; diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_transport.h b/drivers/scsi/mpi3mr/mpi/mpi30_transport.h index 290a1f5c2924..794ecc778945 100644 --- a/drivers/scsi/mpi3mr/mpi/mpi30_transport.h +++ b/drivers/scsi/mpi3mr/mpi/mpi30_transport.h @@ -18,7 +18,7 @@ union mpi3_version_union { #define MPI3_VERSION_MAJOR (3) #define MPI3_VERSION_MINOR (0) -#define MPI3_VERSION_UNIT (39) +#define MPI3_VERSION_UNIT (41) #define MPI3_VERSION_DEV (0) #define MPI3_DEVHANDLE_INVALID (0xffff) struct mpi3_sysif_oper_queue_indexes { diff --git a/drivers/scsi/mpi3mr/mpi3mr.h b/drivers/scsi/mpi3mr/mpi3mr.h index c25525fe0671..d6e16707fd97 100644 --- a/drivers/scsi/mpi3mr/mpi3mr.h +++ b/drivers/scsi/mpi3mr/mpi3mr.h @@ -56,8 +56,8 @@ extern struct list_head mrioc_list; extern int prot_mask; extern atomic64_t event_counter; -#define MPI3MR_DRIVER_VERSION "8.17.0.3.50" -#define MPI3MR_DRIVER_RELDATE "09-January-2026" +#define MPI3MR_DRIVER_VERSION "8.18.0.8.50" +#define MPI3MR_DRIVER_RELDATE "26-June-2026" #define MPI3MR_DRIVER_NAME "mpi3mr" #define MPI3MR_DRIVER_LICENSE "GPL" @@ -125,6 +125,7 @@ extern atomic64_t event_counter; #define MPI3MR_RESETTM_TIMEOUT 60 #define MPI3MR_RESET_HOST_IOWAIT_TIMEOUT 5 #define MPI3MR_TSUPDATE_INTERVAL 900 +#define MPI3MR_EARLY_TSUPDATE_SECONDS 60 #define MPI3MR_DEFAULT_SHUTDOWN_TIME 120 #define MPI3MR_RAID_ERRREC_RESET_TIMEOUT 180 #define MPI3MR_PREPARE_FOR_RESET_TIMEOUT 180 @@ -168,6 +169,7 @@ extern atomic64_t event_counter; #define MPI3MR_DEFAULT_MDTS (128 * 1024) #define MPI3MR_DEFAULT_PGSZEXP (12) +#define MPI3MR_MAX_PGSZEXP (27) /* Command retry count definitions */ #define MPI3MR_DEV_RMHS_RETRY_COUNT 3 @@ -177,7 +179,7 @@ extern atomic64_t event_counter; #define MPI3MR_DEFAULT_SDEV_QD 32 /* Definitions for Threaded IRQ poll*/ -#define MPI3MR_IRQ_POLL_SLEEP 20 +#define MPI3MR_IRQ_POLL_SLEEP 2 #define MPI3MR_IRQ_POLL_TRIGGER_IOCOUNT 8 /* Definitions for the controller security status*/ @@ -1118,6 +1120,7 @@ struct scmd_priv { * @evtack_cmds_bitmap: Event Ack bitmap * @delayed_evtack_cmds_list: Delayed event acknowledgment list * @ts_update_counter: Timestamp update counter + * @early_ts_sync_done: Early (1 min) timestamp sync completed after load * @ts_update_interval: Timestamp update interval * @reset_in_progress: Reset in progress flag * @unrecoverable: Controller unrecoverable flag @@ -1318,6 +1321,7 @@ struct mpi3mr_ioc { struct list_head delayed_evtack_cmds_list; u16 ts_update_counter; + u8 early_ts_sync_done; u16 ts_update_interval; u8 reset_in_progress; u8 unrecoverable; @@ -1410,6 +1414,9 @@ struct mpi3mr_ioc { struct dma_pool *trace_buf_pool; struct segments *trace_buf; u8 invalid_io_comp; + bool is_unload; + bool skip_dev_shutdown_on_unload; + }; diff --git a/drivers/scsi/mpi3mr/mpi3mr_app.c b/drivers/scsi/mpi3mr/mpi3mr_app.c index 0cdcb8d236d3..5184d9d516a8 100644 --- a/drivers/scsi/mpi3mr/mpi3mr_app.c +++ b/drivers/scsi/mpi3mr/mpi3mr_app.c @@ -141,25 +141,27 @@ void mpi3mr_alloc_diag_bufs(struct mpi3mr_ioc *mrioc) trace_min_size = fw_min_size = MPI3MR_DEFAULT_HDB_MIN_SZ; } else { - trace_size = driver_pg1.host_diag_trace_max_size * 1024; - trace_dec_size = driver_pg1.host_diag_trace_decrement_size + trace_size = le16_to_cpu(driver_pg1.host_diag_trace_max_size) * 1024; + trace_dec_size = le16_to_cpu(driver_pg1.host_diag_trace_decrement_size) * 1024; - trace_min_size = driver_pg1.host_diag_trace_min_size * 1024; - fw_size = driver_pg1.host_diag_fw_max_size * 1024; - fw_dec_size = driver_pg1.host_diag_fw_decrement_size * 1024; - fw_min_size = driver_pg1.host_diag_fw_min_size * 1024; + trace_min_size = le16_to_cpu(driver_pg1.host_diag_trace_min_size) * 1024; + fw_size = le16_to_cpu(driver_pg1.host_diag_fw_max_size) * 1024; + fw_dec_size = le16_to_cpu(driver_pg1.host_diag_fw_decrement_size) * 1024; + fw_min_size = le16_to_cpu(driver_pg1.host_diag_fw_min_size) * 1024; dprint_init(mrioc, "%s:trace diag buffer sizes read from driver\n" "page1: maximum size = %dKB, decrement size = %dKB\n" - ", minimum size = %dKB\n", __func__, driver_pg1.host_diag_trace_max_size, - driver_pg1.host_diag_trace_decrement_size, - driver_pg1.host_diag_trace_min_size); + ", minimum size = %dKB\n", __func__, + le16_to_cpu(driver_pg1.host_diag_trace_max_size), + le16_to_cpu(driver_pg1.host_diag_trace_decrement_size), + le16_to_cpu(driver_pg1.host_diag_trace_min_size)); dprint_init(mrioc, "%s:firmware diag buffer sizes read from driver\n" "page1: maximum size = %dKB, decrement size = %dKB\n" - ", minimum size = %dKB\n", __func__, driver_pg1.host_diag_fw_max_size, - driver_pg1.host_diag_fw_decrement_size, - driver_pg1.host_diag_fw_min_size); + ", minimum size = %dKB\n", __func__, + le16_to_cpu(driver_pg1.host_diag_fw_max_size), + le16_to_cpu(driver_pg1.host_diag_fw_decrement_size), + le16_to_cpu(driver_pg1.host_diag_fw_min_size)); if ((trace_size == 0) && (fw_size == 0)) return; } @@ -179,6 +181,12 @@ void mpi3mr_alloc_diag_bufs(struct mpi3mr_ioc *mrioc) mpi3mr_alloc_trace_buffer(mrioc, trace_size)) { retry = true; + + if (!trace_dec_size || trace_dec_size > trace_size) { + retry = false; + goto retry_fw; + } + trace_size -= trace_dec_size; dprint_init(mrioc, "trace diag buffer allocation failed\n" "retrying smaller size %dKB\n", trace_size / 1024); @@ -211,11 +219,13 @@ void mpi3mr_alloc_diag_bufs(struct mpi3mr_ioc *mrioc) diag_buffer->size = fw_size; } else { retry = true; - fw_size -= fw_dec_size; - dprint_init(mrioc, "%s:trace diag buffer allocation failed,\n" - "retrying smaller size %dKB\n", - __func__, fw_size / 1024); - goto retry_fw; + if (fw_dec_size && fw_dec_size <= fw_size) { + fw_size -= fw_dec_size; + dprint_init(mrioc, "%s:trace diag buffer allocation failed,\n" + "retrying smaller size %dKB\n", + __func__, fw_size / 1024); + goto retry_fw; + } } } } diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c index 61a1592f37d7..f0d3cd398dd0 100644 --- a/drivers/scsi/mpi3mr/mpi3mr_fw.c +++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c @@ -490,6 +490,12 @@ int mpi3mr_process_admin_reply_q(struct mpi3mr_ioc *mrioc) return 0; } + /* + * Ensure that the descriptor payload is read only after + * the phase bit check is complete. + */ + dma_rmb(); + do { if (mrioc->unrecoverable || mrioc->io_admin_reset_sync) break; @@ -510,6 +516,13 @@ int mpi3mr_process_admin_reply_q(struct mpi3mr_ioc *mrioc) if ((le16_to_cpu(reply_desc->reply_flags) & MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase) break; + + /* + * Ensure that the descriptor payload is read only after + * the phase bit check is complete. + */ + dma_rmb(); + if (threshold_comps == MPI3MR_THRESHOLD_REPLY_COUNT) { writel(admin_reply_ci, &mrioc->sysif_regs->admin_reply_queue_ci); @@ -570,6 +583,9 @@ int mpi3mr_process_op_reply_q(struct mpi3mr_ioc *mrioc, struct mpi3_default_reply_descriptor *reply_desc; u16 req_q_idx = 0, reply_qidx, threshold_comps = 0; + if (!op_reply_q) + return 0; + reply_qidx = op_reply_q->qid - 1; if (!atomic_add_unless(&op_reply_q->in_use, 1, 1)) @@ -581,15 +597,33 @@ int mpi3mr_process_op_reply_q(struct mpi3mr_ioc *mrioc, reply_desc = mpi3mr_get_reply_desc(op_reply_q, reply_ci); if ((le16_to_cpu(reply_desc->reply_flags) & MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase) { + /* Recheck under in_use before releasing, to avoid a reclaim race */ + dma_rmb(); + if ((le16_to_cpu(reply_desc->reply_flags) & + MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) == exp_phase) + goto process_desc; atomic_dec(&op_reply_q->in_use); return 0; } +process_desc: + /* + * Ensure that the descriptor payload is read only after + * the phase bit check is complete. + */ + dma_rmb(); do { if (mrioc->unrecoverable || mrioc->io_admin_reset_sync) break; req_q_idx = le16_to_cpu(reply_desc->request_queue_id) - 1; + + if (unlikely(req_q_idx >= mrioc->num_op_req_q)) { + ioc_err(mrioc, "Invalid request queue id %d, skipping reply\n", + req_q_idx + 1); + goto next_reply; + } + op_req_q = &mrioc->req_qinfo[req_q_idx]; WRITE_ONCE(op_req_q->ci, le16_to_cpu(reply_desc->request_queue_ci)); @@ -598,8 +632,9 @@ int mpi3mr_process_op_reply_q(struct mpi3mr_ioc *mrioc, if (reply_dma) mpi3mr_repost_reply_buf(mrioc, reply_dma); - num_op_reply++; threshold_comps++; +next_reply: + num_op_reply++; if (++reply_ci == op_reply_q->num_replies) { reply_ci = 0; @@ -609,8 +644,19 @@ int mpi3mr_process_op_reply_q(struct mpi3mr_ioc *mrioc, reply_desc = mpi3mr_get_reply_desc(op_reply_q, reply_ci); if ((le16_to_cpu(reply_desc->reply_flags) & - MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase) + MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase) { + dma_rmb(); + if ((le16_to_cpu(reply_desc->reply_flags) & + MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) == exp_phase) + goto reply_ready; break; + } +reply_ready: + /* + * Ensure that the descriptor payload is read only after + * the phase bit check is complete. + */ + dma_rmb(); #ifndef CONFIG_PREEMPT_RT /* * Exit completion loop to avoid CPU lockup @@ -672,6 +718,7 @@ static irqreturn_t mpi3mr_isr_primary(int irq, void *privdata) { struct mpi3mr_intr_info *intr_info = privdata; struct mpi3mr_ioc *mrioc; + struct op_reply_qinfo *op_reply_q; u16 midx; u32 num_admin_replies = 0, num_op_reply = 0; @@ -687,9 +734,9 @@ static irqreturn_t mpi3mr_isr_primary(int irq, void *privdata) if (!midx) num_admin_replies = mpi3mr_process_admin_reply_q(mrioc); - if (intr_info->op_reply_q) - num_op_reply = mpi3mr_process_op_reply_q(mrioc, - intr_info->op_reply_q); + op_reply_q = READ_ONCE(intr_info->op_reply_q); + if (op_reply_q) + num_op_reply = mpi3mr_process_op_reply_q(mrioc, op_reply_q); if (num_admin_replies || num_op_reply) return IRQ_HANDLED; @@ -702,6 +749,7 @@ static irqreturn_t mpi3mr_isr_primary(int irq, void *privdata) static irqreturn_t mpi3mr_isr(int irq, void *privdata) { struct mpi3mr_intr_info *intr_info = privdata; + struct op_reply_qinfo *op_reply_q; int ret; if (!intr_info) @@ -714,11 +762,12 @@ static irqreturn_t mpi3mr_isr(int irq, void *privdata) * If more IOs are expected, schedule IRQ polling thread. * Otherwise exit from ISR. */ - if ((threaded_isr_poll == false) || !intr_info->op_reply_q) + op_reply_q = READ_ONCE(intr_info->op_reply_q); + if ((threaded_isr_poll == false) || !op_reply_q) return ret; - if (!intr_info->op_reply_q->enable_irq_poll || - !atomic_read(&intr_info->op_reply_q->pend_ios)) + if (!op_reply_q->enable_irq_poll || + !atomic_read(&op_reply_q->pend_ios)) return ret; disable_irq_nosync(intr_info->os_irq); @@ -740,12 +789,19 @@ static irqreturn_t mpi3mr_isr_poll(int irq, void *privdata) { struct mpi3mr_intr_info *intr_info = privdata; struct mpi3mr_ioc *mrioc; + struct op_reply_qinfo *op_reply_q; u16 midx; u32 num_op_reply = 0; - if (!intr_info || !intr_info->op_reply_q) + if (!intr_info) return IRQ_NONE; + op_reply_q = READ_ONCE(intr_info->op_reply_q); + if (!op_reply_q) { + enable_irq(intr_info->os_irq); + return IRQ_HANDLED; + } + mrioc = intr_info->mrioc; midx = intr_info->msix_index; @@ -754,19 +810,23 @@ static irqreturn_t mpi3mr_isr_poll(int irq, void *privdata) if (!mrioc->intr_enabled || mrioc->unrecoverable) break; + op_reply_q = READ_ONCE(intr_info->op_reply_q); + if (!op_reply_q) + break; + if (!midx) mpi3mr_process_admin_reply_q(mrioc); - if (intr_info->op_reply_q) - num_op_reply += - mpi3mr_process_op_reply_q(mrioc, - intr_info->op_reply_q); + num_op_reply += + mpi3mr_process_op_reply_q(mrioc, op_reply_q); + if (!atomic_read(&op_reply_q->pend_ios)) + break; - usleep_range(MPI3MR_IRQ_POLL_SLEEP, MPI3MR_IRQ_POLL_SLEEP + 1); + usleep_range(MPI3MR_IRQ_POLL_SLEEP, 10 * MPI3MR_IRQ_POLL_SLEEP); - } while (atomic_read(&intr_info->op_reply_q->pend_ios) && - (num_op_reply < mrioc->max_host_ios)); + } while (num_op_reply < mrioc->max_host_ios); - intr_info->op_reply_q->enable_irq_poll = false; + if (op_reply_q) + op_reply_q->enable_irq_poll = false; enable_irq(intr_info->os_irq); return IRQ_HANDLED; @@ -1950,10 +2010,6 @@ static void mpi3mr_free_op_req_q_segments(struct mpi3mr_ioc *mrioc, u16 q_idx) int size; struct segments *segments; - segments = mrioc->req_qinfo[q_idx].q_segments; - if (!segments) - return; - if (mrioc->enable_segqueue) { size = MPI3MR_OP_REQ_Q_SEG_SIZE; if (mrioc->req_qinfo[q_idx].q_segment_list) { @@ -1967,6 +2023,10 @@ static void mpi3mr_free_op_req_q_segments(struct mpi3mr_ioc *mrioc, u16 q_idx) size = mrioc->req_qinfo[q_idx].segment_qd * mrioc->facts.op_req_sz; + segments = mrioc->req_qinfo[q_idx].q_segments; + if (!segments) + return; + for (j = 0; j < mrioc->req_qinfo[q_idx].num_segments; j++) { if (!segments[j].segment) continue; @@ -1993,10 +2053,17 @@ static void mpi3mr_free_op_reply_q_segments(struct mpi3mr_ioc *mrioc, u16 q_idx) u16 j; int size; struct segments *segments; + u16 midx = REPLY_QUEUE_IDX_TO_MSIX_IDX(q_idx, mrioc->op_reply_q_offset); - segments = mrioc->op_reply_qinfo[q_idx].q_segments; - if (!segments) - return; + /* + * Stop the ISR/poll thread from picking up this queue before its + * segments are freed below, and wait for any in-flight handler + * that already has the old pointer to finish using it. + */ + if (midx < mrioc->intr_info_count) { + WRITE_ONCE(mrioc->intr_info[midx].op_reply_q, NULL); + synchronize_irq(pci_irq_vector(mrioc->pdev, midx)); + } if (mrioc->enable_segqueue) { size = MPI3MR_OP_REP_Q_SEG_SIZE; @@ -2011,6 +2078,10 @@ static void mpi3mr_free_op_reply_q_segments(struct mpi3mr_ioc *mrioc, u16 q_idx) size = mrioc->op_reply_qinfo[q_idx].segment_qd * mrioc->op_reply_desc_sz; + segments = mrioc->op_reply_qinfo[q_idx].q_segments; + if (!segments) + return; + for (j = 0; j < mrioc->op_reply_qinfo[q_idx].num_segments; j++) { if (!segments[j].segment) continue; @@ -2477,7 +2548,7 @@ static int mpi3mr_create_op_req_q(struct mpi3mr_ioc *mrioc, u16 idx, static int mpi3mr_create_op_queues(struct mpi3mr_ioc *mrioc) { int retval = 0; - u16 num_queues = 0, i = 0, msix_count_op_q = 1; + u16 num_queues = 0, i = 0, j = 0, msix_count_op_q = 1; u32 ioc_status; enum mpi3mr_iocstate ioc_state; @@ -2529,6 +2600,13 @@ static int mpi3mr_create_op_queues(struct mpi3mr_ioc *mrioc) } } + if (i < num_queues) { + for (j = i; j < num_queues; j++) { + mpi3mr_free_op_req_q_segments(mrioc, j); + mpi3mr_free_op_reply_q_segments(mrioc, j); + } + } + if (i == 0) { /* Not even one queue is created successfully*/ retval = -1; @@ -2550,11 +2628,19 @@ static int mpi3mr_create_op_queues(struct mpi3mr_ioc *mrioc) return retval; out_failed: - kfree(mrioc->req_qinfo); - mrioc->req_qinfo = NULL; + if (mrioc->req_qinfo) { + for (j = 0; j < i; j++) { + mpi3mr_free_op_req_q_segments(mrioc, j); + mpi3mr_free_op_reply_q_segments(mrioc, j); + } + kfree(mrioc->req_qinfo); + mrioc->req_qinfo = NULL; + } + mrioc->num_op_req_q = 0; kfree(mrioc->op_reply_qinfo); mrioc->op_reply_qinfo = NULL; + mrioc->num_op_reply_q = 0; return retval; } @@ -2598,7 +2684,8 @@ int mpi3mr_op_request_post(struct mpi3mr_ioc *mrioc, if (mpi3mr_check_req_qfull(op_req_q)) { midx = REPLY_QUEUE_IDX_TO_MSIX_IDX( reply_qidx, mrioc->op_reply_q_offset); - mpi3mr_process_op_reply_q(mrioc, mrioc->intr_info[midx].op_reply_q); + mpi3mr_process_op_reply_q(mrioc, + READ_ONCE(mrioc->intr_info[midx].op_reply_q)); if (mpi3mr_check_req_qfull(op_req_q)) { @@ -2891,8 +2978,9 @@ static int mpi3mr_print_pkg_ver(struct mpi3mr_ioc *mrioc) * @work: work struct * * Watch dog work periodically executed (1 second interval) to - * monitor firmware fault and to issue periodic timer sync to - * the firmware. + * monitor firmware fault and perform timestamp synchronization + * to firmware, with an early sync 1 minute after load followed + * by periodic updates at ts_update_interval seconds (default 15 minutes). * * Return: Nothing. */ @@ -2938,11 +3026,23 @@ static void mpi3mr_watchdog_work(struct work_struct *work) } if (!(mrioc->facts.ioc_capabilities & - MPI3_IOCFACTS_CAPABILITY_NON_SUPERVISOR_IOC) && - (mrioc->ts_update_counter++ >= mrioc->ts_update_interval)) { + MPI3_IOCFACTS_CAPABILITY_NON_SUPERVISOR_IOC)) { + if (!mrioc->early_ts_sync_done) { + /* + * Send time sync 1 min after load + */ + if (mrioc->ts_update_counter++ >= + MPI3MR_EARLY_TSUPDATE_SECONDS) { + mrioc->early_ts_sync_done = 1; + mrioc->ts_update_counter = 0; + mpi3mr_sync_timestamp(mrioc); + } + } else if (mrioc->ts_update_counter++ >= + mrioc->ts_update_interval) { + mrioc->ts_update_counter = 0; + mpi3mr_sync_timestamp(mrioc); + } - mrioc->ts_update_counter = 0; - mpi3mr_sync_timestamp(mrioc); } if ((mrioc->prepare_for_reset) && @@ -4128,26 +4228,35 @@ static int mpi3mr_repost_diag_bufs(struct mpi3mr_ioc *mrioc) } /** - * mpi3mr_read_tsu_interval - Update time stamp interval + * mpi3mr_read_driver_page1 - Read Driver Page 1 parameters * @mrioc: Adapter instance reference * - * Update time stamp interval if its defined in driver page 1, - * otherwise use default value. + * Reads and caches Driver Page 1 parameters such as + * timestamp update interval and driver behavior flags. * * Return: Nothing */ static void -mpi3mr_read_tsu_interval(struct mpi3mr_ioc *mrioc) +mpi3mr_read_driver_page1(struct mpi3mr_ioc *mrioc) { struct mpi3_driver_page1 driver_pg1; u16 pg_sz = sizeof(driver_pg1); int retval = 0; mrioc->ts_update_interval = MPI3MR_TSUPDATE_INTERVAL; + mrioc->skip_dev_shutdown_on_unload = 0; retval = mpi3mr_cfg_get_driver_pg1(mrioc, &driver_pg1, pg_sz); - if (!retval && driver_pg1.time_stamp_update) + + if (retval) + return; + + if (driver_pg1.time_stamp_update) mrioc->ts_update_interval = (driver_pg1.time_stamp_update * 60); + + mrioc->skip_dev_shutdown_on_unload = + (le32_to_cpu(driver_pg1.flags) & + MPI3_DRIVER1_FLAGS_DEVICE_SHUTDOWN_ON_UNLOAD_DISABLE) ? 1 : 0; } /** @@ -4453,7 +4562,7 @@ int mpi3mr_init_ioc(struct mpi3mr_ioc *mrioc) goto out_failed_noretry; } - mpi3mr_read_tsu_interval(mrioc); + mpi3mr_read_driver_page1(mrioc); mpi3mr_print_ioc_info(mrioc); dprint_init(mrioc, "allocating host diag buffers\n"); @@ -4625,7 +4734,7 @@ int mpi3mr_reinit_ioc(struct mpi3mr_ioc *mrioc, u8 is_resume) goto out_failed_noretry; } - mpi3mr_read_tsu_interval(mrioc); + mpi3mr_read_driver_page1(mrioc); mpi3mr_print_ioc_info(mrioc); if (is_resume) { @@ -5110,8 +5219,16 @@ static void mpi3mr_issue_ioc_shutdown(struct mpi3mr_ioc *mrioc) return; } - shutdown_action = MPI3_SYSIF_IOC_CONFIG_SHUTDOWN_NORMAL | - MPI3_SYSIF_IOC_CONFIG_DEVICE_SHUTDOWN_SEND_REQ; + shutdown_action = MPI3_SYSIF_IOC_CONFIG_SHUTDOWN_NORMAL; + + if (!(mrioc->is_unload && mrioc->skip_dev_shutdown_on_unload)) + shutdown_action |= + MPI3_SYSIF_IOC_CONFIG_DEVICE_SHUTDOWN_SEND_REQ; + else + ioc_info(mrioc, + "The shutdown request is issued without the device shutdown bit set\n" + "as indicated by the controller configuration\n"); + ioc_config = readl(&mrioc->sysif_regs->ioc_configuration); ioc_config |= shutdown_action; diff --git a/drivers/scsi/mpi3mr/mpi3mr_os.c b/drivers/scsi/mpi3mr/mpi3mr_os.c index 6876f56a5350..ce0ab28fb054 100644 --- a/drivers/scsi/mpi3mr/mpi3mr_os.c +++ b/drivers/scsi/mpi3mr/mpi3mr_os.c @@ -283,32 +283,6 @@ void mpi3mr_hdb_trigger_data_event(struct mpi3mr_ioc *mrioc, mpi3mr_fwevt_add_to_list(mrioc, fwevt); } -/** - * 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) -{ - unsigned long flags; - - spin_lock_irqsave(&mrioc->fwevt_lock, flags); - if (!list_empty(&fwevt->list)) { - list_del_init(&fwevt->list); - /* - * Put fwevt reference count after - * removing it from fwevt_list - */ - mpi3mr_fwevt_put(fwevt); - } - spin_unlock_irqrestore(&mrioc->fwevt_lock, flags); -} - /** * mpi3mr_dequeue_fwevt - Dequeue firmware event from the list * @mrioc: Adapter instance reference @@ -328,11 +302,7 @@ static struct mpi3mr_fwevt *mpi3mr_dequeue_fwevt( fwevt = list_first_entry(&mrioc->fwevt_list, struct mpi3mr_fwevt, list); list_del_init(&fwevt->list); - /* - * Put fwevt reference count after - * removing it from fwevt_list - */ - mpi3mr_fwevt_put(fwevt); + } spin_unlock_irqrestore(&mrioc->fwevt_lock, flags); @@ -366,6 +336,11 @@ static void mpi3mr_cancel_work(struct mpi3mr_fwevt *fwevt) */ mpi3mr_fwevt_put(fwevt); } + + /* + * Drop the reference count that was acquired by the caller. + */ + mpi3mr_fwevt_put(fwevt); } /** @@ -380,17 +355,44 @@ static void mpi3mr_cancel_work(struct mpi3mr_fwevt *fwevt) void mpi3mr_cleanup_fwevt_list(struct mpi3mr_ioc *mrioc) { struct mpi3mr_fwevt *fwevt = NULL; + unsigned long flags; + /* + * Safely read current_event under lock to prevent TOCTOU race + * with the firmware event worker thread. + */ + spin_lock_irqsave(&mrioc->fwevt_lock, flags); if ((list_empty(&mrioc->fwevt_list) && !mrioc->current_event) || - !mrioc->fwevt_worker_thread) + !mrioc->fwevt_worker_thread) { + spin_unlock_irqrestore(&mrioc->fwevt_lock, flags); return; + } + spin_unlock_irqrestore(&mrioc->fwevt_lock, flags); while ((fwevt = mpi3mr_dequeue_fwevt(mrioc))) mpi3mr_cancel_work(fwevt); - if (mrioc->current_event) { - fwevt = mrioc->current_event; + /* + * Safely read current_event under lock to prevent TOCTOU race + * with the firmware event worker thread. + */ + spin_lock_irqsave(&mrioc->fwevt_lock, flags); + fwevt = mrioc->current_event; + if (fwevt) { /* + * Take a reference to ensure the event is not freed by the + * worker thread while we are evaluating or cancelling it. + */ + mpi3mr_fwevt_get(fwevt); + } + spin_unlock_irqrestore(&mrioc->fwevt_lock, flags); + + if (fwevt) { + bool pending_at_sml; + + /* + * Read pending_at_sml under lock to avoid a stale value. + * * Don't call cancel_work_sync() API for the * fwevt work if the controller reset is * get called as part of processing the @@ -398,8 +400,13 @@ void mpi3mr_cleanup_fwevt_list(struct mpi3mr_ioc *mrioc) * waiting for device add/remove APIs to complete. * Otherwise we will see deadlock. */ - if (current_work() == &fwevt->work || fwevt->pending_at_sml) { + spin_lock_irqsave(&mrioc->fwevt_lock, flags); + pending_at_sml = fwevt->pending_at_sml; + spin_unlock_irqrestore(&mrioc->fwevt_lock, flags); + + if (current_work() == &fwevt->work || pending_at_sml) { fwevt->discard = 1; + mpi3mr_fwevt_put(fwevt); return; } @@ -913,6 +920,8 @@ void mpi3mr_remove_tgtdev_from_host(struct mpi3mr_ioc *mrioc, struct mpi3mr_tgt_dev *tgtdev) { struct mpi3mr_stgt_priv_data *tgt_priv; + unsigned long flags; + bool discard = false; ioc_info(mrioc, "%s :Removing handle(0x%04x), wwid(0x%016llx)\n", __func__, tgtdev->dev_handle, (unsigned long long)tgtdev->wwid); @@ -925,17 +934,27 @@ void mpi3mr_remove_tgtdev_from_host(struct mpi3mr_ioc *mrioc, if (!mrioc->sas_transport_enabled || (tgtdev->dev_type != MPI3_DEVICE_DEVFORM_SAS_SATA) || tgtdev->non_stl) { if (tgtdev->starget) { + spin_lock_irqsave(&mrioc->fwevt_lock, flags); + if (mrioc->stop_drv_processing || + mrioc->reset_in_progress) { + spin_unlock_irqrestore(&mrioc->fwevt_lock, flags); + return; + } if (mrioc->current_event) mrioc->current_event->pending_at_sml = 1; + spin_unlock_irqrestore(&mrioc->fwevt_lock, flags); scsi_remove_target(&tgtdev->starget->dev); tgtdev->host_exposed = 0; + spin_lock_irqsave(&mrioc->fwevt_lock, flags); if (mrioc->current_event) { mrioc->current_event->pending_at_sml = 0; - if (mrioc->current_event->discard) { - mpi3mr_print_device_event_notice(mrioc, - false); - return; - } + discard = mrioc->current_event->discard; + } + spin_unlock_irqrestore(&mrioc->fwevt_lock, flags); + if (discard) { + mpi3mr_print_device_event_notice(mrioc, + false); + return; } } } else @@ -963,6 +982,8 @@ static int mpi3mr_report_tgtdev_to_host(struct mpi3mr_ioc *mrioc, { int retval = 0; struct mpi3mr_tgt_dev *tgtdev; + unsigned long flags; + bool discard = false; if (mrioc->reset_in_progress || mrioc->pci_err_recovery) return -1; @@ -979,19 +1000,29 @@ static int mpi3mr_report_tgtdev_to_host(struct mpi3mr_ioc *mrioc, if (!mrioc->sas_transport_enabled || (tgtdev->dev_type != MPI3_DEVICE_DEVFORM_SAS_SATA) || tgtdev->non_stl){ tgtdev->host_exposed = 1; + spin_lock_irqsave(&mrioc->fwevt_lock, flags); + if (mrioc->stop_drv_processing || mrioc->reset_in_progress) { + spin_unlock_irqrestore(&mrioc->fwevt_lock, flags); + tgtdev->host_exposed = 0; + goto out; + } if (mrioc->current_event) mrioc->current_event->pending_at_sml = 1; + spin_unlock_irqrestore(&mrioc->fwevt_lock, flags); scsi_scan_target(&mrioc->shost->shost_gendev, mrioc->scsi_device_channel, tgtdev->perst_id, SCAN_WILD_CARD, SCSI_SCAN_INITIAL); if (!tgtdev->starget) tgtdev->host_exposed = 0; + spin_lock_irqsave(&mrioc->fwevt_lock, flags); if (mrioc->current_event) { mrioc->current_event->pending_at_sml = 0; - if (mrioc->current_event->discard) { - mpi3mr_print_device_event_notice(mrioc, true); - goto out; - } + discard = mrioc->current_event->discard; + } + spin_unlock_irqrestore(&mrioc->fwevt_lock, flags); + if (discard) { + mpi3mr_print_device_event_notice(mrioc, true); + goto out; } dprint_event_bh(mrioc, "exposed target device with handle(0x%04x), perst_id(%d)\n", @@ -1350,24 +1381,32 @@ static void mpi3mr_update_tgtdev(struct mpi3mr_ioc *mrioc, struct mpi3_device0_pcie_format *pcieinf = &dev_pg0->device_specific.pcie_format; u16 dev_info = le16_to_cpu(pcieinf->device_info); + u8 pgsz = MPI3MR_DEFAULT_PGSZEXP; + u8 reset_to = MPI3MR_INTADMCMD_TIMEOUT; + u8 abort_to = MPI3MR_INTADMCMD_TIMEOUT; + u8 fw_pgsz = READ_ONCE(pcieinf->page_size); tgtdev->dev_spec.pcie_inf.dev_info = dev_info; tgtdev->dev_spec.pcie_inf.capb = le32_to_cpu(pcieinf->capabilities); tgtdev->dev_spec.pcie_inf.mdts = MPI3MR_DEFAULT_MDTS; - /* 2^12 = 4096 */ - tgtdev->dev_spec.pcie_inf.pgsz = 12; + /* Validate firmware page size to prevent undefined shift behavior */ + if (fw_pgsz >= MPI3MR_DEFAULT_PGSZEXP && fw_pgsz <= MPI3MR_MAX_PGSZEXP) + pgsz = fw_pgsz; + if (dev_pg0->access_status == MPI3_DEVICE0_ASTATUS_NO_ERRORS) { tgtdev->dev_spec.pcie_inf.mdts = le32_to_cpu(pcieinf->maximum_data_transfer_size); - tgtdev->dev_spec.pcie_inf.pgsz = pcieinf->page_size; - tgtdev->dev_spec.pcie_inf.reset_to = - max_t(u8, pcieinf->controller_reset_to, - MPI3MR_INTADMCMD_TIMEOUT); - tgtdev->dev_spec.pcie_inf.abort_to = - max_t(u8, pcieinf->nvme_abort_to, + reset_to = max_t(u8, pcieinf->controller_reset_to, + MPI3MR_INTADMCMD_TIMEOUT); + abort_to = max_t(u8, pcieinf->nvme_abort_to, MPI3MR_INTADMCMD_TIMEOUT); } + + tgtdev->dev_spec.pcie_inf.pgsz = pgsz; + tgtdev->dev_spec.pcie_inf.reset_to = reset_to; + tgtdev->dev_spec.pcie_inf.abort_to = abort_to; + if (tgtdev->dev_spec.pcie_inf.mdts > (1024 * 1024)) tgtdev->dev_spec.pcie_inf.mdts = (1024 * 1024); if (((dev_info & MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_MASK) != @@ -2126,9 +2165,19 @@ static void mpi3mr_fwevt_bh(struct mpi3mr_ioc *mrioc, u16 perst_id, handle, dev_info; struct mpi3_device0_sas_sata_format *sasinf = NULL; unsigned int timeout; + unsigned long flags; - mpi3mr_fwevt_del_from_list(mrioc, fwevt); + spin_lock_irqsave(&mrioc->fwevt_lock, flags); + if (!list_empty(&fwevt->list)) { + list_del_init(&fwevt->list); + /* + * Put fwevt reference count after + * removing it from fwevt_list + */ + mpi3mr_fwevt_put(fwevt); + } mrioc->current_event = fwevt; + spin_unlock_irqrestore(&mrioc->fwevt_lock, flags); if (mrioc->stop_drv_processing || mrioc->pci_err_recovery) { dprint_event_bh(mrioc, @@ -2263,9 +2312,12 @@ static void mpi3mr_fwevt_bh(struct mpi3mr_ioc *mrioc, mpi3mr_process_event_ack(mrioc, fwevt->event_id, fwevt->evt_ctx); out: + spin_lock_irqsave(&mrioc->fwevt_lock, flags); + mrioc->current_event = NULL; + spin_unlock_irqrestore(&mrioc->fwevt_lock, flags); + /* Put fwevt reference count to neutralize kref_init increment */ mpi3mr_fwevt_put(fwevt); - mrioc->current_event = NULL; } /** @@ -3484,8 +3536,12 @@ void mpi3mr_process_op_reply_desc(struct mpi3mr_ioc *mrioc, scsi_reply = mpi3mr_get_reply_virt_addr(mrioc, *reply_dma); if (!scsi_reply) { - panic("%s: scsi_reply is NULL, this shouldn't happen\n", - mrioc->name); + ioc_err(mrioc, "scsi_reply is NULL, invalid reply_frame_address\n"); + /* + * Do not let the caller repost an address that + * failed virt-addr lookup back to the hardware. + */ + *reply_dma = 0; goto out; } host_tag = le16_to_cpu(scsi_reply->host_tag); @@ -3982,7 +4038,7 @@ inline void mpi3mr_poll_pend_io_completions(struct mpi3mr_ioc *mrioc) for (i = mrioc->op_reply_q_offset; i < num_of_reply_queues; i++) mpi3mr_process_op_reply_q(mrioc, - mrioc->intr_info[i].op_reply_q); + READ_ONCE(mrioc->intr_info[i].op_reply_q)); } /** @@ -5730,6 +5786,8 @@ static void mpi3mr_remove(struct pci_dev *pdev) return; mrioc = shost_priv(shost); + mrioc->is_unload = true; + while (mrioc->reset_in_progress || mrioc->is_driver_loading) ssleep(1); @@ -5862,6 +5920,9 @@ mpi3mr_suspend(struct device *dev) ssleep(1); mrioc->stop_drv_processing = 1; mpi3mr_cleanup_fwevt_list(mrioc); + /* Flush any pending discarded event before unmapping PCI resources below. */ + if (mrioc->fwevt_worker_thread) + flush_workqueue(mrioc->fwevt_worker_thread); scsi_block_requests(shost); mpi3mr_stop_watchdog(mrioc); mpi3mr_cleanup_ioc(mrioc); diff --git a/drivers/scsi/mpi3mr/mpi3mr_transport.c b/drivers/scsi/mpi3mr/mpi3mr_transport.c index 2afff1a002ed..a3412cd4ca5b 100644 --- a/drivers/scsi/mpi3mr/mpi3mr_transport.c +++ b/drivers/scsi/mpi3mr/mpi3mr_transport.c @@ -1223,13 +1223,14 @@ void mpi3mr_sas_host_add(struct mpi3mr_ioc *mrioc) } num_phys = sas_io_unit_pg0->num_phys; kfree(sas_io_unit_pg0); + sas_io_unit_pg0 = NULL; mrioc->sas_hba.host_node = 1; INIT_LIST_HEAD(&mrioc->sas_hba.sas_port_list); mrioc->sas_hba.parent_dev = &mrioc->shost->shost_gendev; mrioc->sas_hba.phy = kzalloc_objs(struct mpi3mr_sas_phy, num_phys); if (!mrioc->sas_hba.phy) - return; + goto out; mrioc->sas_hba.num_phys = num_phys; @@ -1237,12 +1238,12 @@ void mpi3mr_sas_host_add(struct mpi3mr_ioc *mrioc) (num_phys * sizeof(struct mpi3_sas_io_unit0_phy_data)); sas_io_unit_pg0 = kzalloc(sz, GFP_KERNEL); if (!sas_io_unit_pg0) - return; + goto out_free_phy; if (mpi3mr_cfg_get_sas_io_unit_pg0(mrioc, sas_io_unit_pg0, sz)) { ioc_err(mrioc, "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); - goto out; + goto out_free_phy; } mrioc->sas_hba.handle = 0; @@ -1256,12 +1257,12 @@ void mpi3mr_sas_host_add(struct mpi3mr_ioc *mrioc) MPI3_SAS_PHY_PGAD_FORM_PHY_NUMBER, i)) { ioc_err(mrioc, "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); - goto out; + goto out_free_phy; } if (ioc_status != MPI3_IOCSTATUS_SUCCESS) { ioc_err(mrioc, "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); - goto out; + goto out_free_phy; } if (!mrioc->sas_hba.handle) @@ -1271,26 +1272,27 @@ void mpi3mr_sas_host_add(struct mpi3mr_ioc *mrioc) if (!(mpi3mr_get_hba_port_by_id(mrioc, port_id))) if (!mpi3mr_alloc_hba_port(mrioc, port_id)) - goto out; + goto out_free_phy; mrioc->sas_hba.phy[i].handle = mrioc->sas_hba.handle; mrioc->sas_hba.phy[i].phy_id = i; mrioc->sas_hba.phy[i].hba_port = mpi3mr_get_hba_port_by_id(mrioc, port_id); - mpi3mr_add_host_phy(mrioc, &mrioc->sas_hba.phy[i], - phy_pg0, mrioc->sas_hba.parent_dev); + if (mpi3mr_add_host_phy(mrioc, &mrioc->sas_hba.phy[i], + phy_pg0, mrioc->sas_hba.parent_dev)) + goto out_free_phy; } if ((mpi3mr_cfg_get_dev_pg0(mrioc, &ioc_status, &dev_pg0, sizeof(dev_pg0), MPI3_DEVICE_PGAD_FORM_HANDLE, mrioc->sas_hba.handle))) { ioc_err(mrioc, "%s: device page0 read failed\n", __func__); - goto out; + goto out_free_phy; } if (ioc_status != MPI3_IOCSTATUS_SUCCESS) { ioc_err(mrioc, "device page read failed for handle(0x%04x), with ioc_status(0x%04x) failure at %s:%d/%s()!\n", mrioc->sas_hba.handle, ioc_status, __FILE__, __LINE__, __func__); - goto out; + goto out_free_phy; } mrioc->sas_hba.enclosure_handle = le16_to_cpu(dev_pg0.enclosure_handle); @@ -1313,6 +1315,17 @@ void mpi3mr_sas_host_add(struct mpi3mr_ioc *mrioc) le64_to_cpu(encl_pg0.enclosure_logical_id); } + goto out; + +out_free_phy: + for (i = 0; i < mrioc->sas_hba.num_phys; i++) { + if (mrioc->sas_hba.phy[i].phy) + sas_phy_delete(mrioc->sas_hba.phy[i].phy); + } + kfree(mrioc->sas_hba.phy); + mrioc->sas_hba.phy = NULL; + mrioc->sas_hba.num_phys = 0; + out: kfree(sas_io_unit_pg0); } @@ -1337,6 +1350,7 @@ static struct mpi3mr_sas_port *mpi3mr_sas_port_add(struct mpi3mr_ioc *mrioc, struct mpi3mr_sas_phy *mr_sas_phy, *next; struct mpi3mr_sas_port *mr_sas_port; unsigned long flags; + bool discard = false; struct mpi3mr_sas_node *mr_sas_node; struct sas_rphy *rphy; struct mpi3mr_tgt_dev *tgtdev = NULL; @@ -1435,9 +1449,15 @@ static struct mpi3mr_sas_port *mpi3mr_sas_port_add(struct mpi3mr_ioc *mrioc, } port = sas_port_alloc_num(mr_sas_node->parent_dev); + if (!port) { + ioc_err(mrioc, "failure at %s:%d/%s() (sas_port_alloc)!\n", + __FILE__, __LINE__, __func__); + goto out_fail; + } if ((sas_port_add(port))) { ioc_err(mrioc, "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); + sas_port_free(port); goto out_fail; } @@ -1457,19 +1477,51 @@ static struct mpi3mr_sas_port *mpi3mr_sas_port_add(struct mpi3mr_ioc *mrioc, mr_sas_port->port = port; if (mr_sas_port->remote_identify.device_type == SAS_END_DEVICE) { rphy = sas_end_device_alloc(port); + if (!rphy) { + ioc_err(mrioc, "failure at %s:%d/%s() (sas_end_device_alloc)!\n", + __FILE__, __LINE__, __func__); + sas_port_delete(port); + goto out_fail; + } tgtdev->dev_spec.sas_sata_inf.rphy = rphy; } else { rphy = sas_expander_alloc(port, mr_sas_port->remote_identify.device_type); + if (!rphy) { + ioc_err(mrioc, "failure at %s:%d/%s() (sas_expander_alloc)!\n", + __FILE__, __LINE__, __func__); + sas_port_delete(port); + goto out_fail; + } } rphy->identify = mr_sas_port->remote_identify; + spin_lock_irqsave(&mrioc->fwevt_lock, flags); + if (mrioc->stop_drv_processing || mrioc->reset_in_progress) { + spin_unlock_irqrestore(&mrioc->fwevt_lock, flags); + sas_rphy_free(rphy); + sas_port_delete(port); + goto out_fail; + } if (mrioc->current_event) mrioc->current_event->pending_at_sml = 1; + spin_unlock_irqrestore(&mrioc->fwevt_lock, flags); if ((sas_rphy_add(rphy))) { ioc_err(mrioc, "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); + spin_lock_irqsave(&mrioc->fwevt_lock, flags); + if (mrioc->current_event) { + mrioc->current_event->pending_at_sml = 0; + discard = mrioc->current_event->discard; + } + spin_unlock_irqrestore(&mrioc->fwevt_lock, flags); + if (discard) + mpi3mr_print_device_event_notice(mrioc, true); + sas_rphy_unlink(rphy); + sas_rphy_free(rphy); + sas_port_delete(port); + goto out_fail; } if (mr_sas_port->remote_identify.device_type == SAS_END_DEVICE) { tgtdev->dev_spec.sas_sata_inf.pend_sas_rphy_add = 0; @@ -1487,11 +1539,14 @@ static struct mpi3mr_sas_port *mpi3mr_sas_port_add(struct mpi3mr_ioc *mrioc, list_add_tail(&mr_sas_port->port_list, &mr_sas_node->sas_port_list); spin_unlock_irqrestore(&mrioc->sas_node_lock, flags); + spin_lock_irqsave(&mrioc->fwevt_lock, flags); if (mrioc->current_event) { mrioc->current_event->pending_at_sml = 0; - if (mrioc->current_event->discard) - mpi3mr_print_device_event_notice(mrioc, true); + discard = mrioc->current_event->discard; } + spin_unlock_irqrestore(&mrioc->fwevt_lock, flags); + if (discard) + mpi3mr_print_device_event_notice(mrioc, true); /* fill in report manufacture */ if (mr_sas_port->remote_identify.device_type == @@ -1505,9 +1560,17 @@ static struct mpi3mr_sas_port *mpi3mr_sas_port_add(struct mpi3mr_ioc *mrioc, return mr_sas_port; out_fail: + if (tgtdev) { + tgtdev->dev_spec.sas_sata_inf.pend_sas_rphy_add = 0; + tgtdev->dev_spec.sas_sata_inf.rphy = NULL; + mpi3mr_tgtdev_put(tgtdev); + } + list_for_each_entry_safe(mr_sas_phy, next, &mr_sas_port->phy_list, - port_siblings) + port_siblings) { + mr_sas_phy->phy_belongs_to_port = 0; list_del(&mr_sas_phy->port_siblings); + } kfree(mr_sas_port); return NULL; } @@ -1529,6 +1592,7 @@ static void mpi3mr_sas_port_remove(struct mpi3mr_ioc *mrioc, u64 sas_address, { int i; unsigned long flags; + bool discard = false; struct mpi3mr_sas_port *mr_sas_port, *next; struct mpi3mr_sas_node *mr_sas_node; u8 found = 0; @@ -1585,8 +1649,10 @@ static void mpi3mr_sas_port_remove(struct mpi3mr_ioc *mrioc, u64 sas_address, spin_unlock_irqrestore(&mrioc->sas_node_lock, flags); + spin_lock_irqsave(&mrioc->fwevt_lock, flags); if (mrioc->current_event) mrioc->current_event->pending_at_sml = 1; + spin_unlock_irqrestore(&mrioc->fwevt_lock, flags); list_for_each_entry_safe(mr_sas_phy, next_phy, &mr_sas_port->phy_list, port_siblings) { @@ -1608,11 +1674,14 @@ static void mpi3mr_sas_port_remove(struct mpi3mr_ioc *mrioc, u64 sas_address, ioc_info(mrioc, "%s: removed sas_address(0x%016llx)\n", __func__, (unsigned long long)sas_address); + spin_lock_irqsave(&mrioc->fwevt_lock, flags); if (mrioc->current_event) { mrioc->current_event->pending_at_sml = 0; - if (mrioc->current_event->discard) - mpi3mr_print_device_event_notice(mrioc, false); + discard = mrioc->current_event->discard; } + spin_unlock_irqrestore(&mrioc->fwevt_lock, flags); + if (discard) + mpi3mr_print_device_event_notice(mrioc, false); kfree(mr_sas_port); }