| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (C) 2026 LeapIO Tech Inc. |
| * |
| * LeapRAID storage and RAID controller driver. |
| */ |
| #include <linux/module.h> |
| |
| #include "leapraid_func.h" |
| |
| static int poll_queues; |
| module_param(poll_queues, int, 0444); |
| MODULE_PARM_DESC(poll_queues, |
| "specifies the number of queues for io_uring poll mode."); |
| |
| static int max_msix_vectors = LEAPRAID_INVALID_MSIX_VECTORS; |
| module_param(max_msix_vectors, int, 0444); |
| MODULE_PARM_DESC(max_msix_vectors, "max msix vectors"); |
| |
| static void leapraid_remove_device(struct leapraid_adapter *adapter, |
| struct leapraid_sas_dev *sas_dev); |
| static void leapraid_set_led(struct leapraid_adapter *adapter, |
| struct leapraid_sas_dev *sas_dev, bool on); |
| static void leapraid_ublk_io_dev(struct leapraid_adapter *adapter, |
| u64 sas_address, |
| struct leapraid_card_port *port); |
| static void leapraid_clear_cached_boot_dev(struct leapraid_adapter *adapter, |
| void *dev, u32 chnl); |
| static int leapraid_make_adapter_available(struct leapraid_adapter *adapter); |
| static void leapraid_sync_irqs_for_cleanup(struct leapraid_adapter *adapter); |
| static int leapraid_fw_log_init(struct leapraid_adapter *adapter); |
| static bool leapraid_should_skip_poll_work(struct leapraid_adapter *adapter); |
| static int leapraid_make_adapter_ready(struct leapraid_adapter *adapter, |
| enum reset_type type); |
| |
| static noinline bool leapraid_shost_in_recovery(struct Scsi_Host *shost) |
| { |
| enum scsi_host_state state; |
| |
| state = scsi_get_host_state(shost); |
| return state == SHOST_RECOVERY || |
| state == SHOST_CANCEL_RECOVERY || |
| state == SHOST_DEL_RECOVERY || |
| shost->tmf_in_progress; |
| } |
| |
| static inline bool leapraid_is_end_dev(u32 dev_type) |
| { |
| return (dev_type & LEAPRAID_DEVTYP_END_DEV) && |
| ((dev_type & LEAPRAID_DEVTYP_SSP_TGT) || |
| (dev_type & LEAPRAID_DEVTYP_STP_TGT) || |
| (dev_type & LEAPRAID_DEVTYP_SATA_DEV)); |
| } |
| |
| bool leapraid_pci_removed(struct leapraid_adapter *adapter) |
| { |
| struct pci_dev *pdev = adapter->pdev; |
| u32 vendor_id; |
| |
| if (pci_bus_read_config_dword(pdev->bus, |
| pdev->devfn, |
| PCI_VENDOR_ID, |
| &vendor_id)) |
| return true; |
| |
| return (vendor_id & LEAPRAID_PCI_VENDOR_ID_MASK) != |
| LEAPRAID_VENDOR_ID; |
| } |
| |
| static bool leapraid_pci_active(struct leapraid_adapter *adapter) |
| { |
| return !(adapter->access_ctrl.pcie_recovering || |
| leapraid_pci_removed(adapter)); |
| } |
| |
| void *leapraid_get_reply_vaddr(struct leapraid_adapter *adapter, u32 rep_paddr) |
| { |
| if (!rep_paddr) |
| return NULL; |
| |
| return adapter->mem_desc.rep_msg + |
| (rep_paddr - (u32)adapter->mem_desc.rep_msg_dma); |
| } |
| |
| void *leapraid_get_task_desc(struct leapraid_adapter *adapter, u16 taskid) |
| { |
| return adapter->mem_desc.task_desc + |
| taskid * LEAPRAID_REQUEST_SIZE; |
| } |
| |
| void *leapraid_get_sense_buffer(struct leapraid_adapter *adapter, u16 taskid) |
| { |
| return adapter->mem_desc.sense_data + |
| (taskid - 1) * SCSI_SENSE_BUFFERSIZE; |
| } |
| |
| __le32 leapraid_get_sense_buffer_dma(struct leapraid_adapter *adapter, |
| u16 taskid) |
| { |
| return cpu_to_le32(adapter->mem_desc.sense_data_dma + |
| ((taskid - 1) * SCSI_SENSE_BUFFERSIZE)); |
| } |
| |
| void leapraid_mask_int(struct leapraid_adapter *adapter) |
| { |
| u32 reg; |
| |
| adapter->mask_int = 1; |
| reg = leapraid_readl(&adapter->iomem_base->host_int_mask); |
| reg |= LEAPRAID_TO_SYS_DB_MASK + LEAPRAID_REPLY_INT_MASK + |
| LEAPRAID_RESET_IRQ_MASK; |
| writel(reg, &adapter->iomem_base->host_int_mask); |
| leapraid_readl(&adapter->iomem_base->host_int_mask); |
| } |
| |
| void leapraid_unmask_int(struct leapraid_adapter *adapter) |
| { |
| u32 reg; |
| |
| reg = leapraid_readl(&adapter->iomem_base->host_int_mask); |
| reg &= ~LEAPRAID_REPLY_INT_MASK; |
| writel(reg, &adapter->iomem_base->host_int_mask); |
| adapter->mask_int = 0; |
| } |
| |
| static void leapraid_overheat_suspend(struct leapraid_adapter *adapter) |
| { |
| struct workqueue_struct *wq; |
| struct Scsi_Host *shost; |
| struct pci_dev *pdev; |
| |
| if (!adapter) |
| return; |
| |
| pdev = adapter->pdev; |
| shost = pci_get_drvdata(pdev); |
| if (!shost) { |
| dev_warn(&pdev->dev, |
| "Overheat suspend failed, invalid host or adapter\n"); |
| return; |
| } |
| |
| wq = adapter->overheat_desc.fault_overheat_wq; |
| if (!wq) |
| return; |
| |
| if (atomic_cmpxchg(&adapter->overheat_desc.thermal_alert, 0, 1)) |
| return; |
| |
| queue_work(wq, &adapter->overheat_desc.fault_overheat_work); |
| } |
| |
| static void leapraid_stop_adapter_on_fault(struct leapraid_adapter *adapter, |
| u32 db) |
| { |
| u32 adapter_state = db & LEAPRAID_DB_MASK; |
| bool fault_1 = false; |
| bool fault_2 = false; |
| |
| fault_1 = adapter_state == LEAPRAID_DB_MASK; |
| fault_2 = adapter_state == LEAPRAID_DB_FAULT && |
| (db & LEAPRAID_DB_DATA_MASK) == LEAPRAID_DB_OVER_TEMPERATURE; |
| |
| if (!fault_1 && !fault_2) |
| return; |
| |
| if (fault_1) |
| dev_err(&adapter->pdev->dev, |
| "%s: Doorbell status 0xFFFF!\n", __func__); |
| else |
| dev_err(&adapter->pdev->dev, |
| "%s: Adapter overheating detected!\n", __func__); |
| |
| leapraid_overheat_suspend(adapter); |
| } |
| |
| u32 leapraid_get_adapter_state(struct leapraid_adapter *adapter) |
| { |
| u32 db; |
| u32 adapter_state; |
| |
| db = leapraid_readl(&adapter->iomem_base->db); |
| adapter_state = db & LEAPRAID_DB_MASK; |
| leapraid_stop_adapter_on_fault(adapter, db); |
| return adapter_state; |
| } |
| |
| static bool leapraid_wait_adapter_ready(struct leapraid_adapter *adapter) |
| { |
| u32 cur_state; |
| u32 cnt; |
| |
| for (cnt = LEAPRAID_ADAPTER_READY_MAX_RETRY; cnt > 0; cnt--) { |
| cur_state = leapraid_get_adapter_state(adapter); |
| |
| if (cur_state == LEAPRAID_DB_READY) |
| return true; |
| if (cur_state == LEAPRAID_DB_FAULT) |
| break; |
| usleep_range(LEAPRAID_ADAPTER_READY_SLEEP_MIN_US, |
| LEAPRAID_ADAPTER_READY_SLEEP_MAX_US); |
| } |
| |
| return false; |
| } |
| |
| static int leapraid_db_wait_int_host(struct leapraid_adapter *adapter) |
| { |
| u32 cnt; |
| |
| for (cnt = LEAPRAID_DB_WAIT_MAX_RETRY; cnt > 0; cnt--) { |
| if (leapraid_readl(&adapter->iomem_base->host_int_status) & |
| LEAPRAID_ADAPTER2HOST_DB_STATUS) |
| return 0; |
| |
| udelay(LEAPRAID_DB_WAIT_DELAY_US); |
| } |
| |
| return -EFAULT; |
| } |
| |
| static int leapraid_db_wait_ack_and_clear_int(struct leapraid_adapter *adapter) |
| { |
| u32 adapter_state; |
| u32 int_status; |
| u32 cnt; |
| |
| for (cnt = LEAPRAID_ADAPTER_READY_MAX_RETRY; cnt > 0; cnt--) { |
| int_status = |
| leapraid_readl(&adapter->iomem_base->host_int_status); |
| |
| if (int_status == 0xFFFFFFFF) |
| return -EFAULT; |
| |
| if (!(int_status & LEAPRAID_HOST2ADAPTER_DB_STATUS)) |
| return 0; |
| |
| if (int_status & LEAPRAID_ADAPTER2HOST_DB_STATUS) { |
| adapter_state = leapraid_get_adapter_state(adapter); |
| if (adapter_state == LEAPRAID_DB_FAULT) |
| return -EFAULT; |
| } |
| |
| usleep_range(LEAPRAID_ADAPTER_READY_SLEEP_MIN_US, |
| LEAPRAID_ADAPTER_READY_SLEEP_MAX_US); |
| } |
| |
| return -EFAULT; |
| } |
| |
| static int leapraid_handshake_func(struct leapraid_adapter *adapter, |
| int req_bytes, u32 *req, |
| int rep_bytes, u16 *rep) |
| { |
| int failed, i; |
| |
| if (leapraid_readl(&adapter->iomem_base->db) & LEAPRAID_DB_USED) { |
| dev_err(&adapter->pdev->dev, "doorbell used\n"); |
| return -EFAULT; |
| } |
| |
| if (leapraid_readl(&adapter->iomem_base->host_int_status) & |
| LEAPRAID_ADAPTER2HOST_DB_STATUS) |
| writel(0, &adapter->iomem_base->host_int_status); |
| |
| writel(((LEAPRAID_FUNC_HANDSHAKE << LEAPRAID_DB_FUNC_SHIFT) | |
| ((req_bytes / LEAPRAID_DWORDS_BYTE_SIZE) << |
| LEAPRAID_DB_ADD_DWORDS_SHIFT)), |
| &adapter->iomem_base->db); |
| |
| if (leapraid_db_wait_int_host(adapter)) { |
| dev_err(&adapter->pdev->dev, "%d: Wait db interrupt timeout\n", |
| __LINE__); |
| return -EFAULT; |
| } |
| |
| writel(0, &adapter->iomem_base->host_int_status); |
| |
| if (leapraid_db_wait_ack_and_clear_int(adapter)) { |
| dev_err(&adapter->pdev->dev, "%d: Wait ack failure\n", |
| __LINE__); |
| return -EFAULT; |
| } |
| |
| for (i = 0, failed = 0; |
| i < req_bytes / LEAPRAID_DWORDS_BYTE_SIZE && !failed; |
| i++) { |
| writel((u32)req[i], &adapter->iomem_base->db); |
| if (leapraid_db_wait_ack_and_clear_int(adapter)) |
| failed = 1; |
| } |
| if (failed) { |
| dev_err(&adapter->pdev->dev, "%d: Wait ack failure\n", |
| __LINE__); |
| return -EFAULT; |
| } |
| |
| for (i = 0; i < rep_bytes / LEAPRAID_WORD_BYTE_SIZE; i++) { |
| if (leapraid_db_wait_int_host(adapter)) { |
| dev_err(&adapter->pdev->dev, |
| "%d: Wait db interrupt timeout\n", __LINE__); |
| return -EFAULT; |
| } |
| rep[i] = (u16)(leapraid_readl(&adapter->iomem_base->db) |
| & LEAPRAID_DB_DATA_MASK); |
| writel(0, &adapter->iomem_base->host_int_status); |
| } |
| |
| if (leapraid_db_wait_int_host(adapter)) { |
| dev_err(&adapter->pdev->dev, "%d: Wait db interrupt timeout\n", |
| __LINE__); |
| return -EFAULT; |
| } |
| |
| writel(0, &adapter->iomem_base->host_int_status); |
| |
| return 0; |
| } |
| |
| int leapraid_check_adapter_is_op(struct leapraid_adapter *adapter, int wait, |
| const char *caller) |
| { |
| int wait_count; |
| |
| for (wait_count = wait; wait_count > 0; wait_count--) { |
| if (READ_ONCE(adapter->access_ctrl.host_removing)) { |
| dev_warn(&adapter->pdev->dev, |
| "%s: Host is removing\n", caller); |
| return -EFAULT; |
| } |
| |
| if (leapraid_pci_removed(adapter)) { |
| dev_warn(&adapter->pdev->dev, |
| "%s: PCI device removed\n", __func__); |
| return -EFAULT; |
| } |
| |
| if (leapraid_get_adapter_state(adapter) == |
| LEAPRAID_DB_OPERATIONAL) |
| return 0; |
| |
| dev_dbg(&adapter->pdev->dev, |
| "%s: Wait for adapter to become op status(cnt=%d)\n", |
| caller, wait - wait_count); |
| |
| ssleep(1); |
| } |
| |
| dev_err(&adapter->pdev->dev, |
| "%s: Adapter failed to become op state, last state=%d\n", |
| caller, leapraid_get_adapter_state(adapter)); |
| |
| return -EFAULT; |
| } |
| |
| struct leapraid_io_req_tracker *leapraid_get_io_tracker_from_taskid( |
| struct leapraid_adapter *adapter, u16 taskid) |
| { |
| struct scsi_cmnd *scmd; |
| |
| if (WARN_ON(!taskid)) |
| return NULL; |
| |
| if (WARN_ON(taskid > adapter->shost->can_queue)) |
| return NULL; |
| |
| scmd = leapraid_get_scmd_from_taskid(adapter, taskid); |
| if (scmd) |
| return scsi_cmd_priv(scmd); |
| |
| return NULL; |
| } |
| |
| static u8 leapraid_get_cb_idx(struct leapraid_adapter *adapter, u16 taskid) |
| { |
| struct leapraid_driver_cmd *sp_cmd; |
| u8 cb_idx = 0xFF; |
| |
| if (WARN_ON(!taskid)) |
| return cb_idx; |
| |
| list_for_each_entry(sp_cmd, &adapter->driver_cmds.special_cmd_list, |
| list) |
| if (taskid == sp_cmd->taskid || |
| taskid == sp_cmd->hp_taskid || |
| taskid == sp_cmd->inter_taskid) |
| return sp_cmd->cb_idx; |
| |
| WARN_ON(cb_idx == 0xFF); |
| return cb_idx; |
| } |
| |
| struct scsi_cmnd *leapraid_get_scmd_from_taskid( |
| struct leapraid_adapter *adapter, |
| u16 taskid) |
| { |
| struct leapraid_scsiio_req *leap_mpi_req; |
| struct leapraid_io_req_tracker *st; |
| struct scsi_cmnd *scmd; |
| u32 uniq_tag; |
| |
| if (taskid <= 0 || taskid > adapter->shost->can_queue) |
| return NULL; |
| |
| uniq_tag = adapter->mem_desc.taskid_to_uniq_tag[taskid - 1] << |
| BLK_MQ_UNIQUE_TAG_BITS | (taskid - 1); |
| leap_mpi_req = leapraid_get_task_desc(adapter, taskid); |
| if (!leap_mpi_req->dev_hdl) |
| return NULL; |
| |
| scmd = scsi_host_find_tag(adapter->shost, uniq_tag); |
| if (scmd) { |
| st = scsi_cmd_priv(scmd); |
| if (st && st->taskid == taskid) |
| return scmd; |
| } |
| |
| return NULL; |
| } |
| |
| u16 leapraid_alloc_scsiio_taskid(struct leapraid_adapter *adapter, |
| struct scsi_cmnd *scmd) |
| { |
| struct leapraid_io_req_tracker *request; |
| u16 taskid; |
| u32 tag = scsi_cmd_to_rq(scmd)->tag; |
| u32 unique_tag; |
| |
| unique_tag = blk_mq_unique_tag(scsi_cmd_to_rq(scmd)); |
| tag = blk_mq_unique_tag_to_tag(unique_tag); |
| adapter->mem_desc.taskid_to_uniq_tag[tag] = |
| blk_mq_unique_tag_to_hwq(unique_tag); |
| |
| request = scsi_cmd_priv(scmd); |
| taskid = tag + 1; |
| request->taskid = taskid; |
| request->scmd = scmd; |
| return taskid; |
| } |
| |
| static void leapraid_check_pending_io(struct leapraid_adapter *adapter) |
| { |
| if (adapter->access_ctrl.shost_recovering && |
| adapter->reset_desc.pending_io_cnt) { |
| if (adapter->reset_desc.pending_io_cnt == 1) |
| wake_up(&adapter->reset_desc.reset_wait_queue); |
| adapter->reset_desc.pending_io_cnt--; |
| } |
| } |
| |
| static void leapraid_clear_io_tracker( |
| struct leapraid_adapter *adapter, |
| struct leapraid_io_req_tracker *io_tracker) |
| { |
| if (!io_tracker) |
| return; |
| |
| if (WARN_ON(io_tracker->taskid == 0)) |
| return; |
| |
| io_tracker->scmd = NULL; |
| } |
| |
| static bool leapraid_is_fixed_taskid(struct leapraid_adapter *adapter, |
| u16 taskid) |
| { |
| struct leapraid_driver_cmds *driver_cmds = &adapter->driver_cmds; |
| |
| return (taskid == driver_cmds->ctl_cmd.taskid || |
| taskid == driver_cmds->tm_cmd.hp_taskid || |
| taskid == driver_cmds->ctl_cmd.hp_taskid || |
| taskid == driver_cmds->scan_dev_cmd.inter_taskid || |
| taskid == driver_cmds->transport_cmd.inter_taskid || |
| taskid == driver_cmds->cfg_op_cmd.inter_taskid || |
| taskid == driver_cmds->enc_cmd.inter_taskid || |
| taskid == driver_cmds->notify_event_cmd.inter_taskid); |
| } |
| |
| void leapraid_free_taskid(struct leapraid_adapter *adapter, u16 taskid) |
| { |
| struct leapraid_io_req_tracker *io_tracker; |
| void *task_desc; |
| |
| if (leapraid_is_fixed_taskid(adapter, taskid)) |
| return; |
| |
| if (taskid <= adapter->shost->can_queue) { |
| io_tracker = leapraid_get_io_tracker_from_taskid(adapter, |
| taskid); |
| if (!io_tracker) { |
| leapraid_check_pending_io(adapter); |
| return; |
| } |
| |
| task_desc = leapraid_get_task_desc(adapter, taskid); |
| memset(task_desc, 0, LEAPRAID_REQUEST_SIZE); |
| leapraid_clear_io_tracker(adapter, io_tracker); |
| leapraid_check_pending_io(adapter); |
| adapter->mem_desc.taskid_to_uniq_tag[taskid - 1] = 0xFFFF; |
| } |
| } |
| |
| static u8 leapraid_get_msix_idx(struct leapraid_adapter *adapter, |
| struct scsi_cmnd *scmd) |
| { |
| if (scmd && adapter->shost->nr_hw_queues > 1) { |
| u32 tag = blk_mq_unique_tag(scsi_cmd_to_rq(scmd)); |
| |
| return blk_mq_unique_tag_to_hwq(tag); |
| } |
| return adapter->notification_desc.msix_cpu_map[raw_smp_processor_id()]; |
| } |
| |
| static u8 leapraid_get_and_set_msix_idx_from_taskid( |
| struct leapraid_adapter *adapter, u16 taskid) |
| { |
| struct leapraid_io_req_tracker *io_tracker = NULL; |
| |
| if (taskid <= adapter->shost->can_queue) |
| io_tracker = leapraid_get_io_tracker_from_taskid(adapter, |
| taskid); |
| |
| if (!io_tracker) |
| return leapraid_get_msix_idx(adapter, NULL); |
| |
| io_tracker->msix_io = leapraid_get_msix_idx(adapter, io_tracker->scmd); |
| |
| return io_tracker->msix_io; |
| } |
| |
| void leapraid_fire_scsi_io(struct leapraid_adapter *adapter, u16 taskid, |
| u16 handle) |
| { |
| struct leapraid_atomic_req_desc desc; |
| |
| desc.flg = LEAPRAID_REQ_DESC_FLG_SCSI_IO; |
| desc.msix_idx = leapraid_get_and_set_msix_idx_from_taskid(adapter, |
| taskid); |
| desc.taskid = cpu_to_le16(taskid); |
| writel((__force u32)cpu_to_le32(*((u32 *)&desc)), |
| &adapter->iomem_base->atomic_req_desc_post); |
| } |
| |
| void leapraid_fire_hpr_task(struct leapraid_adapter *adapter, u16 taskid, |
| u16 msix_task) |
| { |
| struct leapraid_atomic_req_desc desc; |
| |
| desc.flg = LEAPRAID_REQ_DESC_FLG_HPR; |
| desc.msix_idx = msix_task; |
| desc.taskid = cpu_to_le16(taskid); |
| writel((__force u32)cpu_to_le32(*((u32 *)&desc)), |
| &adapter->iomem_base->atomic_req_desc_post); |
| } |
| |
| void leapraid_fire_task(struct leapraid_adapter *adapter, u16 taskid) |
| { |
| struct leapraid_atomic_req_desc desc; |
| |
| desc.flg = LEAPRAID_REQ_DESC_FLG_DFLT_TYPE; |
| desc.msix_idx = leapraid_get_and_set_msix_idx_from_taskid(adapter, |
| taskid); |
| desc.taskid = cpu_to_le16(taskid); |
| writel((__force u32)cpu_to_le32(*((u32 *)&desc)), |
| &adapter->iomem_base->atomic_req_desc_post); |
| } |
| |
| void leapraid_clean_active_scsi_cmds(struct leapraid_adapter *adapter) |
| { |
| struct leapraid_io_req_tracker *io_tracker; |
| struct scsi_cmnd *scmd; |
| void *task_desc; |
| u16 taskid; |
| |
| leapraid_sync_irqs_for_cleanup(adapter); |
| |
| for (taskid = 1; taskid <= adapter->shost->can_queue; taskid++) { |
| scmd = leapraid_get_scmd_from_taskid(adapter, taskid); |
| if (!scmd) |
| continue; |
| |
| io_tracker = scsi_cmd_priv(scmd); |
| if (io_tracker && io_tracker->taskid == 0) |
| continue; |
| |
| scsi_dma_unmap(scmd); |
| task_desc = leapraid_get_task_desc(adapter, taskid); |
| memset(task_desc, 0, LEAPRAID_REQUEST_SIZE); |
| leapraid_clear_io_tracker(adapter, io_tracker); |
| if (!leapraid_pci_active(adapter) || |
| adapter->reset_desc.adapter_reset_results != 0 || |
| atomic_read(&adapter->overheat_desc.thermal_alert) || |
| adapter->access_ctrl.host_removing) |
| scmd->result = DID_NO_CONNECT << 16; |
| else |
| scmd->result = DID_RESET << LEAPRAID_SCSI_HOST_SHIFT; |
| scsi_done(scmd); |
| } |
| } |
| |
| static void leapraid_clean_active_driver_cmd(struct leapraid_driver_cmd *cmd) |
| { |
| if (cmd->status & LEAPRAID_CMD_PENDING) { |
| cmd->status |= LEAPRAID_CMD_RESET; |
| complete(&cmd->done); |
| } |
| } |
| |
| static void leapraid_clean_active_driver_cmds(struct leapraid_adapter *adapter) |
| { |
| struct leapraid_driver_cmds *driver_cmds; |
| |
| driver_cmds = &adapter->driver_cmds; |
| leapraid_clean_active_driver_cmd(&driver_cmds->tm_cmd); |
| leapraid_clean_active_driver_cmd(&driver_cmds->transport_cmd); |
| leapraid_clean_active_driver_cmd(&driver_cmds->enc_cmd); |
| leapraid_clean_active_driver_cmd(&driver_cmds->notify_event_cmd); |
| leapraid_clean_active_driver_cmd(&driver_cmds->cfg_op_cmd); |
| leapraid_clean_active_driver_cmd(&driver_cmds->ctl_cmd); |
| |
| if (driver_cmds->scan_dev_cmd.status & LEAPRAID_CMD_PENDING) { |
| adapter->scan_dev_desc.scan_dev_failed = 1; |
| driver_cmds->scan_dev_cmd.status |= LEAPRAID_CMD_RESET; |
| if (adapter->scan_dev_desc.driver_loading) { |
| adapter->scan_dev_desc.scan_start_failed = |
| LEAPRAID_ADAPTER_STATUS_INTERNAL_ERROR; |
| adapter->scan_dev_desc.scan_start = 0; |
| } else { |
| complete(&driver_cmds->scan_dev_cmd.done); |
| } |
| } |
| } |
| |
| static void leapraid_clean_active_cmds(struct leapraid_adapter *adapter) |
| { |
| leapraid_clean_active_driver_cmds(adapter); |
| leapraid_clean_active_fw_evt(adapter); |
| leapraid_clean_active_scsi_cmds(adapter); |
| } |
| |
| static void leapraid_tgt_not_responding(struct leapraid_adapter *adapter, |
| u16 hdl) |
| { |
| struct leapraid_starget_priv *starget_priv = NULL; |
| struct leapraid_sas_dev *sas_dev; |
| unsigned long flags = 0; |
| u32 adapter_state; |
| |
| if (adapter->access_ctrl.pcie_recovering) |
| return; |
| |
| adapter_state = leapraid_get_adapter_state(adapter); |
| if (adapter_state != LEAPRAID_DB_OPERATIONAL) |
| return; |
| |
| if (!hdl || hdl > adapter->adapter_attr.features.max_dev_handle || |
| test_bit(hdl, adapter->dev_topo.pd_hdls)) |
| return; |
| |
| spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); |
| sas_dev = leapraid_hold_lock_get_sas_dev_by_hdl(adapter, hdl); |
| if (sas_dev && sas_dev->starget && sas_dev->starget->hostdata) { |
| starget_priv = sas_dev->starget->hostdata; |
| starget_priv->deleted = 1; |
| } |
| spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); |
| |
| if (starget_priv) |
| starget_priv->hdl = LEAPRAID_INVALID_DEV_HANDLE; |
| |
| if (sas_dev) |
| leapraid_sdev_put(sas_dev); |
| } |
| |
| static void leapraid_tgt_rst_send(struct leapraid_adapter *adapter, u16 hdl) |
| { |
| struct leapraid_starget_priv *starget_priv = NULL; |
| struct leapraid_sas_dev *sas_dev; |
| struct leapraid_card_port *port; |
| u64 sas_address; |
| unsigned long flags; |
| u32 adapter_state; |
| |
| if (adapter->access_ctrl.pcie_recovering) |
| return; |
| |
| adapter_state = leapraid_get_adapter_state(adapter); |
| if (adapter_state != LEAPRAID_DB_OPERATIONAL) |
| return; |
| |
| if (!hdl || hdl > adapter->adapter_attr.features.max_dev_handle || |
| test_bit(hdl, adapter->dev_topo.pd_hdls)) |
| return; |
| |
| spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); |
| sas_dev = leapraid_hold_lock_get_sas_dev_by_hdl(adapter, hdl); |
| if (sas_dev && sas_dev->starget && sas_dev->starget->hostdata) { |
| starget_priv = sas_dev->starget->hostdata; |
| starget_priv->deleted = 1; |
| sas_address = sas_dev->sas_addr; |
| port = sas_dev->card_port; |
| } |
| spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); |
| |
| if (starget_priv) { |
| leapraid_ublk_io_dev(adapter, sas_address, port); |
| starget_priv->hdl = LEAPRAID_INVALID_DEV_HANDLE; |
| } |
| if (sas_dev) |
| leapraid_sdev_put(sas_dev); |
| } |
| |
| static inline void leapraid_single_mpi_sg_append( |
| struct leapraid_adapter *adapter, |
| void *sge, |
| u32 flag_and_len, |
| dma_addr_t dma_addr) |
| { |
| if (adapter->adapter_attr.use_32_dma_mask) { |
| struct leapraid_sge_simple32 *sge32 = sge; |
| |
| sge32->flg_and_len = |
| cpu_to_le32(flag_and_len | |
| (LEAPRAID_SGE_FLG_32 | |
| LEAPRAID_SGE_FLG_SYSTEM_ADDR) << |
| LEAPRAID_SGE_FLG_SHIFT); |
| sge32->addr = cpu_to_le32(dma_addr); |
| } else { |
| struct leapraid_sge_simple64 *sge64 = sge; |
| |
| sge64->flg_and_len = |
| cpu_to_le32(flag_and_len | |
| (LEAPRAID_SGE_FLG_64 | |
| LEAPRAID_SGE_FLG_SYSTEM_ADDR) << |
| LEAPRAID_SGE_FLG_SHIFT); |
| sge64->addr = cpu_to_le64(dma_addr); |
| } |
| } |
| |
| static inline void leapraid_single_ieee_sg_append(void *sge, u8 flag, |
| u8 next_chain_offset, |
| u32 len, |
| dma_addr_t dma_addr) |
| { |
| struct leapraid_chain64_ieee_sg *ieee_sg = sge; |
| |
| ieee_sg->flg = flag; |
| ieee_sg->next_chain_offset = next_chain_offset; |
| ieee_sg->len = cpu_to_le32(len); |
| ieee_sg->addr = cpu_to_le64(dma_addr); |
| } |
| |
| static void leapraid_build_nodata_mpi_sg(struct leapraid_adapter *adapter, |
| void *sge) |
| { |
| leapraid_single_mpi_sg_append(adapter, |
| sge, |
| (LEAPRAID_SGE_FLG_LAST_ONE | |
| LEAPRAID_SGE_FLG_EOB | |
| LEAPRAID_SGE_FLG_EOL | |
| LEAPRAID_SGE_FLG_SIMPLE_ONE) << |
| LEAPRAID_SGE_FLG_SHIFT, |
| LEAPRAID_SGE_NO_DATA_ADDR); |
| } |
| |
| void leapraid_build_mpi_sg(struct leapraid_adapter *adapter, void *sge, |
| dma_addr_t h2c_dma_addr, size_t h2c_size, |
| dma_addr_t c2h_dma_addr, size_t c2h_size) |
| { |
| if (h2c_size && !c2h_size) { |
| leapraid_single_mpi_sg_append(adapter, |
| sge, |
| ((LEAPRAID_SGE_FLG_SIMPLE_ONE | |
| LEAPRAID_SGE_FLG_LAST_ONE | |
| LEAPRAID_SGE_FLG_EOB | |
| LEAPRAID_SGE_FLG_EOL | |
| LEAPRAID_SGE_FLG_H2C) << |
| LEAPRAID_SGE_FLG_SHIFT) | |
| h2c_size, |
| h2c_dma_addr); |
| } else if (!h2c_size && c2h_size) { |
| leapraid_single_mpi_sg_append(adapter, |
| sge, |
| ((LEAPRAID_SGE_FLG_SIMPLE_ONE | |
| LEAPRAID_SGE_FLG_LAST_ONE | |
| LEAPRAID_SGE_FLG_EOB | |
| LEAPRAID_SGE_FLG_EOL) << |
| LEAPRAID_SGE_FLG_SHIFT) | |
| c2h_size, |
| c2h_dma_addr); |
| } else if (h2c_size && c2h_size) { |
| leapraid_single_mpi_sg_append(adapter, |
| sge, |
| ((LEAPRAID_SGE_FLG_SIMPLE_ONE | |
| LEAPRAID_SGE_FLG_EOB | |
| LEAPRAID_SGE_FLG_H2C) << |
| LEAPRAID_SGE_FLG_SHIFT) | |
| h2c_size, |
| h2c_dma_addr); |
| if (adapter->adapter_attr.use_32_dma_mask) |
| sge += sizeof(struct leapraid_sge_simple32); |
| else |
| sge += sizeof(struct leapraid_sge_simple64); |
| leapraid_single_mpi_sg_append(adapter, |
| sge, |
| ((LEAPRAID_SGE_FLG_SIMPLE_ONE | |
| LEAPRAID_SGE_FLG_LAST_ONE | |
| LEAPRAID_SGE_FLG_EOB | |
| LEAPRAID_SGE_FLG_EOL) << |
| LEAPRAID_SGE_FLG_SHIFT) | |
| c2h_size, |
| c2h_dma_addr); |
| } else { |
| leapraid_build_nodata_mpi_sg(adapter, sge); |
| } |
| } |
| |
| void leapraid_build_ieee_nodata_sg(struct leapraid_adapter *adapter, void *sge) |
| { |
| leapraid_single_ieee_sg_append(sge, |
| (LEAPRAID_IEEE_SGE_FLG_SIMPLE_ONE | |
| LEAPRAID_IEEE_SGE_FLG_SYSTEM_ADDR | |
| LEAPRAID_IEEE_SGE_FLG_EOL), |
| 0, |
| 0, |
| LEAPRAID_SGE_NO_DATA_ADDR); |
| } |
| |
| int leapraid_build_scmd_ieee_sg(struct leapraid_adapter *adapter, |
| struct scsi_cmnd *scmd, u16 taskid) |
| { |
| struct leapraid_scsiio_req *scsiio_req; |
| struct leapraid_io_req_tracker *io_tracker; |
| struct scatterlist *scmd_sg_cur; |
| int sg_entries_left; |
| void *sg_entry_cur; |
| void *host_chain; |
| dma_addr_t host_chain_dma; |
| u8 host_chain_cursor; |
| u32 sg_entries_in_cur_seg; |
| u32 chain_offset_in_cur_seg; |
| u32 chain_len_in_cur_seg; |
| |
| io_tracker = scsi_cmd_priv(scmd); |
| scsiio_req = leapraid_get_task_desc(adapter, taskid); |
| scmd_sg_cur = scsi_sglist(scmd); |
| sg_entries_left = scsi_dma_map(scmd); |
| if (sg_entries_left < 0) |
| return -ENOMEM; |
| sg_entry_cur = &scsiio_req->sgl; |
| if (sg_entries_left <= LEAPRAID_SGL_INLINE_THRESHOLD) |
| goto fill_last_seg; |
| |
| scsiio_req->chain_offset = LEAPRAID_CHAIN_OFFSET_DWORDS; |
| leapraid_single_ieee_sg_append(sg_entry_cur, |
| LEAPRAID_IEEE_SGE_FLG_SIMPLE_ONE | |
| LEAPRAID_IEEE_SGE_FLG_SYSTEM_ADDR, |
| 0, sg_dma_len(scmd_sg_cur), |
| sg_dma_address(scmd_sg_cur)); |
| scmd_sg_cur = sg_next(scmd_sg_cur); |
| sg_entry_cur += LEAPRAID_IEEE_SGE64_ENTRY_SIZE; |
| sg_entries_left--; |
| |
| host_chain_cursor = 0; |
| host_chain = io_tracker->chain + |
| host_chain_cursor * LEAPRAID_CHAIN_SEG_SIZE; |
| host_chain_dma = io_tracker->chain_dma + |
| host_chain_cursor * LEAPRAID_CHAIN_SEG_SIZE; |
| host_chain_cursor += 1; |
| for (;;) { |
| sg_entries_in_cur_seg = |
| (sg_entries_left <= LEAPRAID_MAX_SGES_IN_CHAIN) ? |
| sg_entries_left : LEAPRAID_MAX_SGES_IN_CHAIN; |
| chain_offset_in_cur_seg = |
| (sg_entries_left == (int)sg_entries_in_cur_seg) ? |
| 0 : sg_entries_in_cur_seg; |
| chain_len_in_cur_seg = sg_entries_in_cur_seg * |
| LEAPRAID_IEEE_SGE64_ENTRY_SIZE; |
| if (chain_offset_in_cur_seg) |
| chain_len_in_cur_seg += LEAPRAID_IEEE_SGE64_ENTRY_SIZE; |
| |
| leapraid_single_ieee_sg_append( |
| sg_entry_cur, |
| LEAPRAID_IEEE_SGE_FLG_CHAIN_ONE | |
| LEAPRAID_IEEE_SGE_FLG_SYSTEM_ADDR, |
| chain_offset_in_cur_seg, chain_len_in_cur_seg, |
| host_chain_dma); |
| sg_entry_cur = host_chain; |
| if (!chain_offset_in_cur_seg) |
| goto fill_last_seg; |
| |
| while (sg_entries_in_cur_seg) { |
| leapraid_single_ieee_sg_append( |
| sg_entry_cur, |
| LEAPRAID_IEEE_SGE_FLG_SIMPLE_ONE | |
| LEAPRAID_IEEE_SGE_FLG_SYSTEM_ADDR, |
| 0, sg_dma_len(scmd_sg_cur), |
| sg_dma_address(scmd_sg_cur)); |
| scmd_sg_cur = sg_next(scmd_sg_cur); |
| sg_entry_cur += LEAPRAID_IEEE_SGE64_ENTRY_SIZE; |
| sg_entries_left--; |
| sg_entries_in_cur_seg--; |
| } |
| host_chain = io_tracker->chain + |
| host_chain_cursor * LEAPRAID_CHAIN_SEG_SIZE; |
| host_chain_dma = io_tracker->chain_dma + |
| host_chain_cursor * LEAPRAID_CHAIN_SEG_SIZE; |
| host_chain_cursor += 1; |
| } |
| |
| fill_last_seg: |
| while (sg_entries_left > 0) { |
| u32 flags = LEAPRAID_IEEE_SGE_FLG_SIMPLE_ONE | |
| LEAPRAID_IEEE_SGE_FLG_SYSTEM_ADDR; |
| if (sg_entries_left == 1) |
| flags |= LEAPRAID_IEEE_SGE_FLG_EOL; |
| leapraid_single_ieee_sg_append(sg_entry_cur, flags, |
| 0, sg_dma_len(scmd_sg_cur), |
| sg_dma_address(scmd_sg_cur)); |
| scmd_sg_cur = sg_next(scmd_sg_cur); |
| sg_entry_cur += LEAPRAID_IEEE_SGE64_ENTRY_SIZE; |
| sg_entries_left--; |
| } |
| return 0; |
| } |
| |
| void leapraid_build_ieee_sg(struct leapraid_adapter *adapter, void *sge, |
| dma_addr_t h2c_dma_addr, size_t h2c_size, |
| dma_addr_t c2h_dma_addr, size_t c2h_size) |
| { |
| u32 base_flag; |
| u32 flag; |
| |
| base_flag = LEAPRAID_IEEE_SGE_FLG_SIMPLE_ONE | |
| LEAPRAID_IEEE_SGE_FLG_SYSTEM_ADDR; |
| |
| if (h2c_size && !c2h_size) { |
| flag = base_flag | LEAPRAID_IEEE_SGE_FLG_EOL; |
| |
| leapraid_single_ieee_sg_append(sge, |
| flag, |
| 0, |
| h2c_size, |
| h2c_dma_addr); |
| } else if (!h2c_size && c2h_size) { |
| flag = base_flag | LEAPRAID_IEEE_SGE_FLG_EOL; |
| |
| leapraid_single_ieee_sg_append(sge, |
| flag, |
| 0, |
| c2h_size, |
| c2h_dma_addr); |
| } else if (h2c_size && c2h_size) { |
| leapraid_single_ieee_sg_append(sge, |
| base_flag, |
| 0, |
| h2c_size, |
| h2c_dma_addr); |
| sge += LEAPRAID_IEEE_SGE64_ENTRY_SIZE; |
| |
| flag = base_flag | LEAPRAID_IEEE_SGE_FLG_EOL; |
| leapraid_single_ieee_sg_append(sge, |
| flag, |
| 0, |
| c2h_size, |
| c2h_dma_addr); |
| } else { |
| leapraid_build_ieee_nodata_sg(adapter, sge); |
| } |
| } |
| |
| struct leapraid_sas_dev *leapraid_hold_lock_get_sas_dev_from_tgt( |
| struct leapraid_adapter *adapter, |
| struct leapraid_starget_priv *tgt_priv) |
| { |
| assert_spin_locked(&adapter->dev_topo.sas_dev_lock); |
| if (tgt_priv->sas_dev) |
| leapraid_sdev_get(tgt_priv->sas_dev); |
| |
| return tgt_priv->sas_dev; |
| } |
| |
| struct leapraid_sas_dev *leapraid_get_sas_dev_from_tgt( |
| struct leapraid_adapter *adapter, |
| struct leapraid_starget_priv *tgt_priv) |
| { |
| struct leapraid_sas_dev *sas_dev; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); |
| sas_dev = leapraid_hold_lock_get_sas_dev_from_tgt(adapter, tgt_priv); |
| spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); |
| return sas_dev; |
| } |
| |
| static struct leapraid_card_port *leapraid_get_port_by_id( |
| struct leapraid_adapter *adapter, |
| u8 port_id, bool skip_dirty) |
| { |
| struct leapraid_card_port *port; |
| struct leapraid_card_port *dirty_port = NULL; |
| |
| if (!adapter->adapter_attr.enable_mp) |
| port_id = LEAPRAID_DISABLE_MP_PORT_ID; |
| |
| list_for_each_entry(port, &adapter->dev_topo.card_port_list, list) { |
| if (port->port_id != port_id) |
| continue; |
| |
| if (!(port->flg & LEAPRAID_CARD_PORT_FLG_DIRTY)) |
| return port; |
| |
| if (skip_dirty && !dirty_port) |
| dirty_port = port; |
| } |
| |
| if (dirty_port) |
| return dirty_port; |
| |
| if (unlikely(!adapter->adapter_attr.enable_mp)) { |
| port = kzalloc_obj(*port, GFP_ATOMIC); |
| if (!port) { |
| dev_warn(&adapter->pdev->dev, |
| "%s: Failed to alloc port\n", __func__); |
| return NULL; |
| } |
| |
| port->port_id = LEAPRAID_DISABLE_MP_PORT_ID; |
| list_add_tail(&port->list, &adapter->dev_topo.card_port_list); |
| return port; |
| } |
| |
| return NULL; |
| } |
| |
| struct leapraid_vphy *leapraid_get_vphy_by_phy(struct leapraid_card_port *port, |
| u32 phy_seq_num) |
| { |
| struct leapraid_vphy *vphy; |
| |
| if (!port || !port->vphys_mask) |
| return NULL; |
| |
| list_for_each_entry(vphy, &port->vphys_list, list) { |
| if (vphy->phy_mask & BIT(phy_seq_num)) |
| return vphy; |
| } |
| |
| return NULL; |
| } |
| |
| struct leapraid_sas_dev *leapraid_hold_lock_get_sas_dev_by_addr_and_rphy( |
| struct leapraid_adapter *adapter, |
| u64 sas_address, |
| struct sas_rphy *rphy) |
| { |
| struct leapraid_sas_dev *sas_dev; |
| |
| assert_spin_locked(&adapter->dev_topo.sas_dev_lock); |
| list_for_each_entry(sas_dev, &adapter->dev_topo.sas_dev_list, list) |
| if (sas_dev->sas_addr == sas_address && |
| sas_dev->rphy == rphy) { |
| leapraid_sdev_get(sas_dev); |
| return sas_dev; |
| } |
| |
| list_for_each_entry(sas_dev, &adapter->dev_topo.sas_dev_init_list, |
| list) |
| if (sas_dev->sas_addr == sas_address && |
| sas_dev->rphy == rphy) { |
| leapraid_sdev_get(sas_dev); |
| return sas_dev; |
| } |
| |
| return NULL; |
| } |
| |
| struct leapraid_sas_dev *leapraid_hold_lock_get_sas_dev_by_addr( |
| struct leapraid_adapter *adapter, |
| u64 sas_address, struct leapraid_card_port *port) |
| { |
| struct leapraid_sas_dev *sas_dev; |
| |
| if (!port) { |
| dev_warn(&adapter->pdev->dev, "%s: Invalid port\n", __func__); |
| return NULL; |
| } |
| |
| assert_spin_locked(&adapter->dev_topo.sas_dev_lock); |
| list_for_each_entry(sas_dev, &adapter->dev_topo.sas_dev_list, list) |
| if (sas_dev->sas_addr == sas_address && |
| sas_dev->card_port == port) { |
| leapraid_sdev_get(sas_dev); |
| return sas_dev; |
| } |
| |
| list_for_each_entry(sas_dev, &adapter->dev_topo.sas_dev_init_list, |
| list) |
| if (sas_dev->sas_addr == sas_address && |
| sas_dev->card_port == port) { |
| leapraid_sdev_get(sas_dev); |
| return sas_dev; |
| } |
| |
| return NULL; |
| } |
| |
| struct leapraid_sas_dev *leapraid_get_sas_dev_by_addr( |
| struct leapraid_adapter *adapter, |
| u64 sas_address, struct leapraid_card_port *port) |
| { |
| struct leapraid_sas_dev *sas_dev; |
| unsigned long flags; |
| |
| if (!port) { |
| dev_warn(&adapter->pdev->dev, "%s: Invalid port\n", __func__); |
| return NULL; |
| } |
| |
| spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); |
| sas_dev = leapraid_hold_lock_get_sas_dev_by_addr(adapter, sas_address, |
| port); |
| spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); |
| return sas_dev; |
| } |
| |
| struct leapraid_sas_dev *leapraid_hold_lock_get_sas_dev_by_hdl( |
| struct leapraid_adapter *adapter, u16 hdl) |
| { |
| struct leapraid_sas_dev *sas_dev; |
| |
| assert_spin_locked(&adapter->dev_topo.sas_dev_lock); |
| list_for_each_entry(sas_dev, &adapter->dev_topo.sas_dev_list, list) |
| if (sas_dev->hdl == hdl) { |
| leapraid_sdev_get(sas_dev); |
| return sas_dev; |
| } |
| |
| list_for_each_entry(sas_dev, &adapter->dev_topo.sas_dev_init_list, |
| list) |
| if (sas_dev->hdl == hdl) { |
| leapraid_sdev_get(sas_dev); |
| return sas_dev; |
| } |
| |
| return NULL; |
| } |
| |
| struct leapraid_sas_dev *leapraid_get_sas_dev_by_hdl( |
| struct leapraid_adapter *adapter, u16 hdl) |
| { |
| struct leapraid_sas_dev *sas_dev; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); |
| sas_dev = leapraid_hold_lock_get_sas_dev_by_hdl(adapter, hdl); |
| spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); |
| return sas_dev; |
| } |
| |
| void leapraid_sas_dev_remove(struct leapraid_adapter *adapter, |
| struct leapraid_sas_dev *sas_dev) |
| { |
| unsigned long flags; |
| bool del_from_list; |
| |
| if (!sas_dev) { |
| dev_warn(&adapter->pdev->dev, |
| "%s: Invalid SAS device\n", __func__); |
| return; |
| } |
| |
| del_from_list = false; |
| spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); |
| if (!list_empty(&sas_dev->list)) { |
| list_del_init(&sas_dev->list); |
| del_from_list = true; |
| } |
| spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); |
| |
| if (del_from_list) { |
| leapraid_clear_cached_boot_dev(adapter, sas_dev, 0); |
| leapraid_sdev_put(sas_dev); |
| } |
| } |
| |
| static void leapraid_sas_dev_remove_by_hdl(struct leapraid_adapter *adapter, |
| u16 hdl) |
| { |
| struct leapraid_sas_dev *sas_dev; |
| unsigned long flags; |
| bool del_from_list; |
| |
| if (adapter->access_ctrl.shost_recovering) |
| return; |
| |
| del_from_list = false; |
| spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); |
| sas_dev = leapraid_hold_lock_get_sas_dev_by_hdl(adapter, hdl); |
| if (sas_dev && (!list_empty(&sas_dev->list))) { |
| list_del_init(&sas_dev->list); |
| del_from_list = true; |
| leapraid_sdev_put(sas_dev); |
| } |
| spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); |
| |
| if (del_from_list) { |
| leapraid_remove_device(adapter, sas_dev); |
| leapraid_sdev_put(sas_dev); |
| } |
| } |
| |
| void leapraid_sas_dev_remove_by_sas_address(struct leapraid_adapter *adapter, |
| u64 sas_address, |
| struct leapraid_card_port *port) |
| { |
| struct leapraid_sas_dev *sas_dev; |
| unsigned long flags; |
| bool del_from_list; |
| |
| if (adapter->access_ctrl.shost_recovering) |
| return; |
| |
| del_from_list = false; |
| spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); |
| sas_dev = leapraid_hold_lock_get_sas_dev_by_addr(adapter, sas_address, |
| port); |
| if (sas_dev && (!list_empty(&sas_dev->list))) { |
| list_del_init(&sas_dev->list); |
| del_from_list = true; |
| leapraid_sdev_put(sas_dev); |
| } |
| spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); |
| |
| if (del_from_list) { |
| leapraid_remove_device(adapter, sas_dev); |
| leapraid_sdev_put(sas_dev); |
| } |
| } |
| |
| struct leapraid_raid_volume *leapraid_raid_volume_find_by_id( |
| struct leapraid_adapter *adapter, uint id, uint channel) |
| { |
| struct leapraid_raid_volume *raid_volume; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); |
| list_for_each_entry(raid_volume, &adapter->dev_topo.raid_volume_list, |
| list) { |
| if (raid_volume->id == id && raid_volume->channel == channel) { |
| leapraid_raid_volume_get(raid_volume); |
| spin_unlock_irqrestore( |
| &adapter->dev_topo.raid_volume_lock, flags); |
| return raid_volume; |
| } |
| } |
| spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags); |
| |
| return NULL; |
| } |
| |
| struct leapraid_raid_volume *leapraid_raid_volume_find_by_hdl( |
| struct leapraid_adapter *adapter, u16 hdl) |
| { |
| struct leapraid_raid_volume *raid_volume; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); |
| list_for_each_entry(raid_volume, &adapter->dev_topo.raid_volume_list, |
| list) { |
| if (raid_volume->hdl == hdl) { |
| leapraid_raid_volume_get(raid_volume); |
| spin_unlock_irqrestore( |
| &adapter->dev_topo.raid_volume_lock, flags); |
| return raid_volume; |
| } |
| } |
| spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags); |
| |
| return NULL; |
| } |
| |
| static struct leapraid_raid_volume *leapraid_raid_volume_find_by_wwid( |
| struct leapraid_adapter *adapter, u64 wwid) |
| { |
| struct leapraid_raid_volume *raid_volume; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); |
| list_for_each_entry(raid_volume, &adapter->dev_topo.raid_volume_list, |
| list) { |
| if (raid_volume->wwid == wwid) { |
| leapraid_raid_volume_get(raid_volume); |
| spin_unlock_irqrestore( |
| &adapter->dev_topo.raid_volume_lock, flags); |
| return raid_volume; |
| } |
| } |
| spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags); |
| |
| return NULL; |
| } |
| |
| static void leapraid_raid_volume_add(struct leapraid_adapter *adapter, |
| struct leapraid_raid_volume *raid_volume) |
| { |
| unsigned long flags; |
| |
| spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); |
| leapraid_raid_volume_get(raid_volume); |
| list_add_tail(&raid_volume->list, &adapter->dev_topo.raid_volume_list); |
| spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags); |
| } |
| |
| void leapraid_raid_volume_remove(struct leapraid_adapter *adapter, |
| struct leapraid_raid_volume *raid_volume) |
| { |
| unsigned long flags; |
| bool del_from_list = false; |
| |
| if (!raid_volume) { |
| dev_warn(&adapter->pdev->dev, |
| "%s: Invalid RAID volume\n", __func__); |
| return; |
| } |
| |
| spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); |
| if (!list_empty(&raid_volume->list)) { |
| list_del_init(&raid_volume->list); |
| del_from_list = true; |
| } |
| spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags); |
| |
| leapraid_clear_cached_boot_dev(adapter, raid_volume, RAID_CHANNEL); |
| if (del_from_list) |
| leapraid_raid_volume_put(raid_volume); |
| } |
| |
| static struct leapraid_enc_node *leapraid_enc_find_by_hdl( |
| struct leapraid_adapter *adapter, u16 hdl) |
| { |
| struct leapraid_enc_node *enc_dev; |
| |
| list_for_each_entry(enc_dev, &adapter->dev_topo.enc_list, list) |
| if (le16_to_cpu(enc_dev->pg0.enc_hdl) == hdl) |
| return enc_dev; |
| |
| return NULL; |
| } |
| |
| struct leapraid_topo_node *leapraid_exp_find_by_sas_address( |
| struct leapraid_adapter *adapter, |
| u64 sas_address, struct leapraid_card_port *port) |
| { |
| struct leapraid_topo_node *sas_exp; |
| |
| if (!port) { |
| dev_warn(&adapter->pdev->dev, "%s: Invalid port\n", __func__); |
| return NULL; |
| } |
| |
| list_for_each_entry(sas_exp, &adapter->dev_topo.exp_list, list) |
| if (sas_exp->sas_address == sas_address && |
| sas_exp->card_port == port) |
| return sas_exp; |
| |
| dev_warn(&adapter->pdev->dev, |
| "%s: No expander found for SAS addr=0x%016llx port=%p\n", |
| __func__, (unsigned long long)sas_address, port); |
| return NULL; |
| } |
| |
| bool leapraid_scmd_find_by_tgt(struct leapraid_adapter *adapter, uint id, |
| uint channel) |
| { |
| struct scsi_cmnd *scmd; |
| int taskid; |
| |
| for (taskid = 1; taskid <= adapter->shost->can_queue; taskid++) { |
| scmd = leapraid_get_scmd_from_taskid(adapter, taskid); |
| if (!scmd) |
| continue; |
| |
| if (scmd->device->id == id && scmd->device->channel == channel) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool leapraid_scmd_find_by_lun(struct leapraid_adapter *adapter, uint id, |
| unsigned int lun, uint channel) |
| { |
| struct scsi_cmnd *scmd; |
| int taskid; |
| |
| for (taskid = 1; taskid <= adapter->shost->can_queue; taskid++) { |
| scmd = leapraid_get_scmd_from_taskid(adapter, taskid); |
| if (!scmd) |
| continue; |
| |
| if (scmd->device->id == id && |
| scmd->device->channel == channel && |
| scmd->device->lun == lun) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| static struct leapraid_topo_node *leapraid_exp_find_by_hdl( |
| struct leapraid_adapter *adapter, u16 hdl) |
| { |
| struct leapraid_topo_node *sas_exp; |
| |
| list_for_each_entry(sas_exp, &adapter->dev_topo.exp_list, list) |
| if (sas_exp->hdl == hdl) |
| return sas_exp; |
| |
| return NULL; |
| } |
| |
| static enum leapraid_card_port_checking_flg leapraid_get_card_port_feature( |
| struct leapraid_card_port *old_card_port, |
| struct leapraid_card_port *card_port, |
| struct leapraid_card_port_feature *feature) |
| { |
| feature->dirty_flg = |
| old_card_port->flg & LEAPRAID_CARD_PORT_FLG_DIRTY; |
| feature->same_addr = |
| old_card_port->sas_address == card_port->sas_address; |
| feature->exact_phy = |
| old_card_port->phy_mask == card_port->phy_mask; |
| feature->phy_overlap = |
| old_card_port->phy_mask & card_port->phy_mask; |
| feature->same_port = |
| old_card_port->port_id == card_port->port_id; |
| feature->cur_chking_old_port = old_card_port; |
| |
| if (!feature->dirty_flg || !feature->same_addr) |
| return CARD_PORT_SKIP_CHECKING; |
| |
| return CARD_PORT_FURTHER_CHECKING_NEEDED; |
| } |
| |
| static bool leapraid_process_card_port_feature( |
| struct leapraid_card_port_feature *feature) |
| { |
| struct leapraid_card_port *old_card_port; |
| |
| old_card_port = feature->cur_chking_old_port; |
| if (feature->exact_phy) { |
| feature->checking_state = SAME_PORT_WITH_NOTHING_CHANGED; |
| feature->expected_old_port = old_card_port; |
| return true; |
| } |
| |
| if (feature->phy_overlap) { |
| if (feature->same_port) { |
| feature->checking_state = |
| SAME_PORT_WITH_PARTIALLY_CHANGED_PHYS; |
| feature->expected_old_port = old_card_port; |
| } else if (feature->checking_state != |
| SAME_PORT_WITH_PARTIALLY_CHANGED_PHYS) { |
| feature->checking_state = |
| SAME_ADDR_WITH_PARTIALLY_CHANGED_PHYS; |
| feature->expected_old_port = old_card_port; |
| } |
| } else if (feature->checking_state != |
| SAME_PORT_WITH_PARTIALLY_CHANGED_PHYS && |
| feature->checking_state != |
| SAME_ADDR_WITH_PARTIALLY_CHANGED_PHYS) { |
| feature->checking_state = SAME_ADDR_ONLY; |
| feature->expected_old_port = old_card_port; |
| feature->same_addr_port_count++; |
| } |
| |
| return false; |
| } |
| |
| static int leapraid_check_card_port( |
| struct leapraid_adapter *adapter, |
| struct leapraid_card_port *card_port, |
| struct leapraid_card_port **expected_card_port, |
| int *count) |
| { |
| struct leapraid_card_port *old_card_port; |
| struct leapraid_card_port_feature feature; |
| |
| *expected_card_port = NULL; |
| memset(&feature, 0, sizeof(struct leapraid_card_port_feature)); |
| feature.expected_old_port = NULL; |
| feature.same_addr_port_count = 0; |
| feature.checking_state = NEW_CARD_PORT; |
| |
| list_for_each_entry(old_card_port, &adapter->dev_topo.card_port_list, |
| list) { |
| if (leapraid_get_card_port_feature(old_card_port, card_port, |
| &feature)) |
| continue; |
| |
| if (leapraid_process_card_port_feature(&feature)) |
| break; |
| } |
| |
| if (feature.checking_state == SAME_ADDR_ONLY) |
| *count = feature.same_addr_port_count; |
| |
| *expected_card_port = feature.expected_old_port; |
| return feature.checking_state; |
| } |
| |
| static void leapraid_del_phy_part_of_anther_port( |
| struct leapraid_adapter *adapter, |
| struct leapraid_card_port *card_port_table, int index, |
| u8 port_count, int offset) |
| { |
| struct leapraid_topo_node *card_topo_node; |
| bool found = false; |
| int i; |
| |
| card_topo_node = &adapter->dev_topo.card; |
| for (i = 0; i < port_count; i++) { |
| if (i == index) |
| continue; |
| |
| if (card_port_table[i].phy_mask & BIT(offset)) { |
| leapraid_transport_detach_phy_to_port( |
| adapter, |
| card_topo_node, |
| &card_topo_node->card_phy[offset]); |
| found = true; |
| break; |
| } |
| } |
| |
| if (!found) |
| card_port_table[index].phy_mask |= BIT(offset); |
| } |
| |
| static void leapraid_add_or_del_phys_from_existing_port( |
| struct leapraid_adapter *adapter, |
| struct leapraid_card_port *card_port, |
| struct leapraid_card_port *card_port_table, |
| int index, u8 port_count) |
| { |
| struct leapraid_topo_node *card_topo_node; |
| u32 phy_mask_diff; |
| u32 offset; |
| |
| card_topo_node = &adapter->dev_topo.card; |
| phy_mask_diff = card_port->phy_mask ^ |
| card_port_table[index].phy_mask; |
| for (offset = 0; offset < adapter->dev_topo.card.phys_num; offset++) { |
| if (!(phy_mask_diff & BIT(offset))) |
| continue; |
| |
| if (!(card_port_table[index].phy_mask & BIT(offset))) { |
| leapraid_del_phy_part_of_anther_port(adapter, |
| card_port_table, |
| index, port_count, |
| offset); |
| continue; |
| } |
| |
| if (card_topo_node->card_phy[offset].phy_is_assigned) |
| leapraid_transport_detach_phy_to_port( |
| adapter, |
| card_topo_node, |
| &card_topo_node->card_phy[offset]); |
| |
| leapraid_transport_attach_phy_to_port( |
| adapter, |
| card_topo_node, |
| &card_topo_node->card_phy[offset], |
| card_port->sas_address, |
| card_port); |
| } |
| } |
| |
| struct leapraid_sas_dev *leapraid_get_next_sas_dev_from_init_list( |
| struct leapraid_adapter *adapter) |
| { |
| struct leapraid_sas_dev *sas_dev = NULL; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); |
| if (!list_empty(&adapter->dev_topo.sas_dev_init_list)) { |
| sas_dev = list_first_entry(&adapter->dev_topo.sas_dev_init_list, |
| struct leapraid_sas_dev, |
| list); |
| leapraid_sdev_get(sas_dev); |
| } |
| spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); |
| return sas_dev; |
| } |
| |
| static bool leapraid_check_boot_dev_internal(u64 sas_address, u64 dev_name, |
| u64 enc_lid, u16 slot, |
| struct leapraid_boot_dev *boot_dev, |
| u8 form) |
| { |
| struct leapraid_boot_dev_format_sas_wwid *wwid; |
| struct leapraid_boot_dev_format_enc_slot *es; |
| struct leapraid_boot_dev_format_dev_name *dn; |
| void *pg_dev; |
| |
| if (!boot_dev) |
| return false; |
| |
| pg_dev = boot_dev->pg_dev; |
| switch (form & LEAPRAID_BOOTDEV_FORM_MASK) { |
| case LEAPRAID_BOOTDEV_FORM_SAS_WWID: |
| wwid = pg_dev; |
| |
| if (!sas_address) |
| return false; |
| |
| return sas_address == le64_to_cpu(wwid->sas_addr); |
| case LEAPRAID_BOOTDEV_FORM_ENC_SLOT: |
| es = pg_dev; |
| |
| if (!enc_lid) |
| return false; |
| |
| return (enc_lid == le64_to_cpu(es->enc_lid) && |
| slot == le16_to_cpu(es->slot_num)); |
| case LEAPRAID_BOOTDEV_FORM_DEV_NAME: |
| dn = pg_dev; |
| |
| if (!dev_name) |
| return false; |
| |
| return dev_name == le64_to_cpu(dn->dev_name); |
| case LEAPRAID_BOOTDEV_FORM_NONE: |
| default: |
| return false; |
| } |
| } |
| |
| void leapraid_boot_dev_get(void *dev, u32 chnl) |
| { |
| if (!dev) |
| return; |
| |
| if (chnl == RAID_CHANNEL) |
| leapraid_raid_volume_get((struct leapraid_raid_volume *)dev); |
| else |
| leapraid_sdev_get((struct leapraid_sas_dev *)dev); |
| } |
| |
| void leapraid_boot_dev_put(void *dev, u32 chnl) |
| { |
| if (!dev) |
| return; |
| |
| if (chnl == RAID_CHANNEL) |
| leapraid_raid_volume_put((struct leapraid_raid_volume *)dev); |
| else |
| leapraid_sdev_put((struct leapraid_sas_dev *)dev); |
| } |
| |
| static void leapraid_try_set_boot_dev(struct leapraid_boot_dev *boot_dev, |
| u64 sas_addr, u64 dev_name, |
| u64 enc_lid, u16 slot, |
| void *dev, u32 chnl) |
| { |
| bool matched = false; |
| |
| if (boot_dev->dev) |
| return; |
| |
| matched = leapraid_check_boot_dev_internal(sas_addr, dev_name, enc_lid, |
| slot, boot_dev, |
| boot_dev->form); |
| if (matched) { |
| leapraid_boot_dev_get(dev, chnl); |
| boot_dev->dev = dev; |
| boot_dev->chnl = chnl; |
| } |
| } |
| |
| static void leapraid_clear_boot_dev(struct leapraid_adapter *adapter, |
| struct leapraid_boot_dev *boot_dev, |
| void *dev, u32 chnl) |
| { |
| void *cached_dev; |
| u32 cached_chnl; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&adapter->boot_devs.lock, flags); |
| if (boot_dev->dev != dev || boot_dev->chnl != chnl) |
| goto out_unlock; |
| |
| cached_dev = boot_dev->dev; |
| cached_chnl = boot_dev->chnl; |
| boot_dev->dev = NULL; |
| boot_dev->chnl = 0; |
| spin_unlock_irqrestore(&adapter->boot_devs.lock, flags); |
| leapraid_boot_dev_put(cached_dev, cached_chnl); |
| return; |
| |
| out_unlock: |
| spin_unlock_irqrestore(&adapter->boot_devs.lock, flags); |
| } |
| |
| static void leapraid_clear_cached_boot_dev(struct leapraid_adapter *adapter, |
| void *dev, u32 chnl) |
| { |
| leapraid_clear_boot_dev(adapter, |
| &adapter->boot_devs.requested_boot_dev, |
| dev, chnl); |
| leapraid_clear_boot_dev(adapter, |
| &adapter->boot_devs.requested_alt_boot_dev, |
| dev, chnl); |
| leapraid_clear_boot_dev(adapter, |
| &adapter->boot_devs.current_boot_dev, |
| dev, chnl); |
| } |
| |
| static void leapraid_check_boot_dev(struct leapraid_adapter *adapter, |
| void *dev, u32 chnl) |
| { |
| struct leapraid_raid_volume *raid_volume; |
| struct leapraid_sas_dev *sas_dev; |
| u64 sas_addr; |
| u64 dev_name = 0; |
| u64 enc_lid = 0; |
| u16 slot = 0; |
| |
| if (!adapter->scan_dev_desc.driver_loading) |
| return; |
| |
| switch (chnl) { |
| case RAID_CHANNEL: |
| raid_volume = dev; |
| sas_addr = raid_volume->wwid; |
| break; |
| default: |
| sas_dev = dev; |
| sas_addr = sas_dev->sas_addr; |
| dev_name = sas_dev->dev_name; |
| enc_lid = sas_dev->enc_lid; |
| slot = sas_dev->slot; |
| break; |
| } |
| |
| leapraid_try_set_boot_dev(&adapter->boot_devs.requested_boot_dev, |
| sas_addr, dev_name, enc_lid, |
| slot, dev, chnl); |
| leapraid_try_set_boot_dev(&adapter->boot_devs.requested_alt_boot_dev, |
| sas_addr, dev_name, enc_lid, |
| slot, dev, chnl); |
| leapraid_try_set_boot_dev(&adapter->boot_devs.current_boot_dev, |
| sas_addr, dev_name, enc_lid, |
| slot, dev, chnl); |
| } |
| |
| static const char *leapraid_func_name(u8 func) |
| { |
| switch (func) { |
| case LEAPRAID_FUNC_SCSIIO: |
| return "SCSIIO"; |
| case LEAPRAID_FUNC_SCSI_TMF: |
| return "SCSI_TMF"; |
| case LEAPRAID_FUNC_ADAPTER_INIT: |
| return "ADAPTER_INIT"; |
| case LEAPRAID_FUNC_GET_ADAPTER_FEATURES: |
| return "GET_ADAPTER_FEATURES"; |
| case LEAPRAID_FUNC_CONFIG_OP: |
| return "CONFIG_OP"; |
| case LEAPRAID_FUNC_SCAN_DEV: |
| return "SCAN_DEV"; |
| case LEAPRAID_FUNC_EVENT_NOTIFY: |
| return "EVENT_NOTIFY"; |
| case LEAPRAID_FUNC_FW_DOWNLOAD: |
| return "FW_DOWNLOAD"; |
| case LEAPRAID_FUNC_FW_UPLOAD: |
| return "FW_UPLOAD"; |
| case LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH: |
| return "SCSIIO_RAID_PASSTHROUGH"; |
| case LEAPRAID_FUNC_SCSI_ENC_PROCESSOR: |
| return "SCSI_ENC_PROCESSOR"; |
| case LEAPRAID_FUNC_SMP_PASSTHROUGH: |
| return "SMP_PASSTHROUGH"; |
| case LEAPRAID_FUNC_SAS_IO_UNIT_CTRL: |
| return "SAS_IO_UNIT_CTRL"; |
| case LEAPRAID_FUNC_SCSIIO_SATA_PASSTHROUGH: |
| return "SCSIIO_SATA_PASSTHROUGH"; |
| case LEAPRAID_FUNC_ADAPTER_UNIT_RESET: |
| return "ADAPTER_UNIT_RESET"; |
| case LEAPRAID_FUNC_HANDSHAKE: |
| return "HANDSHAKE"; |
| case LEAPRAID_FUNC_LOGBUF_INIT: |
| return "LOGBUF_INIT"; |
| default: |
| return "UNKNOWN"; |
| } |
| } |
| |
| static const char *leapraid_cfg_action_name(u8 action) |
| { |
| switch (action) { |
| case LEAPRAID_CFG_ACT_PAGE_HEADER: |
| return "PAGE_HEADER"; |
| case LEAPRAID_CFG_ACT_PAGE_READ_CUR: |
| return "PAGE_READ_CUR"; |
| case LEAPRAID_CFG_ACT_PAGE_WRITE_CUR: |
| return "PAGE_WRITE_CUR"; |
| default: |
| return "UNKNOWN"; |
| } |
| } |
| |
| static const char *leapraid_cfg_page_type_name(u8 page_type) |
| { |
| switch (page_type) { |
| case LEAPRAID_CFG_PT_IO_UNIT: |
| return "IO_UNIT"; |
| case LEAPRAID_CFG_PT_ADAPTER: |
| return "ADAPTER"; |
| case LEAPRAID_CFG_PT_BIOS: |
| return "BIOS"; |
| case LEAPRAID_CFG_PT_RAID_VOLUME: |
| return "RAID_VOLUME"; |
| case LEAPRAID_CFG_PT_MANUFACTURING: |
| return "MANUFACTURING"; |
| case LEAPRAID_CFG_PT_RAID_PHYSDISK: |
| return "RAID_PHYSDISK"; |
| case LEAPRAID_CFG_PT_EXTENDED: |
| return "EXTENDED"; |
| default: |
| return "UNKNOWN"; |
| } |
| } |
| |
| static const char *leapraid_cfg_ext_page_type_name(u8 ext_page_type) |
| { |
| switch (ext_page_type) { |
| case LEAPRAID_CFG_EXTPT_SAS_IO_UNIT: |
| return "SAS_IO_UNIT"; |
| case LEAPRAID_CFG_EXTPT_SAS_EXP: |
| return "SAS_EXPANDER"; |
| case LEAPRAID_CFG_EXTPT_SAS_DEV: |
| return "SAS_DEVICE"; |
| case LEAPRAID_CFG_EXTPT_SAS_PHY: |
| return "SAS_PHY"; |
| case LEAPRAID_CFG_EXTPT_ENC: |
| return "ENCLOSURE"; |
| case LEAPRAID_CFG_EXTPT_RAID_CONFIG: |
| return "RAID_CONFIG"; |
| default: |
| return "UNKNOWN"; |
| } |
| } |
| |
| static const char *leapraid_sep_action_name(u8 action) |
| { |
| switch (action) { |
| case LEAPRAID_SEP_REQ_ACT_WRITE_STATUS: |
| return "WRITE_STATUS"; |
| default: |
| return "UNKNOWN"; |
| } |
| } |
| |
| static const char *leapraid_sas_op_name(u8 op) |
| { |
| switch (op) { |
| case LEAPRAID_SAS_OP_PHY_LINK_RESET: |
| return "PHY_LINK_RESET"; |
| case LEAPRAID_SAS_OP_PHY_HARD_RESET: |
| return "PHY_HARD_RESET"; |
| case LEAPRAID_SAS_OP_SET_PARAMETER: |
| return "SET_PARAMETER"; |
| default: |
| return "UNKNOWN"; |
| } |
| } |
| |
| static const char *leapraid_tm_type_name(u8 task_type) |
| { |
| switch (task_type) { |
| case LEAPRAID_TM_TASKTYPE_ABORT_TASK: |
| return "ABORT_TASK"; |
| case LEAPRAID_TM_TASKTYPE_ABRT_TASK_SET: |
| return "ABORT_TASK_SET"; |
| case LEAPRAID_TM_TASKTYPE_TARGET_RESET: |
| return "TARGET_RESET"; |
| case LEAPRAID_TM_TASKTYPE_LOGICAL_UNIT_RESET: |
| return "LOGICAL_UNIT_RESET"; |
| case LEAPRAID_TM_TASKTYPE_CLEAR_TASK_SET: |
| return "CLEAR_TASK_SET"; |
| case LEAPRAID_TM_TASKTYPE_QUERY_TASK: |
| return "QUERY_TASK"; |
| case LEAPRAID_TM_TASKTYPE_CLEAR_ACA: |
| return "CLEAR_ACA"; |
| case LEAPRAID_TM_TASKTYPE_QUERY_TASK_SET: |
| return "QUERY_TASK_SET"; |
| case LEAPRAID_TM_TASKTYPE_QUERY_ASYNC_EVENT: |
| return "QUERY_ASYNC_EVENT"; |
| default: |
| return "UNKNOWN"; |
| } |
| } |
| |
| void leapraid_log_req_context(struct leapraid_adapter *adapter, u16 smid, |
| const void *req_data) |
| { |
| const struct leapraid_req *req = req_data; |
| |
| if (!adapter || !adapter->pdev || !req_data) |
| return; |
| |
| switch (req->func) { |
| case LEAPRAID_FUNC_CONFIG_OP: { |
| const struct leapraid_cfg_req *cfg_req = req_data; |
| |
| dev_err(&adapter->pdev->dev, |
| "cfg-req: smid=%u func=0x%02x(%s) action=0x%02x(%s)\n", |
| smid, req->func, leapraid_func_name(req->func), |
| cfg_req->action, |
| leapraid_cfg_action_name(cfg_req->action)); |
| dev_err(&adapter->pdev->dev, |
| "cfg-req: page_type=0x%02x(%s) page_num=%u\n", |
| cfg_req->header.page_type, |
| leapraid_cfg_page_type_name(cfg_req->header.page_type), |
| cfg_req->header.page_num); |
| if (cfg_req->header.page_type == LEAPRAID_CFG_PT_EXTENDED) |
| dev_err(&adapter->pdev->dev, |
| "cfg-req: ext_page_type=0x%02x(%s)\n", |
| cfg_req->ext_page_type, |
| leapraid_cfg_ext_page_type_name( |
| cfg_req->ext_page_type)); |
| dev_err(&adapter->pdev->dev, "cfg-req: page_addr=0x%08x\n", |
| le32_to_cpu(cfg_req->page_addr)); |
| break; |
| } |
| case LEAPRAID_FUNC_SCSI_TMF: { |
| const struct leapraid_scsi_tm_req *tm_req = req_data; |
| |
| dev_err(&adapter->pdev->dev, |
| "scsi_tm:: smid=%u func=0x%02x(%s) task=0x%02x(%s)\n", |
| smid, req->func, leapraid_func_name(req->func), |
| tm_req->task_type, |
| leapraid_tm_type_name(tm_req->task_type)); |
| dev_err(&adapter->pdev->dev, |
| "scsi_tm:: dev_hdl=0x%04x task_mid=%u\n", |
| le16_to_cpu(tm_req->dev_hdl), |
| le16_to_cpu(tm_req->task_mid)); |
| break; |
| } |
| case LEAPRAID_FUNC_SCSI_ENC_PROCESSOR: { |
| const struct leapraid_sep_req *sep_req = req_data; |
| |
| dev_err(&adapter->pdev->dev, |
| "sep: smid=%u func=0x%02x(%s) action=0x%02x(%s)\n", |
| smid, req->func, leapraid_func_name(req->func), |
| sep_req->act, |
| leapraid_sep_action_name(sep_req->act)); |
| dev_err(&adapter->pdev->dev, |
| "sep: dev_hdl=0x%04x slot=%u enc_hdl=0x%04x\n", |
| le16_to_cpu(sep_req->dev_hdl), |
| le16_to_cpu(sep_req->slot), |
| le16_to_cpu(sep_req->enc_hdl)); |
| break; |
| } |
| case LEAPRAID_FUNC_SAS_IO_UNIT_CTRL: { |
| const struct leapraid_io_unit_ctrl_req *io_req = req_data; |
| |
| dev_err(&adapter->pdev->dev, |
| "ctl_cmd: smid=%u func=0x%02x(%s) action=0x%02x(%s)\n", |
| smid, req->func, leapraid_func_name(req->func), |
| io_req->op, leapraid_sas_op_name(io_req->op)); |
| dev_err(&adapter->pdev->dev, |
| "ctl_cmd: dev_hdl=0x%04x param=0x%02x\n", |
| le16_to_cpu(io_req->dev_hdl), |
| io_req->adapter_para); |
| break; |
| } |
| case LEAPRAID_FUNC_SMP_PASSTHROUGH: { |
| const struct leapraid_smp_passthrough_req *smp_req = req_data; |
| |
| dev_err(&adapter->pdev->dev, |
| "smp_cmd: smid=%u func=0x%02x(%s) action=0x%02x\n", |
| smid, req->func, leapraid_func_name(req->func), |
| smp_req->passthrough_flg); |
| dev_err(&adapter->pdev->dev, |
| "smp_cmd: port=%u req_len=%u\n", |
| smp_req->physical_port, |
| le16_to_cpu(smp_req->req_data_len)); |
| dev_err(&adapter->pdev->dev, "smp_cmd: sas_addr=0x%016llx\n", |
| (unsigned long long)le64_to_cpu(smp_req->sas_address)); |
| break; |
| } |
| default: |
| dev_err(&adapter->pdev->dev, |
| "cmd: smid=%u func=0x%02x(%s)\n", |
| smid, req->func, leapraid_func_name(req->func)); |
| break; |
| } |
| } |
| |
| static void leapraid_build_and_fire_cfg_req( |
| struct leapraid_adapter *adapter, |
| struct leapraid_cfg_req *leap_mpi_cfgp_req, |
| struct leapraid_cfg_rep *leap_mpi_cfgp_rep) |
| { |
| struct leapraid_cfg_req *local_leap_cfg_req; |
| u16 smid; |
| |
| memset(leap_mpi_cfgp_rep, 0, sizeof(struct leapraid_cfg_rep)); |
| memset(&adapter->driver_cmds.cfg_op_cmd.reply, 0, |
| sizeof(struct leapraid_cfg_rep)); |
| adapter->driver_cmds.cfg_op_cmd.status = LEAPRAID_CMD_PENDING; |
| smid = adapter->driver_cmds.cfg_op_cmd.inter_taskid; |
| local_leap_cfg_req = leapraid_get_task_desc(adapter, smid); |
| memcpy(local_leap_cfg_req, leap_mpi_cfgp_req, |
| sizeof(struct leapraid_cfg_req)); |
| init_completion(&adapter->driver_cmds.cfg_op_cmd.done); |
| leapraid_fire_task(adapter, smid); |
| wait_for_completion_timeout(&adapter->driver_cmds.cfg_op_cmd.done, |
| LEAPRAID_CFG_OP_TIMEOUT * HZ); |
| } |
| |
| static int leapraid_req_cfg_func(struct leapraid_adapter *adapter, |
| struct leapraid_cfg_req *leap_mpi_cfgp_req, |
| struct leapraid_cfg_rep *leap_mpi_cfgp_rep, |
| void *target_cfg_pg, void *real_cfg_pg_addr, |
| u16 target_real_cfg_pg_sz) |
| { |
| u32 adapter_status = UINT_MAX; |
| bool issue_reset = false; |
| u16 smid; |
| u8 retry_cnt; |
| int rc; |
| |
| retry_cnt = 0; |
| mutex_lock(&adapter->driver_cmds.cfg_op_cmd.mutex); |
| smid = adapter->driver_cmds.cfg_op_cmd.inter_taskid; |
| retry: |
| if (retry_cnt) { |
| if (retry_cnt > LEAPRAID_CFG_REQ_RETRY_TIMES) { |
| rc = -EFAULT; |
| goto out_cleanup; |
| } |
| dev_warn(&adapter->pdev->dev, |
| "cfg-req: Retry request, cnt=%u\n", retry_cnt); |
| } |
| |
| rc = leapraid_check_adapter_is_op(adapter, LEAPRAID_DB_WAIT_OP_SHORT, |
| __func__); |
| if (rc) { |
| dev_err(&adapter->pdev->dev, |
| "cfg-req: Adapter not operational\n"); |
| goto out_cleanup; |
| } |
| |
| leapraid_build_and_fire_cfg_req(adapter, leap_mpi_cfgp_req, |
| leap_mpi_cfgp_rep); |
| if (!(adapter->driver_cmds.cfg_op_cmd.status & LEAPRAID_CMD_DONE)) { |
| retry_cnt++; |
| if (adapter->driver_cmds.cfg_op_cmd.status & |
| LEAPRAID_CMD_RESET) { |
| dev_warn(&adapter->pdev->dev, |
| "cfg-req: CMD fail due to hard reset\n"); |
| goto retry; |
| } |
| |
| if (adapter->access_ctrl.shost_recovering || |
| adapter->access_ctrl.pcie_recovering) { |
| dev_err(&adapter->pdev->dev, |
| "cfg-req: pending in %s, status=0x%x\n", |
| adapter->access_ctrl.shost_recovering ? |
| "shost recovery" : "pcie recovery", |
| adapter->driver_cmds.cfg_op_cmd.status); |
| leapraid_log_req_context(adapter, smid, |
| leap_mpi_cfgp_req); |
| issue_reset = false; |
| rc = -EFAULT; |
| } else { |
| dev_err(&adapter->pdev->dev, |
| "cfg-req: timeout, status=0x%x, reset\n", |
| adapter->driver_cmds.cfg_op_cmd.status); |
| leapraid_log_req_context(adapter, smid, |
| leap_mpi_cfgp_req); |
| issue_reset = true; |
| } |
| |
| goto out_cleanup; |
| } |
| |
| if (adapter->driver_cmds.cfg_op_cmd.status & |
| LEAPRAID_CMD_REPLY_VALID) { |
| memcpy(leap_mpi_cfgp_rep, |
| &adapter->driver_cmds.cfg_op_cmd.reply, |
| sizeof(struct leapraid_cfg_rep)); |
| adapter_status = le16_to_cpu( |
| leap_mpi_cfgp_rep->adapter_status) & |
| LEAPRAID_ADAPTER_STATUS_MASK; |
| if (adapter_status == LEAPRAID_ADAPTER_STATUS_SUCCESS && |
| target_cfg_pg && real_cfg_pg_addr && |
| target_real_cfg_pg_sz && |
| leap_mpi_cfgp_req->action == |
| LEAPRAID_CFG_ACT_PAGE_READ_CUR) |
| memcpy(target_cfg_pg, real_cfg_pg_addr, |
| target_real_cfg_pg_sz); |
| if (adapter_status != LEAPRAID_ADAPTER_STATUS_SUCCESS) { |
| if (adapter_status != |
| LEAPRAID_ADAPTER_STATUS_CONFIG_INVALID_PAGE) |
| dev_err(&adapter->pdev->dev, |
| "cfg-rep: adapter_status=0x%x\n", |
| adapter_status); |
| rc = -EFAULT; |
| } |
| } else { |
| dev_err(&adapter->pdev->dev, "cfg-rep: Reply invalid\n"); |
| rc = -EFAULT; |
| } |
| |
| out_cleanup: |
| adapter->driver_cmds.cfg_op_cmd.status = LEAPRAID_CMD_NOT_USED; |
| mutex_unlock(&adapter->driver_cmds.cfg_op_cmd.mutex); |
| if (issue_reset) { |
| if (adapter->scan_dev_desc.first_scan_dev_fired) { |
| dev_warn(&adapter->pdev->dev, |
| "%s:%d cfg-req: Failure, issuing reset\n", |
| __func__, __LINE__); |
| leapraid_hard_reset_handler(adapter, FULL_RESET); |
| } else { |
| dev_warn(&adapter->pdev->dev, |
| "cfg-req: CMD fail in init, skip reset\n"); |
| } |
| rc = -EFAULT; |
| } |
| return rc; |
| } |
| |
| static int leapraid_request_cfg_pg_header( |
| struct leapraid_adapter *adapter, |
| struct leapraid_cfg_req *leap_mpi_cfgp_req, |
| struct leapraid_cfg_rep *leap_mpi_cfgp_rep) |
| { |
| return leapraid_req_cfg_func(adapter, leap_mpi_cfgp_req, |
| leap_mpi_cfgp_rep, NULL, NULL, 0); |
| } |
| |
| static int leapraid_request_cfg_pg(struct leapraid_adapter *adapter, |
| struct leapraid_cfg_req *leap_mpi_cfgp_req, |
| struct leapraid_cfg_rep *leap_mpi_cfgp_rep, |
| void *target_cfg_pg, void *real_cfg_pg_addr, |
| u16 target_real_cfg_pg_sz) |
| { |
| return leapraid_req_cfg_func(adapter, leap_mpi_cfgp_req, |
| leap_mpi_cfgp_rep, target_cfg_pg, |
| real_cfg_pg_addr, target_real_cfg_pg_sz); |
| } |
| |
| int leapraid_op_config_page(struct leapraid_adapter *adapter, |
| void *target_cfg_pg, union cfg_param_1 cfgp1, |
| union cfg_param_2 cfgp2, |
| enum config_page_action cfg_op) |
| { |
| struct leapraid_cfg_req leap_mpi_cfgp_req; |
| struct leapraid_cfg_rep leap_mpi_cfgp_rep; |
| u16 real_cfg_pg_sz; |
| void *real_cfg_pg_addr; |
| dma_addr_t real_cfg_pg_dma = 0; |
| u32 __page_size; |
| int rc; |
| |
| memset(&leap_mpi_cfgp_req, 0, sizeof(struct leapraid_cfg_req)); |
| leap_mpi_cfgp_req.func = LEAPRAID_FUNC_CONFIG_OP; |
| leap_mpi_cfgp_req.action = LEAPRAID_CFG_ACT_PAGE_HEADER; |
| |
| switch (cfg_op) { |
| case GET_BIOS_PG3: |
| leap_mpi_cfgp_req.header.page_type = LEAPRAID_CFG_PT_BIOS; |
| leap_mpi_cfgp_req.header.page_num = |
| LEAPRAID_CFG_PAGE_NUM_BIOS3; |
| __page_size = sizeof(struct leapraid_bios_page3); |
| break; |
| case GET_BIOS_PG2: |
| leap_mpi_cfgp_req.header.page_type = LEAPRAID_CFG_PT_BIOS; |
| leap_mpi_cfgp_req.header.page_num = |
| LEAPRAID_CFG_PAGE_NUM_BIOS2; |
| __page_size = sizeof(struct leapraid_bios_page2); |
| break; |
| case GET_MANUFACTURING_PG0: |
| leap_mpi_cfgp_req.header.page_type = |
| LEAPRAID_CFG_PT_MANUFACTURING; |
| leap_mpi_cfgp_req.header.page_num = |
| LEAPRAID_CFG_PAGE_NUM_MANU0; |
| __page_size = sizeof(struct leapraid_manufacturing_p0); |
| break; |
| case GET_SAS_DEVICE_PG0: |
| leap_mpi_cfgp_req.header.page_type = LEAPRAID_CFG_PT_EXTENDED; |
| leap_mpi_cfgp_req.ext_page_type = LEAPRAID_CFG_EXTPT_SAS_DEV; |
| leap_mpi_cfgp_req.header.page_num = LEAPRAID_CFG_PAGE_NUM_DEV0; |
| __page_size = sizeof(struct leapraid_sas_dev_p0); |
| break; |
| case GET_SAS_IOUNIT_PG0: |
| leap_mpi_cfgp_req.header.page_type = LEAPRAID_CFG_PT_EXTENDED; |
| leap_mpi_cfgp_req.ext_page_type = |
| LEAPRAID_CFG_EXTPT_SAS_IO_UNIT; |
| leap_mpi_cfgp_req.header.page_num = |
| LEAPRAID_CFG_PAGE_NUM_IOUNIT0; |
| __page_size = cfgp1.size; |
| break; |
| case GET_SAS_IOUNIT_PG1: |
| leap_mpi_cfgp_req.header.page_type = LEAPRAID_CFG_PT_EXTENDED; |
| leap_mpi_cfgp_req.ext_page_type = |
| LEAPRAID_CFG_EXTPT_SAS_IO_UNIT; |
| leap_mpi_cfgp_req.header.page_num = |
| LEAPRAID_CFG_PAGE_NUM_IOUNIT1; |
| __page_size = cfgp1.size; |
| break; |
| case GET_SAS_EXPANDER_PG0: |
| leap_mpi_cfgp_req.header.page_type = LEAPRAID_CFG_PT_EXTENDED; |
| leap_mpi_cfgp_req.ext_page_type = LEAPRAID_CFG_EXTPT_SAS_EXP; |
| leap_mpi_cfgp_req.header.page_num = LEAPRAID_CFG_PAGE_NUM_EXP0; |
| __page_size = sizeof(struct leapraid_exp_p0); |
| break; |
| case GET_SAS_EXPANDER_PG1: |
| leap_mpi_cfgp_req.header.page_type = LEAPRAID_CFG_PT_EXTENDED; |
| leap_mpi_cfgp_req.ext_page_type = LEAPRAID_CFG_EXTPT_SAS_EXP; |
| leap_mpi_cfgp_req.header.page_num = LEAPRAID_CFG_PAGE_NUM_EXP1; |
| __page_size = sizeof(struct leapraid_exp_p1); |
| break; |
| case GET_SAS_ENCLOSURE_PG0: |
| leap_mpi_cfgp_req.header.page_type = LEAPRAID_CFG_PT_EXTENDED; |
| leap_mpi_cfgp_req.ext_page_type = LEAPRAID_CFG_EXTPT_ENC; |
| leap_mpi_cfgp_req.header.page_num = LEAPRAID_CFG_PAGE_NUM_ENC0; |
| __page_size = sizeof(struct leapraid_enc_p0); |
| break; |
| case GET_PHY_PG0: |
| leap_mpi_cfgp_req.header.page_type = LEAPRAID_CFG_PT_EXTENDED; |
| leap_mpi_cfgp_req.ext_page_type = LEAPRAID_CFG_EXTPT_SAS_PHY; |
| leap_mpi_cfgp_req.header.page_num = LEAPRAID_CFG_PAGE_NUM_PHY0; |
| __page_size = sizeof(struct leapraid_sas_phy_p0); |
| break; |
| case GET_RAID_VOLUME_PG0: |
| leap_mpi_cfgp_req.header.page_type = |
| LEAPRAID_CFG_PT_RAID_VOLUME; |
| leap_mpi_cfgp_req.header.page_num = LEAPRAID_CFG_PAGE_NUM_VOL0; |
| __page_size = cfgp1.size; |
| break; |
| case GET_RAID_VOLUME_PG1: |
| leap_mpi_cfgp_req.header.page_type = |
| LEAPRAID_CFG_PT_RAID_VOLUME; |
| leap_mpi_cfgp_req.header.page_num = LEAPRAID_CFG_PAGE_NUM_VOL1; |
| __page_size = sizeof(struct leapraid_raidvol_p1); |
| break; |
| case GET_PHY_DISK_PG0: |
| leap_mpi_cfgp_req.header.page_type = |
| LEAPRAID_CFG_PT_RAID_PHYSDISK; |
| leap_mpi_cfgp_req.header.page_num = LEAPRAID_CFG_PAGE_NUM_PD0; |
| __page_size = sizeof(struct leapraid_raidpd_p0); |
| break; |
| default: |
| dev_err(&adapter->pdev->dev, |
| "Unsupported config page action=%d!\n", cfg_op); |
| return -EINVAL; |
| } |
| |
| leapraid_build_nodata_mpi_sg(adapter, |
| &leap_mpi_cfgp_req.page_buf_sge); |
| rc = leapraid_request_cfg_pg_header(adapter, |
| &leap_mpi_cfgp_req, |
| &leap_mpi_cfgp_rep); |
| if (rc) { |
| dev_err(&adapter->pdev->dev, |
| "cfg-req: Header failed rc=%dn", rc); |
| return rc; |
| } |
| |
| if (cfg_op == GET_SAS_DEVICE_PG0 || |
| cfg_op == GET_SAS_EXPANDER_PG0 || |
| cfg_op == GET_SAS_ENCLOSURE_PG0 || |
| cfg_op == GET_RAID_VOLUME_PG1) |
| leap_mpi_cfgp_req.page_addr = cpu_to_le32(cfgp1.form | |
| cfgp2.handle); |
| else if (cfg_op == GET_PHY_DISK_PG0) |
| leap_mpi_cfgp_req.page_addr = cpu_to_le32(cfgp1.form | |
| cfgp2.form_specific); |
| else if (cfg_op == GET_RAID_VOLUME_PG0) |
| leap_mpi_cfgp_req.page_addr = |
| cpu_to_le32(cfgp2.handle | |
| LEAPRAID_RAID_VOL_CFG_PGAD_HDL); |
| else if (cfg_op == GET_SAS_EXPANDER_PG1) |
| leap_mpi_cfgp_req.page_addr = |
| cpu_to_le32(cfgp2.handle | |
| (cfgp1.phy_number << |
| LEAPRAID_SAS_EXP_CFG_PGAD_PHYNUM_SHIFT) | |
| LEAPRAID_SAS_EXP_CFG_PGAD_HDL_PHY_NUM); |
| else if (cfg_op == GET_PHY_PG0) |
| leap_mpi_cfgp_req.page_addr = cpu_to_le32(cfgp1.phy_number | |
| LEAPRAID_SAS_PHY_CFG_PGAD_PHY_NUMBER); |
| |
| leap_mpi_cfgp_req.action = LEAPRAID_CFG_ACT_PAGE_READ_CUR; |
| |
| leap_mpi_cfgp_req.header.page_num = leap_mpi_cfgp_rep.header.page_num; |
| leap_mpi_cfgp_req.header.page_type = |
| leap_mpi_cfgp_rep.header.page_type; |
| leap_mpi_cfgp_req.header.page_len = leap_mpi_cfgp_rep.header.page_len; |
| leap_mpi_cfgp_req.ext_page_len = leap_mpi_cfgp_rep.ext_page_len; |
| leap_mpi_cfgp_req.ext_page_type = leap_mpi_cfgp_rep.ext_page_type; |
| |
| real_cfg_pg_sz = (leap_mpi_cfgp_req.header.page_len) ? |
| leap_mpi_cfgp_req.header.page_len * sizeof(u32) : |
| le16_to_cpu(leap_mpi_cfgp_rep.ext_page_len) * sizeof(u32); |
| real_cfg_pg_addr = dma_alloc_coherent(&adapter->pdev->dev, |
| real_cfg_pg_sz, |
| &real_cfg_pg_dma, |
| GFP_KERNEL); |
| if (!real_cfg_pg_addr) |
| return -ENOMEM; |
| |
| if (leap_mpi_cfgp_req.action == LEAPRAID_CFG_ACT_PAGE_WRITE_CUR) { |
| leapraid_single_mpi_sg_append(adapter, |
| &leap_mpi_cfgp_req.page_buf_sge, |
| ((LEAPRAID_SGE_FLG_SIMPLE_ONE | |
| LEAPRAID_SGE_FLG_LAST_ONE | |
| LEAPRAID_SGE_FLG_EOB | |
| LEAPRAID_SGE_FLG_EOL | |
| LEAPRAID_SGE_FLG_H2C) << |
| LEAPRAID_SGE_FLG_SHIFT) | |
| real_cfg_pg_sz, |
| real_cfg_pg_dma); |
| memcpy(real_cfg_pg_addr, target_cfg_pg, |
| min_t(u16, real_cfg_pg_sz, __page_size)); |
| } else { |
| memset(target_cfg_pg, 0, __page_size); |
| leapraid_single_mpi_sg_append(adapter, |
| &leap_mpi_cfgp_req.page_buf_sge, |
| ((LEAPRAID_SGE_FLG_SIMPLE_ONE | |
| LEAPRAID_SGE_FLG_LAST_ONE | |
| LEAPRAID_SGE_FLG_EOB | |
| LEAPRAID_SGE_FLG_EOL) << |
| LEAPRAID_SGE_FLG_SHIFT) | |
| real_cfg_pg_sz, |
| real_cfg_pg_dma); |
| memset(real_cfg_pg_addr, 0, |
| min_t(u16, real_cfg_pg_sz, __page_size)); |
| } |
| |
| rc = leapraid_request_cfg_pg(adapter, |
| &leap_mpi_cfgp_req, |
| &leap_mpi_cfgp_rep, |
| target_cfg_pg, |
| real_cfg_pg_addr, |
| min_t(u16, real_cfg_pg_sz, __page_size)); |
| if (rc) { |
| u32 status; |
| |
| status = le16_to_cpu(leap_mpi_cfgp_rep.adapter_status) & |
| LEAPRAID_ADAPTER_STATUS_MASK; |
| if (status != LEAPRAID_ADAPTER_STATUS_CONFIG_INVALID_PAGE) |
| dev_err(&adapter->pdev->dev, |
| "cfg-req: rc=%d, pg_info: 0x%x, 0x%x, %d\n", |
| rc, leap_mpi_cfgp_req.header.page_type, |
| leap_mpi_cfgp_req.ext_page_type, |
| leap_mpi_cfgp_req.header.page_num); |
| } |
| |
| if (real_cfg_pg_addr) |
| dma_free_coherent(&adapter->pdev->dev, |
| real_cfg_pg_sz, |
| real_cfg_pg_addr, |
| real_cfg_pg_dma); |
| |
| return rc; |
| } |
| |
| static int leapraid_cfg_find_vol_in_page( |
| struct leapraid_raid_cfg_p0 *raid_cfg_p0, |
| u16 pd_hdl, u16 *vol_hdl) |
| { |
| u16 elements = raid_cfg_p0->elements_num; |
| int i; |
| |
| for (i = 0; i < elements; i++) { |
| struct leapraid_raid_cfg_p0_element *elem; |
| u16 type; |
| |
| elem = &raid_cfg_p0->cfg_element[i]; |
| |
| type = le16_to_cpu(elem->element_flg) & |
| LEAPRAID_RAIDCFG_P0_EFLG_MASK_ELEMENT_TYPE; |
| |
| switch (type) { |
| case LEAPRAID_RAIDCFG_P0_EFLG_VOL_PHYS_DISK_ELEMENT: |
| case LEAPRAID_RAIDCFG_P0_EFLG_OCE_ELEMENT: { |
| u16 phys_hdl; |
| |
| phys_hdl = le16_to_cpu(elem->phys_disk_dev_hdl); |
| if (phys_hdl == pd_hdl) { |
| *vol_hdl = le16_to_cpu(elem->vol_dev_hdl); |
| return 0; |
| } |
| break; |
| } |
| case LEAPRAID_RAIDCFG_P0_EFLG_HOT_SPARE_ELEMENT: |
| *vol_hdl = 0; |
| return 0; |
| |
| default: |
| break; |
| } |
| } |
| |
| return -ENOENT; |
| } |
| |
| static int leapraid_cfg_get_volume_hdl_dispatch( |
| struct leapraid_adapter *adapter, |
| struct leapraid_cfg_req *cfg_req, |
| struct leapraid_cfg_rep *cfg_rep, |
| struct leapraid_raid_cfg_p0 *raid_cfg_p0, |
| void *real_cfg_pg_addr, |
| u16 real_cfg_pg_sz, |
| u16 raid_cfg_p0_sz, |
| u16 pd_hdl, u16 *vol_hdl) |
| { |
| u16 adapter_status; |
| int config_num; |
| int rc; |
| |
| config_num = 0xFF; |
| while (true) { |
| cfg_req->page_addr = |
| cpu_to_le32(config_num + |
| LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP); |
| rc = leapraid_request_cfg_pg( |
| adapter, cfg_req, cfg_rep, |
| raid_cfg_p0, real_cfg_pg_addr, |
| min_t(u16, real_cfg_pg_sz, raid_cfg_p0_sz)); |
| adapter_status = le16_to_cpu(cfg_rep->adapter_status) & |
| LEAPRAID_ADAPTER_STATUS_MASK; |
| if (rc) { |
| if (adapter_status == |
| LEAPRAID_ADAPTER_STATUS_CONFIG_INVALID_PAGE) { |
| *vol_hdl = 0; |
| return 0; |
| } |
| return rc; |
| } |
| |
| if (adapter_status != LEAPRAID_ADAPTER_STATUS_SUCCESS) |
| return LEAPRAID_OPERATION_FAILED; |
| |
| rc = leapraid_cfg_find_vol_in_page(raid_cfg_p0, |
| pd_hdl, |
| vol_hdl); |
| if (rc != -ENOENT) |
| return rc; |
| |
| config_num = raid_cfg_p0->cfg_num; |
| } |
| return 0; |
| } |
| |
| int leapraid_cfg_get_volume_hdl(struct leapraid_adapter *adapter, |
| u16 pd_hdl, u16 *vol_hdl) |
| { |
| struct leapraid_raid_cfg_p0 *raid_cfg_p0; |
| struct leapraid_cfg_req cfg_req; |
| struct leapraid_cfg_rep cfg_rep; |
| dma_addr_t real_cfg_pg_dma = 0; |
| void *real_cfg_pg_addr; |
| u16 real_cfg_pg_sz; |
| int rc, raid_cfg_p0_sz; |
| |
| *vol_hdl = 0; |
| memset(&cfg_req, 0, sizeof(struct leapraid_cfg_req)); |
| cfg_req.func = LEAPRAID_FUNC_CONFIG_OP; |
| cfg_req.action = LEAPRAID_CFG_ACT_PAGE_HEADER; |
| cfg_req.header.page_type = LEAPRAID_CFG_PT_EXTENDED; |
| cfg_req.ext_page_type = LEAPRAID_CFG_EXTPT_RAID_CONFIG; |
| cfg_req.header.page_num = LEAPRAID_CFG_PAGE_NUM_VOL0; |
| |
| leapraid_build_nodata_mpi_sg(adapter, &cfg_req.page_buf_sge); |
| rc = leapraid_request_cfg_pg_header(adapter, &cfg_req, &cfg_rep); |
| if (rc) |
| return rc; |
| |
| cfg_req.action = LEAPRAID_CFG_ACT_PAGE_READ_CUR; |
| raid_cfg_p0_sz = le16_to_cpu(cfg_rep.ext_page_len) * |
| LEAPRAID_CFG_UNIT_SIZE; |
| raid_cfg_p0 = kmalloc(raid_cfg_p0_sz, GFP_KERNEL); |
| if (!raid_cfg_p0) |
| return -ENOMEM; |
| |
| real_cfg_pg_sz = (cfg_req.header.page_len) ? |
| cfg_req.header.page_len * LEAPRAID_CFG_UNIT_SIZE : |
| le16_to_cpu(cfg_rep.ext_page_len) * LEAPRAID_CFG_UNIT_SIZE; |
| |
| real_cfg_pg_addr = dma_alloc_coherent(&adapter->pdev->dev, |
| real_cfg_pg_sz, &real_cfg_pg_dma, |
| GFP_KERNEL); |
| if (!real_cfg_pg_addr) { |
| rc = -ENOMEM; |
| goto out_free; |
| } |
| |
| memset(raid_cfg_p0, 0, raid_cfg_p0_sz); |
| leapraid_single_mpi_sg_append(adapter, |
| &cfg_req.page_buf_sge, |
| ((LEAPRAID_SGE_FLG_SIMPLE_ONE | |
| LEAPRAID_SGE_FLG_LAST_ONE | |
| LEAPRAID_SGE_FLG_EOB | |
| LEAPRAID_SGE_FLG_EOL) << |
| LEAPRAID_SGE_FLG_SHIFT) | |
| real_cfg_pg_sz, |
| real_cfg_pg_dma); |
| memset(real_cfg_pg_addr, 0, |
| min_t(u16, real_cfg_pg_sz, raid_cfg_p0_sz)); |
| |
| rc = leapraid_cfg_get_volume_hdl_dispatch(adapter, |
| &cfg_req, &cfg_rep, |
| raid_cfg_p0, |
| real_cfg_pg_addr, |
| real_cfg_pg_sz, |
| raid_cfg_p0_sz, |
| pd_hdl, vol_hdl); |
| |
| out_free: |
| if (real_cfg_pg_addr) |
| dma_free_coherent(&adapter->pdev->dev, |
| real_cfg_pg_sz, real_cfg_pg_addr, |
| real_cfg_pg_dma); |
| kfree(raid_cfg_p0); |
| return rc; |
| } |
| |
| static int leapraid_get_adapter_phys(struct leapraid_adapter *adapter, |
| u8 *nr_phys) |
| { |
| struct leapraid_sas_io_unit_p0 sas_io_unit_page0; |
| union cfg_param_1 cfgp1 = {0}; |
| union cfg_param_2 cfgp2 = {0}; |
| int rc; |
| |
| *nr_phys = 0; |
| cfgp1.size = sizeof(struct leapraid_sas_io_unit_p0); |
| rc = leapraid_op_config_page(adapter, &sas_io_unit_page0, cfgp1, |
| cfgp2, GET_SAS_IOUNIT_PG0); |
| if (rc) |
| return rc; |
| |
| *nr_phys = sas_io_unit_page0.phy_num; |
| |
| return 0; |
| } |
| |
| static int leapraid_cfg_get_number_pds(struct leapraid_adapter *adapter, |
| u16 hdl, u8 *num_pds) |
| { |
| union cfg_param_1 cfgp1 = {0}; |
| union cfg_param_2 cfgp2 = {0}; |
| struct leapraid_raidvol_p0 raidvol_p0; |
| int rc; |
| |
| *num_pds = 0; |
| cfgp1.size = sizeof(struct leapraid_raidvol_p0); |
| cfgp2.handle = hdl; |
| rc = leapraid_op_config_page(adapter, &raidvol_p0, cfgp1, |
| cfgp2, GET_RAID_VOLUME_PG0); |
| if (!rc) |
| *num_pds = raidvol_p0.num_phys_disks; |
| |
| return rc; |
| } |
| |
| int leapraid_cfg_get_volume_wwid(struct leapraid_adapter *adapter, |
| u16 vol_hdl, u64 *wwid) |
| { |
| union cfg_param_1 cfgp1 = {0}; |
| union cfg_param_2 cfgp2 = {0}; |
| struct leapraid_raidvol_p1 raidvol_p1; |
| int rc; |
| |
| *wwid = 0; |
| cfgp1.form = LEAPRAID_RAID_VOL_CFG_PGAD_HDL; |
| cfgp2.handle = vol_hdl; |
| rc = leapraid_op_config_page(adapter, &raidvol_p1, cfgp1, |
| cfgp2, GET_RAID_VOLUME_PG1); |
| if (!rc) |
| *wwid = le64_to_cpu(raidvol_p1.wwid); |
| |
| return rc; |
| } |
| |
| static int leapraid_get_sas_io_unit_page0( |
| struct leapraid_adapter *adapter, |
| struct leapraid_sas_io_unit_p0 *sas_io_unit_p0, |
| u16 sas_iou_pg0_sz) |
| { |
| union cfg_param_1 cfgp1 = {0}; |
| union cfg_param_2 cfgp2 = {0}; |
| |
| cfgp1.size = sas_iou_pg0_sz; |
| return leapraid_op_config_page(adapter, sas_io_unit_p0, cfgp1, |
| cfgp2, GET_SAS_IOUNIT_PG0); |
| } |
| |
| static int leapraid_get_sas_address(struct leapraid_adapter *adapter, |
| u16 hdl, u64 *sas_address) |
| { |
| union cfg_param_1 cfgp1 = {0}; |
| union cfg_param_2 cfgp2 = {0}; |
| struct leapraid_sas_dev_p0 sas_dev_p0; |
| |
| *sas_address = 0; |
| cfgp1.form = LEAPRAID_SAS_DEV_CFG_PGAD_HDL; |
| cfgp2.handle = hdl; |
| if (leapraid_op_config_page(adapter, &sas_dev_p0, cfgp1, |
| cfgp2, GET_SAS_DEVICE_PG0)) |
| return -ENXIO; |
| |
| if (hdl <= adapter->dev_topo.card.phys_num && |
| (!(le32_to_cpu(sas_dev_p0.dev_info) & LEAPRAID_DEVTYP_SEP))) |
| *sas_address = adapter->dev_topo.card.sas_address; |
| else |
| *sas_address = le64_to_cpu(sas_dev_p0.sas_address); |
| |
| return 0; |
| } |
| |
| int leapraid_get_volume_cap(struct leapraid_adapter *adapter, |
| struct leapraid_raid_volume *raid_volume) |
| { |
| union cfg_param_1 cfgp1 = {0}; |
| union cfg_param_2 cfgp2 = {0}; |
| struct leapraid_raidvol_p0 *raidvol_p0; |
| struct leapraid_sas_dev_p0 sas_dev_p0; |
| struct leapraid_raidpd_p0 raidpd_p0; |
| u8 num_pds; |
| u16 sz; |
| int rc = 0; |
| |
| if (leapraid_cfg_get_number_pds(adapter, raid_volume->hdl, |
| &num_pds) || !num_pds) |
| return -EFAULT; |
| |
| raid_volume->pd_num = num_pds; |
| sz = offsetof(struct leapraid_raidvol_p0, phys_disk) + |
| (num_pds * sizeof(struct leapraid_raidvol0_phys_disk)); |
| raidvol_p0 = kzalloc(sz, GFP_KERNEL); |
| if (!raidvol_p0) |
| return -ENOMEM; |
| |
| cfgp1.size = sz; |
| cfgp2.handle = raid_volume->hdl; |
| if (leapraid_op_config_page(adapter, raidvol_p0, cfgp1, cfgp2, |
| GET_RAID_VOLUME_PG0)) { |
| rc = -EFAULT; |
| goto out_cleanup; |
| } |
| |
| raid_volume->vol_type = raidvol_p0->volume_type; |
| cfgp1.form = LEAPRAID_PHYSDISK_CFG_PGAD_PHYSDISKNUM; |
| cfgp2.form_specific = raidvol_p0->phys_disk[0].phys_disk_num; |
| if (!(leapraid_op_config_page(adapter, &raidpd_p0, cfgp1, cfgp2, |
| GET_PHY_DISK_PG0))) { |
| cfgp1.form = LEAPRAID_SAS_DEV_CFG_PGAD_HDL; |
| cfgp2.handle = le16_to_cpu(raidpd_p0.dev_hdl); |
| if (!(leapraid_op_config_page(adapter, &sas_dev_p0, cfgp1, |
| cfgp2, GET_SAS_DEVICE_PG0))) |
| raid_volume->dev_info = |
| le32_to_cpu(sas_dev_p0.dev_info); |
| } |
| |
| out_cleanup: |
| kfree(raidvol_p0); |
| return rc; |
| } |
| |
| static bool leapraid_should_skip_poll_work(struct leapraid_adapter *adapter) |
| { |
| unsigned long flags; |
| bool skip; |
| |
| spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags); |
| skip = adapter->access_ctrl.shost_recovering || |
| adapter->access_ctrl.pcie_recovering || |
| adapter->access_ctrl.host_removing; |
| spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags); |
| |
| return skip; |
| } |
| |
| static void leapraid_fw_log_work(struct work_struct *work) |
| { |
| struct leapraid_adapter *adapter = container_of(work, |
| struct leapraid_adapter, fw_log_desc.fw_log_work.work); |
| struct leapraid_fw_log_info *infom; |
| struct leapraid_reg_base __iomem *iomem_base; |
| unsigned long flags; |
| |
| if (leapraid_should_skip_poll_work(adapter)) |
| goto scheduled_timer; |
| |
| infom = (struct leapraid_fw_log_info *) |
| (adapter->fw_log_desc.fw_log_buffer + |
| LEAPRAID_SYS_LOG_BUF_SIZE); |
| |
| iomem_base = adapter->iomem_base; |
| if (adapter->fw_log_desc.fw_log_init_flag == 0) { |
| infom->user_position = |
| leapraid_readl(&iomem_base->host_log_buf_pos); |
| infom->adapter_position = |
| leapraid_readl(&iomem_base->adapter_log_buf_pos); |
| adapter->fw_log_desc.fw_log_init_flag++; |
| } |
| |
| writel(infom->user_position, &iomem_base->host_log_buf_pos); |
| infom->adapter_position = |
| leapraid_readl(&iomem_base->adapter_log_buf_pos); |
| |
| scheduled_timer: |
| spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags); |
| if (adapter->fw_log_desc.fw_log_wq) |
| queue_delayed_work( |
| adapter->fw_log_desc.fw_log_wq, |
| &adapter->fw_log_desc.fw_log_work, |
| msecs_to_jiffies(LEAPRAID_PCIE_LOG_POLLING_INTERVAL)); |
| spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags); |
| } |
| |
| void leapraid_fw_log_stop(struct leapraid_adapter *adapter) |
| { |
| struct workqueue_struct *wq; |
| unsigned long flags; |
| |
| if (!adapter->fw_log_desc.open_pcie_trace) |
| return; |
| |
| spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags); |
| wq = adapter->fw_log_desc.fw_log_wq; |
| adapter->fw_log_desc.fw_log_wq = NULL; |
| spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags); |
| if (wq) { |
| if (!cancel_delayed_work_sync(&adapter->fw_log_desc |
| .fw_log_work)) |
| flush_workqueue(wq); |
| destroy_workqueue(wq); |
| } |
| } |
| |
| void leapraid_fw_log_start(struct leapraid_adapter *adapter) |
| { |
| unsigned long flags; |
| |
| if (!adapter->fw_log_desc.open_pcie_trace) |
| return; |
| |
| if (adapter->fw_log_desc.fw_log_wq) |
| return; |
| |
| INIT_DELAYED_WORK(&adapter->fw_log_desc.fw_log_work, |
| leapraid_fw_log_work); |
| snprintf(adapter->fw_log_desc.fw_log_wq_name, |
| sizeof(adapter->fw_log_desc.fw_log_wq_name), |
| "poll_%s%u_fw_log", |
| LEAPRAID_DRIVER_NAME, adapter->adapter_attr.id); |
| adapter->fw_log_desc.fw_log_wq = |
| create_singlethread_workqueue( |
| adapter->fw_log_desc.fw_log_wq_name); |
| if (!adapter->fw_log_desc.fw_log_wq) |
| return; |
| |
| spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags); |
| if (adapter->fw_log_desc.fw_log_wq) |
| queue_delayed_work( |
| adapter->fw_log_desc.fw_log_wq, |
| &adapter->fw_log_desc.fw_log_work, |
| msecs_to_jiffies(LEAPRAID_PCIE_LOG_POLLING_INTERVAL)); |
| spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags); |
| } |
| |
| static void leapraid_check_scheduled_fault_work(struct work_struct *work) |
| { |
| struct leapraid_adapter *adapter; |
| unsigned long flags; |
| u32 adapter_state; |
| int rc; |
| |
| adapter = container_of(work, struct leapraid_adapter, |
| reset_desc.fault_reset_work.work); |
| |
| if (leapraid_should_skip_poll_work(adapter)) |
| goto scheduled_timer; |
| |
| adapter_state = leapraid_get_adapter_state(adapter); |
| if (adapter_state != LEAPRAID_DB_OPERATIONAL) { |
| dev_info(&adapter->pdev->dev, "%s:%d: call hard_reset 0x%x\n", |
| __func__, __LINE__, adapter_state); |
| rc = leapraid_hard_reset_handler(adapter, FULL_RESET); |
| adapter_state = leapraid_get_adapter_state(adapter); |
| if (rc && adapter_state != LEAPRAID_DB_OPERATIONAL) { |
| dev_err(&adapter->pdev->dev, |
| "%s: Hard reset failed, state=0x%x rc=%d\n", |
| __func__, adapter_state, rc); |
| return; |
| } |
| } |
| |
| scheduled_timer: |
| spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags); |
| if (adapter->reset_desc.fault_reset_wq) |
| queue_delayed_work( |
| adapter->reset_desc.fault_reset_wq, |
| &adapter->reset_desc.fault_reset_work, |
| msecs_to_jiffies(LEAPRAID_FAULT_POLLING_INTERVAL)); |
| spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags); |
| } |
| |
| void leapraid_check_scheduled_fault_start(struct leapraid_adapter *adapter) |
| { |
| unsigned long flags; |
| |
| if (adapter->reset_desc.fault_reset_wq) |
| return; |
| |
| INIT_DELAYED_WORK(&adapter->reset_desc.fault_reset_work, |
| leapraid_check_scheduled_fault_work); |
| snprintf(adapter->reset_desc.fault_reset_wq_name, |
| sizeof(adapter->reset_desc.fault_reset_wq_name), |
| "poll_%s%u_status", |
| LEAPRAID_DRIVER_NAME, adapter->adapter_attr.id); |
| adapter->reset_desc.fault_reset_wq = |
| create_singlethread_workqueue( |
| adapter->reset_desc.fault_reset_wq_name); |
| if (!adapter->reset_desc.fault_reset_wq) { |
| dev_err(&adapter->pdev->dev, |
| "Create single thread workqueue failed!\n"); |
| return; |
| } |
| |
| spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags); |
| if (adapter->reset_desc.fault_reset_wq) |
| queue_delayed_work( |
| adapter->reset_desc.fault_reset_wq, |
| &adapter->reset_desc.fault_reset_work, |
| msecs_to_jiffies(LEAPRAID_FAULT_POLLING_INTERVAL)); |
| spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags); |
| } |
| |
| void leapraid_check_scheduled_fault_stop(struct leapraid_adapter *adapter) |
| { |
| struct workqueue_struct *wq; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags); |
| wq = adapter->reset_desc.fault_reset_wq; |
| adapter->reset_desc.fault_reset_wq = NULL; |
| spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags); |
| |
| if (!wq) |
| return; |
| |
| if (!cancel_delayed_work_sync(&adapter->reset_desc.fault_reset_work)) |
| flush_workqueue(wq); |
| destroy_workqueue(wq); |
| } |
| |
| static void leapraid_overheat_work(struct work_struct *work) |
| { |
| struct leapraid_overheat_desc *desc; |
| struct leapraid_adapter *adapter; |
| struct workqueue_struct *wq; |
| struct Scsi_Host *shost; |
| struct pci_dev *pdev; |
| unsigned long flags; |
| |
| desc = container_of(work, struct leapraid_overheat_desc, |
| fault_overheat_work); |
| adapter = container_of(desc, struct leapraid_adapter, overheat_desc); |
| pdev = adapter->pdev; |
| shost = pci_get_drvdata(pdev); |
| |
| if (!shost) { |
| dev_err(&pdev->dev, |
| "Overheat processing failed: invalid host/adapter\n"); |
| atomic_set(&adapter->overheat_desc.thermal_alert, 0); |
| wake_up(&adapter->scan_dev_desc.wait_driver_loading); |
| return; |
| } |
| |
| if (adapter->access_ctrl.host_removing) { |
| atomic_set(&adapter->overheat_desc.thermal_alert, 0); |
| wake_up(&adapter->scan_dev_desc.wait_driver_loading); |
| return; |
| } |
| |
| adapter->access_ctrl.host_removing = 1; |
| adapter->access_ctrl.shost_recover_async = 0; |
| adapter->scan_dev_desc.scan_start = 0; |
| adapter->scan_dev_desc.wait_scan_dev_done = 0; |
| adapter->scan_dev_desc.driver_loading = 0; |
| wake_up(&adapter->access_ctrl.shost_recover_wq); |
| wake_up(&adapter->scan_dev_desc.wait_driver_loading); |
| |
| leapraid_mask_int(adapter); |
| |
| leapraid_check_scheduled_fault_stop(adapter); |
| leapraid_fw_log_stop(adapter); |
| leapraid_mq_polling_pause(adapter); |
| |
| leapraid_clean_active_cmds(adapter); |
| spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags); |
| wq = adapter->fw_evt_s.fw_evt_thread; |
| adapter->fw_evt_s.fw_evt_thread = NULL; |
| spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags); |
| if (wq) |
| destroy_workqueue(wq); |
| |
| sas_remove_host(shost); |
| leapraid_cleanup_lists(adapter); |
| atomic_set(&adapter->overheat_desc.thermal_alert, 0); |
| wake_up(&adapter->scan_dev_desc.wait_driver_loading); |
| dev_err(&pdev->dev, "%s: Suspend adapter due to overheat\n", __func__); |
| } |
| |
| static void leapraid_overheat_init(struct leapraid_adapter *adapter) |
| { |
| if (adapter->overheat_desc.fault_overheat_wq) |
| return; |
| |
| atomic_set(&adapter->overheat_desc.thermal_alert, 0); |
| snprintf(adapter->overheat_desc.fault_overheat_wq_name, |
| sizeof(adapter->overheat_desc.fault_overheat_wq_name), |
| "driver_%s%u_overheat", |
| LEAPRAID_DRIVER_NAME, |
| adapter->adapter_attr.id); |
| adapter->overheat_desc.fault_overheat_wq = |
| create_singlethread_workqueue( |
| adapter->overheat_desc.fault_overheat_wq_name); |
| if (!adapter->overheat_desc.fault_overheat_wq) { |
| dev_err(&adapter->pdev->dev, |
| "Failed to create overheat workqueue\n"); |
| return; |
| } |
| |
| INIT_WORK(&adapter->overheat_desc.fault_overheat_work, |
| leapraid_overheat_work); |
| } |
| |
| void leapraid_overheat_cleanup(struct leapraid_adapter *adapter) |
| { |
| struct workqueue_struct *wq; |
| |
| wq = xchg(&adapter->overheat_desc.fault_overheat_wq, NULL); |
| if (!wq) |
| return; |
| |
| cancel_work_sync(&adapter->overheat_desc.fault_overheat_work); |
| destroy_workqueue(wq); |
| } |
| |
| static void leapraid_fw_work(struct leapraid_adapter *adapter, |
| struct leapraid_fw_evt_work *fw_evt); |
| |
| static void leapraid_fw_evt_free(struct kref *r) |
| { |
| struct leapraid_fw_evt_work *fw_evt; |
| |
| fw_evt = container_of(r, struct leapraid_fw_evt_work, refcnt); |
| |
| kfree(fw_evt->evt_data); |
| kfree(fw_evt); |
| } |
| |
| static void leapraid_fw_evt_get(struct leapraid_fw_evt_work *fw_evt) |
| { |
| kref_get(&fw_evt->refcnt); |
| } |
| |
| static void leapraid_fw_evt_put(struct leapraid_fw_evt_work *fw_work) |
| { |
| kref_put(&fw_work->refcnt, leapraid_fw_evt_free); |
| } |
| |
| static struct leapraid_fw_evt_work *leapraid_alloc_fw_evt_work(void) |
| { |
| struct leapraid_fw_evt_work *fw_evt = |
| kzalloc(sizeof(*fw_evt), GFP_ATOMIC); |
| |
| if (fw_evt) |
| kref_init(&fw_evt->refcnt); |
| |
| return fw_evt; |
| } |
| |
| static void leapraid_run_fw_evt_work(struct work_struct *work) |
| { |
| struct leapraid_fw_evt_work *fw_evt = |
| container_of(work, struct leapraid_fw_evt_work, work); |
| |
| leapraid_fw_work(fw_evt->adapter, fw_evt); |
| } |
| |
| static void leapraid_fw_evt_add(struct leapraid_adapter *adapter, |
| struct leapraid_fw_evt_work *fw_evt) |
| { |
| unsigned long flags; |
| |
| spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags); |
| if (adapter->access_ctrl.host_removing || |
| adapter->access_ctrl.pcie_recovering || |
| !adapter->fw_evt_s.fw_evt_thread) { |
| spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags); |
| return; |
| } |
| |
| leapraid_fw_evt_get(fw_evt); |
| INIT_LIST_HEAD(&fw_evt->list); |
| list_add_tail(&fw_evt->list, &adapter->fw_evt_s.fw_evt_list); |
| INIT_WORK(&fw_evt->work, leapraid_run_fw_evt_work); |
| leapraid_fw_evt_get(fw_evt); |
| queue_work(adapter->fw_evt_s.fw_evt_thread, &fw_evt->work); |
| spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags); |
| } |
| |
| static void leapraid_del_fw_evt_from_list(struct leapraid_adapter *adapter, |
| struct leapraid_fw_evt_work *fw_evt) |
| { |
| unsigned long flags; |
| |
| spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags); |
| if (!list_empty(&fw_evt->list)) { |
| list_del_init(&fw_evt->list); |
| leapraid_fw_evt_put(fw_evt); |
| } |
| spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags); |
| } |
| |
| static struct leapraid_fw_evt_work *leapraid_next_fw_evt( |
| struct leapraid_adapter *adapter) |
| { |
| struct leapraid_fw_evt_work *fw_evt = NULL; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags); |
| if (!list_empty(&adapter->fw_evt_s.fw_evt_list)) { |
| fw_evt = list_first_entry(&adapter->fw_evt_s.fw_evt_list, |
| struct leapraid_fw_evt_work, list); |
| list_del_init(&fw_evt->list); |
| } |
| spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags); |
| return fw_evt; |
| } |
| |
| void leapraid_clean_active_fw_evt(struct leapraid_adapter *adapter) |
| { |
| struct leapraid_fw_evt_work *fw_evt; |
| unsigned long flags; |
| bool in_fw_evt_context; |
| bool rc; |
| |
| if ((list_empty(&adapter->fw_evt_s.fw_evt_list) && |
| !adapter->fw_evt_s.cur_evt) || !adapter->fw_evt_s.fw_evt_thread) |
| return; |
| |
| adapter->fw_evt_s.fw_evt_cleanup = 1; |
| if (adapter->access_ctrl.shost_recovering && |
| adapter->fw_evt_s.cur_evt) |
| adapter->fw_evt_s.cur_evt->ignore = 1; |
| |
| while ((fw_evt = leapraid_next_fw_evt(adapter))) { |
| rc = cancel_work_sync(&fw_evt->work); |
| if (rc) |
| leapraid_fw_evt_put(fw_evt); |
| leapraid_fw_evt_put(fw_evt); |
| } |
| |
| spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags); |
| fw_evt = adapter->fw_evt_s.cur_evt; |
| if (fw_evt) { |
| in_fw_evt_context = adapter->fw_evt_s.cur_evt_task == current; |
| leapraid_fw_evt_get(fw_evt); |
| } |
| spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags); |
| if (fw_evt) { |
| if (!in_fw_evt_context) |
| cancel_work_sync(&fw_evt->work); |
| leapraid_fw_evt_put(fw_evt); |
| } |
| |
| adapter->fw_evt_s.fw_evt_cleanup = 0; |
| } |
| |
| static void leapraid_internal_dev_ublk(struct scsi_device *sdev, |
| struct leapraid_sdev_priv *sdev_priv) |
| { |
| int rc; |
| |
| sdev_printk(KERN_WARNING, sdev, |
| "hdl 0x%04x: Now internal unblkg dev\n", |
| sdev_priv->starget_priv->hdl); |
| sdev_priv->block = 0; |
| rc = scsi_internal_device_unblock_nowait(sdev, SDEV_RUNNING); |
| if (rc == -EINVAL) { |
| sdev_printk(KERN_WARNING, sdev, |
| "hdl 0x%04x: unblkg failed, rc=%d\n", |
| sdev_priv->starget_priv->hdl, rc); |
| sdev_priv->block = 1; |
| rc = scsi_internal_device_block_nowait(sdev); |
| if (rc) |
| sdev_printk(KERN_WARNING, sdev, |
| "hdl 0x%04x: Earlier ublkg err, rc=%d\n", |
| sdev_priv->starget_priv->hdl, rc); |
| |
| sdev_priv->block = 0; |
| rc = scsi_internal_device_unblock_nowait(sdev, SDEV_RUNNING); |
| if (rc) |
| sdev_printk(KERN_WARNING, sdev, |
| "hdl 0x%04x: ublkg failed again, rc=%d\n", |
| sdev_priv->starget_priv->hdl, rc); |
| } |
| } |
| |
| static void leapraid_ublk_io_dev(struct leapraid_adapter *adapter, |
| u64 sas_addr, |
| struct leapraid_card_port *card_port) |
| { |
| struct leapraid_sdev_priv *sdev_priv; |
| struct scsi_device *sdev; |
| |
| shost_for_each_device(sdev, adapter->shost) { |
| sdev_priv = sdev->hostdata; |
| if (!sdev_priv || !sdev_priv->starget_priv) |
| continue; |
| |
| if (sdev_priv->starget_priv->sas_address != sas_addr) |
| continue; |
| |
| if (sdev_priv->starget_priv->card_port != card_port) |
| continue; |
| |
| if (sdev_priv->block) |
| leapraid_internal_dev_ublk(sdev, sdev_priv); |
| |
| scsi_device_set_state(sdev, SDEV_OFFLINE); |
| } |
| } |
| |
| static void leapraid_ublk_io_all_dev(struct leapraid_adapter *adapter) |
| { |
| struct leapraid_sdev_priv *sdev_priv; |
| struct leapraid_starget_priv *stgt_priv; |
| struct scsi_device *sdev; |
| |
| shost_for_each_device(sdev, adapter->shost) { |
| sdev_priv = sdev->hostdata; |
| |
| if (!sdev_priv) |
| continue; |
| |
| stgt_priv = sdev_priv->starget_priv; |
| if (!stgt_priv || stgt_priv->deleted) |
| continue; |
| |
| if (!sdev_priv->block) |
| continue; |
| |
| sdev_printk(KERN_WARNING, sdev, "hdl 0x%04x: blkg...\n", |
| sdev_priv->starget_priv->hdl); |
| leapraid_internal_dev_ublk(sdev, sdev_priv); |
| continue; |
| } |
| } |
| |
| static void leapraid_internal_dev_blk( |
| struct scsi_device *sdev, |
| struct leapraid_sdev_priv *sdev_priv) |
| { |
| int rc; |
| |
| sdev_printk(KERN_INFO, sdev, "Internal blkg hdl 0x%04x\n", |
| sdev_priv->starget_priv->hdl); |
| sdev_priv->block = 1; |
| rc = scsi_internal_device_block_nowait(sdev); |
| if (rc == -EINVAL) |
| sdev_printk(KERN_WARNING, sdev, |
| "hdl 0x%04x: blkg failed, rc=%d\n", |
| rc, sdev_priv->starget_priv->hdl); |
| } |
| |
| static void leapraid_imm_blkio_to_end_dev(struct leapraid_adapter *adapter, |
| struct leapraid_sas_port *sas_port) |
| { |
| struct leapraid_sdev_priv *sdev_priv; |
| struct leapraid_sas_dev *sas_dev; |
| struct scsi_device *sdev; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); |
| sas_dev = leapraid_hold_lock_get_sas_dev_by_addr( |
| adapter, |
| sas_port->remote_identify.sas_address, |
| sas_port->card_port); |
| |
| if (sas_dev) { |
| shost_for_each_device(sdev, adapter->shost) { |
| sdev_priv = sdev->hostdata; |
| if (!sdev_priv) |
| continue; |
| |
| if (sdev_priv->starget_priv->hdl != sas_dev->hdl) |
| continue; |
| |
| if (sdev_priv->block) |
| continue; |
| |
| if (sas_dev && sas_dev->pend_sas_rphy_add) |
| continue; |
| |
| if (sdev_priv->sep) { |
| sdev_printk(KERN_INFO, sdev, |
| "skip dev blk: sep hdl 0x%04x\n", |
| sdev_priv->starget_priv->hdl); |
| continue; |
| } |
| |
| leapraid_internal_dev_blk(sdev, sdev_priv); |
| } |
| |
| leapraid_sdev_put(sas_dev); |
| } |
| spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); |
| } |
| |
| static void leapraid_imm_blkio_set_end_dev_blk_hdls( |
| struct leapraid_adapter *adapter, |
| struct leapraid_topo_node *topo_node_exp) |
| { |
| struct leapraid_sas_port *sas_port; |
| |
| list_for_each_entry(sas_port, |
| &topo_node_exp->sas_port_list, port_list) { |
| if (sas_port->remote_identify.device_type == SAS_END_DEVICE) |
| leapraid_imm_blkio_to_end_dev(adapter, sas_port); |
| } |
| } |
| |
| static void leapraid_imm_blkio_to_kids_attchd_to_ex( |
| struct leapraid_adapter *adapter, |
| struct leapraid_topo_node *topo_node_exp); |
| |
| static void leapraid_imm_blkio_to_sib_exp( |
| struct leapraid_adapter *adapter, |
| struct leapraid_topo_node *topo_node_exp) |
| { |
| struct leapraid_topo_node *topo_node_exp_sib; |
| struct leapraid_sas_port *sas_port; |
| |
| list_for_each_entry(sas_port, |
| &topo_node_exp->sas_port_list, port_list) { |
| if (sas_port->remote_identify.device_type == |
| SAS_EDGE_EXPANDER_DEVICE || |
| sas_port->remote_identify.device_type == |
| SAS_FANOUT_EXPANDER_DEVICE) { |
| topo_node_exp_sib = |
| leapraid_exp_find_by_sas_address( |
| adapter, |
| sas_port->remote_identify.sas_address, |
| sas_port->card_port); |
| leapraid_imm_blkio_to_kids_attchd_to_ex( |
| adapter, |
| topo_node_exp_sib); |
| } |
| } |
| } |
| |
| static void leapraid_imm_blkio_to_kids_attchd_to_ex( |
| struct leapraid_adapter *adapter, |
| struct leapraid_topo_node *topo_node_exp) |
| { |
| if (!topo_node_exp) |
| return; |
| |
| leapraid_imm_blkio_set_end_dev_blk_hdls(adapter, topo_node_exp); |
| |
| leapraid_imm_blkio_to_sib_exp(adapter, topo_node_exp); |
| } |
| |
| static void leapraid_report_sdev_directly(struct leapraid_adapter *adapter, |
| struct leapraid_sas_dev *sas_dev) |
| { |
| struct leapraid_sas_port *sas_port; |
| |
| sas_port = leapraid_transport_port_add(adapter, |
| sas_dev->hdl, |
| sas_dev->parent_sas_addr, |
| sas_dev->card_port); |
| if (!sas_port) { |
| leapraid_sas_dev_remove(adapter, sas_dev); |
| return; |
| } |
| |
| if (!sas_dev->starget) { |
| if (!adapter->scan_dev_desc.driver_loading) { |
| leapraid_transport_port_remove( |
| adapter, |
| sas_dev->sas_addr, |
| sas_dev->parent_sas_addr, |
| sas_dev->card_port); |
| leapraid_sas_dev_remove(adapter, sas_dev); |
| } |
| return; |
| } |
| } |
| |
| static struct leapraid_sas_dev *leapraid_init_sas_dev( |
| struct leapraid_adapter *adapter, |
| struct leapraid_sas_dev_p0 *sas_dev_pg0, |
| struct leapraid_card_port *card_port, u16 hdl, |
| u64 parent_sas_addr, u64 sas_addr, u32 dev_info) |
| { |
| struct leapraid_sas_dev *sas_dev; |
| struct leapraid_enc_node *enc_dev; |
| unsigned long flags; |
| |
| sas_dev = kzalloc_obj(*sas_dev); |
| if (!sas_dev) |
| return NULL; |
| |
| kref_init(&sas_dev->refcnt); |
| sas_dev->hdl = hdl; |
| sas_dev->dev_info = dev_info; |
| sas_dev->sas_addr = sas_addr; |
| sas_dev->card_port = card_port; |
| sas_dev->parent_sas_addr = parent_sas_addr; |
| sas_dev->phy = sas_dev_pg0->phy_num; |
| sas_dev->enc_hdl = le16_to_cpu(sas_dev_pg0->enc_hdl); |
| sas_dev->dev_name = le64_to_cpu(sas_dev_pg0->dev_name); |
| sas_dev->port_connection = sas_dev_pg0->max_port_connections; |
| sas_dev->slot = sas_dev->enc_hdl ? le16_to_cpu(sas_dev_pg0->slot) : 0; |
| if (le16_to_cpu(sas_dev_pg0->flg) & |
| LEAPRAID_SAS_DEV_P0_FLG_ENC_LEVEL_VALID) { |
| sas_dev->enc_level = sas_dev_pg0->enc_level; |
| memcpy(sas_dev->connector_name, |
| sas_dev_pg0->connector_name, |
| LEAPRAID_SAS_DEV_P0_CON_NAME_LEN); |
| sas_dev->connector_name[LEAPRAID_SAS_DEV_P0_CON_NAME_LEN] = |
| '\0'; |
| } else { |
| sas_dev->enc_level = 0; |
| sas_dev->connector_name[0] = '\0'; |
| } |
| if (sas_dev->enc_hdl) { |
| spin_lock_irqsave(&adapter->dev_topo.enc_lock, flags); |
| enc_dev = leapraid_enc_find_by_hdl(adapter, sas_dev->enc_hdl); |
| if (enc_dev) |
| sas_dev->enc_lid = le64_to_cpu(enc_dev->pg0.enc_lid); |
| spin_unlock_irqrestore(&adapter->dev_topo.enc_lock, flags); |
| } |
| dev_info(&adapter->pdev->dev, |
| "add dev: hdl=0x%x, SAS addr=0x%016llx, port connect=0x%x\n", |
| hdl, sas_dev->sas_addr, sas_dev->port_connection); |
| |
| return sas_dev; |
| } |
| |
| static void leapraid_add_dev(struct leapraid_adapter *adapter, u16 hdl) |
| { |
| union cfg_param_1 cfgp1 = {0}; |
| union cfg_param_2 cfgp2 = {0}; |
| struct leapraid_sas_dev_p0 sas_dev_pg0; |
| struct leapraid_card_port *card_port; |
| struct leapraid_sas_dev *sas_dev; |
| unsigned long flags; |
| u64 parent_sas_addr; |
| u32 dev_info; |
| u64 sas_addr; |
| u8 port_id; |
| |
| cfgp1.form = LEAPRAID_SAS_DEV_CFG_PGAD_HDL; |
| cfgp2.handle = hdl; |
| if (leapraid_op_config_page(adapter, &sas_dev_pg0, |
| cfgp1, cfgp2, GET_SAS_DEVICE_PG0)) |
| return; |
| |
| dev_info = le32_to_cpu(sas_dev_pg0.dev_info); |
| if (!(leapraid_is_end_dev(dev_info))) |
| return; |
| |
| sas_addr = le64_to_cpu(sas_dev_pg0.sas_address); |
| if (!(le16_to_cpu(sas_dev_pg0.flg) & |
| LEAPRAID_SAS_DEV_P0_FLG_DEV_PRESENT)) |
| return; |
| |
| port_id = sas_dev_pg0.physical_port; |
| card_port = leapraid_get_port_by_id(adapter, port_id, false); |
| if (!card_port) |
| return; |
| |
| sas_dev = leapraid_get_sas_dev_by_addr(adapter, sas_addr, card_port); |
| if (sas_dev) { |
| leapraid_sdev_put(sas_dev); |
| return; |
| } |
| |
| if (leapraid_get_sas_address(adapter, |
| le16_to_cpu(sas_dev_pg0.parent_dev_hdl), |
| &parent_sas_addr)) |
| return; |
| |
| sas_dev = leapraid_init_sas_dev(adapter, &sas_dev_pg0, card_port, |
| hdl, parent_sas_addr, sas_addr, |
| dev_info); |
| if (!sas_dev) |
| return; |
| |
| if (adapter->scan_dev_desc.wait_scan_dev_done) { |
| spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); |
| leapraid_sdev_get(sas_dev); |
| list_add_tail(&sas_dev->list, |
| &adapter->dev_topo.sas_dev_init_list); |
| leapraid_check_boot_dev(adapter, sas_dev, 0); |
| spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); |
| } else { |
| spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); |
| leapraid_sdev_get(sas_dev); |
| list_add_tail(&sas_dev->list, &adapter->dev_topo.sas_dev_list); |
| spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); |
| leapraid_report_sdev_directly(adapter, sas_dev); |
| } |
| leapraid_sdev_put(sas_dev); |
| } |
| |
| static void leapraid_remove_device(struct leapraid_adapter *adapter, |
| struct leapraid_sas_dev *sas_dev) |
| { |
| struct leapraid_starget_priv *starget_priv; |
| |
| leapraid_clear_cached_boot_dev(adapter, sas_dev, 0); |
| if (sas_dev->led_on) { |
| leapraid_set_led(adapter, sas_dev, false); |
| sas_dev->led_on = 0; |
| } |
| |
| if (sas_dev->starget && sas_dev->starget->hostdata) { |
| starget_priv = sas_dev->starget->hostdata; |
| starget_priv->deleted = 1; |
| leapraid_ublk_io_dev(adapter, |
| sas_dev->sas_addr, sas_dev->card_port); |
| starget_priv->hdl = LEAPRAID_INVALID_DEV_HANDLE; |
| } |
| |
| leapraid_transport_port_remove(adapter, |
| sas_dev->sas_addr, |
| sas_dev->parent_sas_addr, |
| sas_dev->card_port); |
| |
| dev_info(&adapter->pdev->dev, |
| "remove dev: hdl=0x%04x, SAS addr=0x%016llx\n", |
| sas_dev->hdl, (unsigned long long)sas_dev->sas_addr); |
| } |
| |
| static struct leapraid_vphy *leapraid_alloc_vphy( |
| struct leapraid_adapter *adapter, |
| u8 port_id, u8 phy_num) |
| { |
| struct leapraid_card_port *port; |
| struct leapraid_vphy *vphy; |
| |
| port = leapraid_get_port_by_id(adapter, port_id, false); |
| if (!port) |
| return NULL; |
| |
| vphy = leapraid_get_vphy_by_phy(port, phy_num); |
| if (vphy) |
| return vphy; |
| vphy = kzalloc_obj(*vphy); |
| if (!vphy) |
| return NULL; |
| |
| if (!port->vphys_mask) |
| INIT_LIST_HEAD(&port->vphys_list); |
| |
| port->vphys_mask |= BIT(phy_num); |
| vphy->phy_mask |= BIT(phy_num); |
| list_add_tail(&vphy->list, &port->vphys_list); |
| return vphy; |
| } |
| |
| static int leapraid_add_port_to_card_port_list( |
| struct leapraid_adapter *adapter, |
| u8 port_id, bool refresh) |
| { |
| struct leapraid_card_port *card_port; |
| |
| card_port = leapraid_get_port_by_id(adapter, port_id, false); |
| if (card_port) |
| return 0; |
| card_port = kzalloc_obj(*card_port); |
| if (!card_port) |
| return -ENOMEM; |
| |
| card_port->port_id = port_id; |
| dev_dbg(&adapter->pdev->dev, |
| "port: %d is added to card_port list\n", |
| card_port->port_id); |
| |
| if (refresh && adapter->access_ctrl.shost_recovering) |
| card_port->flg = LEAPRAID_CARD_PORT_FLG_NEW; |
| list_add_tail(&card_port->list, &adapter->dev_topo.card_port_list); |
| return 0; |
| } |
| |
| static int leapraid_add_card_phy(struct leapraid_adapter *adapter, |
| struct leapraid_sas_io_unit_p0 *iou, |
| int i) |
| { |
| struct leapraid_sas_phy_p0 phy_pg0; |
| union cfg_param_1 cfgp1 = {0}; |
| union cfg_param_2 cfgp2 = {0}; |
| struct leapraid_topo_node *card = &adapter->dev_topo.card; |
| u8 port_id; |
| |
| cfgp1.phy_number = i; |
| |
| if (leapraid_op_config_page(adapter, &phy_pg0, |
| cfgp1, cfgp2, GET_PHY_PG0)) |
| return -EINVAL; |
| |
| port_id = iou->phy_info[i].port; |
| |
| if (leapraid_add_port_to_card_port_list(adapter, port_id, false)) |
| return -EINVAL; |
| |
| if ((le32_to_cpu(phy_pg0.phy_info) & |
| LEAPRAID_SAS_PHYINFO_VPHY) && |
| ((phy_pg0.neg_link_rate >> |
| LEAPRAID_SAS_NEG_LINK_RATE_SHIFT) >= |
| LEAPRAID_SAS_NEG_LINK_RATE_1_5)) { |
| if (!leapraid_alloc_vphy(adapter, port_id, i)) |
| return -ENOMEM; |
| |
| card->card_phy[i].vphy = 1; |
| } |
| |
| card->card_phy[i].hdl = card->hdl; |
| card->card_phy[i].phy_id = i; |
| card->card_phy[i].card_port = |
| leapraid_get_port_by_id(adapter, port_id, false); |
| |
| leapraid_transport_add_card_phy(adapter, |
| &card->card_phy[i], |
| &phy_pg0, |
| card->parent_dev); |
| |
| return 0; |
| } |
| |
| static int leapraid_refresh_card_phy(struct leapraid_adapter *adapter, |
| struct leapraid_sas_io_unit_p0 *iou, |
| int i) |
| { |
| struct leapraid_topo_node *card = &adapter->dev_topo.card; |
| struct leapraid_sas_phy_p0 phy_pg0; |
| union cfg_param_1 cfgp1 = {0}; |
| union cfg_param_2 cfgp2 = {0}; |
| u16 attached_hdl; |
| u32 dev_info; |
| u8 link_rate; |
| u8 port_id; |
| |
| link_rate = iou->phy_info[i].neg_link_rate >> |
| LEAPRAID_SAS_NEG_LINK_RATE_SHIFT; |
| port_id = iou->phy_info[i].port; |
| if (leapraid_add_port_to_card_port_list(adapter, port_id, true)) |
| return -EINVAL; |
| |
| dev_info = le32_to_cpu(iou->phy_info[i].controller_phy_dev_info); |
| if (dev_info & LEAPRAID_DEVTYP_SEP && |
| link_rate >= LEAPRAID_SAS_NEG_LINK_RATE_1_5) { |
| cfgp1.phy_number = i; |
| |
| if (leapraid_op_config_page(adapter, &phy_pg0, |
| cfgp1, cfgp2, GET_PHY_PG0)) |
| return 0; |
| |
| if (le32_to_cpu(phy_pg0.phy_info) & |
| LEAPRAID_SAS_PHYINFO_VPHY) { |
| if (!leapraid_alloc_vphy(adapter, port_id, i)) |
| return -ENOMEM; |
| card->card_phy[i].vphy = 1; |
| } |
| } |
| |
| card->card_phy[i].hdl = card->hdl; |
| |
| attached_hdl = le16_to_cpu(iou->phy_info[i].attached_dev_hdl); |
| if (attached_hdl && link_rate < LEAPRAID_SAS_NEG_LINK_RATE_1_5) |
| link_rate = LEAPRAID_SAS_NEG_LINK_RATE_1_5; |
| |
| card->card_phy[i].card_port = |
| leapraid_get_port_by_id(adapter, port_id, false); |
| |
| if (!card->card_phy[i].phy) { |
| cfgp1.phy_number = i; |
| if (leapraid_op_config_page(adapter, &phy_pg0, |
| cfgp1, cfgp2, GET_PHY_PG0)) |
| return 0; |
| |
| card->card_phy[i].phy_id = i; |
| |
| leapraid_transport_add_card_phy(adapter, |
| &card->card_phy[i], |
| &phy_pg0, |
| card->parent_dev); |
| return 0; |
| } |
| |
| leapraid_transport_update_links(adapter, |
| card->sas_address, |
| attached_hdl, |
| i, |
| link_rate, |
| card->card_phy[i].card_port); |
| |
| return 0; |
| } |
| |
| static void leapraid_sas_host_add(struct leapraid_adapter *adapter, |
| bool refresh) |
| { |
| union cfg_param_1 cfgp1 = {0}; |
| union cfg_param_2 cfgp2 = {0}; |
| struct leapraid_sas_dev_p0 sas_dev_pg0; |
| struct leapraid_enc_p0 enc_pg0; |
| struct leapraid_sas_io_unit_p0 *sas_iou_pg0; |
| u16 sas_iou_pg0_sz; |
| u8 phys_num; |
| int i; |
| int rc; |
| |
| if (!refresh) { |
| if (leapraid_get_adapter_phys(adapter, &phys_num) || !phys_num) |
| return; |
| |
| adapter->dev_topo.card.card_phy = |
| kcalloc(phys_num, |
| sizeof(struct leapraid_card_phy), GFP_KERNEL); |
| if (!adapter->dev_topo.card.card_phy) |
| return; |
| |
| adapter->dev_topo.card.phys_num = phys_num; |
| } |
| |
| sas_iou_pg0_sz = |
| offsetof(struct leapraid_sas_io_unit_p0, phy_info) + |
| (adapter->dev_topo.card.phys_num * |
| sizeof(struct leapraid_sas_io_unit0_phy_info)); |
| sas_iou_pg0 = kzalloc(sas_iou_pg0_sz, GFP_KERNEL); |
| if (!sas_iou_pg0) |
| return; |
| |
| if (leapraid_get_sas_io_unit_page0(adapter, |
| sas_iou_pg0, |
| sas_iou_pg0_sz)) |
| goto out_free; |
| |
| adapter->dev_topo.card.parent_dev = &adapter->shost->shost_gendev; |
| adapter->dev_topo.card.hdl = |
| le16_to_cpu(sas_iou_pg0->phy_info[0].controller_dev_hdl); |
| for (i = 0; i < adapter->dev_topo.card.phys_num; i++) { |
| if (!refresh) /* add */ |
| rc = leapraid_add_card_phy(adapter, sas_iou_pg0, i); |
| else /* refresh */ |
| rc = leapraid_refresh_card_phy(adapter, |
| sas_iou_pg0, |
| i); |
| if (rc) |
| goto out_free; |
| } |
| |
| if (!refresh) { |
| cfgp1.form = LEAPRAID_SAS_DEV_CFG_PGAD_HDL; |
| cfgp2.handle = adapter->dev_topo.card.hdl; |
| if (leapraid_op_config_page(adapter, &sas_dev_pg0, cfgp1, |
| cfgp2, GET_SAS_DEVICE_PG0)) |
| goto out_free; |
| |
| adapter->dev_topo.card.enc_hdl = |
| le16_to_cpu(sas_dev_pg0.enc_hdl); |
| adapter->dev_topo.card.sas_address = |
| le64_to_cpu(sas_dev_pg0.sas_address); |
| dev_info(&adapter->pdev->dev, |
| "add host: hdl=0x%04x, SAS addr=0x%016llx, phy=%d\n", |
| adapter->dev_topo.card.hdl, |
| (unsigned long long)adapter->dev_topo.card.sas_address, |
| adapter->dev_topo.card.phys_num); |
| |
| if (adapter->dev_topo.card.enc_hdl) { |
| cfgp1.form = LEAPRAID_SAS_ENC_CFG_PGAD_HDL; |
| cfgp2.handle = adapter->dev_topo.card.enc_hdl; |
| if (!(leapraid_op_config_page(adapter, &enc_pg0, |
| cfgp1, cfgp2, |
| GET_SAS_ENCLOSURE_PG0))) |
| adapter->dev_topo.card.enc_lid = |
| le64_to_cpu(enc_pg0.enc_lid); |
| } |
| } |
| out_free: |
| kfree(sas_iou_pg0); |
| } |
| |
| static int leapraid_internal_exp_add(struct leapraid_adapter *adapter, |
| struct leapraid_exp_p0 *exp_pg0, |
| union cfg_param_1 *cfgp1, |
| union cfg_param_2 *cfgp2, |
| u16 hdl) |
| { |
| struct leapraid_topo_node *topo_node_exp; |
| struct leapraid_sas_port *sas_port = NULL; |
| struct leapraid_enc_node *enc_dev; |
| struct leapraid_exp_p1 exp_pg1; |
| int ret; |
| int rc; |
| unsigned long flags; |
| u8 port_id; |
| u16 parent_handle; |
| u64 sas_addr_parent; |
| int i; |
| |
| port_id = exp_pg0->physical_port; |
| parent_handle = le16_to_cpu(exp_pg0->parent_dev_hdl); |
| |
| rc = leapraid_get_sas_address(adapter, |
| parent_handle, &sas_addr_parent); |
| if (rc) |
| return rc; |
| topo_node_exp = kzalloc_obj(*topo_node_exp); |
| if (!topo_node_exp) |
| return -ENOMEM; |
| |
| topo_node_exp->hdl = hdl; |
| topo_node_exp->phys_num = exp_pg0->phy_num; |
| topo_node_exp->sas_address_parent = sas_addr_parent; |
| topo_node_exp->sas_address = le64_to_cpu(exp_pg0->sas_address); |
| topo_node_exp->card_port = |
| leapraid_get_port_by_id(adapter, port_id, false); |
| if (!topo_node_exp->card_port) { |
| rc = -EPERM; |
| goto out_fail; |
| } |
| |
| dev_info(&adapter->pdev->dev, |
| "add exp: saddr=0x%016llx, hdl=0x%04x, phdl=0x%04x, phy=%d\n", |
| (unsigned long long)topo_node_exp->sas_address, |
| hdl, parent_handle, |
| topo_node_exp->phys_num); |
| if (!topo_node_exp->phys_num) { |
| dev_err(&adapter->pdev->dev, |
| "%s: Invalid PHY num from exp_pg0\n", __func__); |
| rc = -EPERM; |
| goto out_fail; |
| } |
| |
| topo_node_exp->card_phy = |
| kcalloc(topo_node_exp->phys_num, |
| sizeof(struct leapraid_card_phy), GFP_KERNEL); |
| if (!topo_node_exp->card_phy) { |
| dev_err(&adapter->pdev->dev, |
| "%s: Failed to alloc expander phy array, count=%u\n", |
| __func__, topo_node_exp->phys_num); |
| rc = -EPERM; |
| goto out_fail; |
| } |
| |
| INIT_LIST_HEAD(&topo_node_exp->sas_port_list); |
| sas_port = leapraid_transport_port_add(adapter, hdl, sas_addr_parent, |
| topo_node_exp->card_port); |
| if (!sas_port) { |
| rc = -EPERM; |
| goto out_fail; |
| } |
| |
| topo_node_exp->parent_dev = &sas_port->rphy->dev; |
| topo_node_exp->rphy = sas_port->rphy; |
| for (i = 0; i < topo_node_exp->phys_num; i++) { |
| cfgp1->phy_number = i; |
| cfgp2->handle = hdl; |
| if (leapraid_op_config_page(adapter, &exp_pg1, *cfgp1, *cfgp2, |
| GET_SAS_EXPANDER_PG1)) { |
| dev_err(&adapter->pdev->dev, |
| "%s: Failed to get exp_pg1, phy=%d\n", |
| __func__, i); |
| rc = -EPERM; |
| goto out_fail; |
| } |
| |
| topo_node_exp->card_phy[i].hdl = hdl; |
| topo_node_exp->card_phy[i].phy_id = i; |
| topo_node_exp->card_phy[i].card_port = |
| leapraid_get_port_by_id(adapter, port_id, false); |
| ret = leapraid_transport_add_exp_phy( |
| adapter, |
| &topo_node_exp->card_phy[i], |
| &exp_pg1, |
| topo_node_exp->parent_dev); |
| if (ret) { |
| rc = -EPERM; |
| goto out_fail; |
| } |
| } |
| |
| if (topo_node_exp->enc_hdl) { |
| spin_lock_irqsave(&adapter->dev_topo.enc_lock, flags); |
| enc_dev = leapraid_enc_find_by_hdl(adapter, |
| topo_node_exp->enc_hdl); |
| if (enc_dev) |
| topo_node_exp->enc_lid = |
| le64_to_cpu(enc_dev->pg0.enc_lid); |
| spin_unlock_irqrestore(&adapter->dev_topo.enc_lock, flags); |
| } |
| |
| spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags); |
| list_add_tail(&topo_node_exp->list, &adapter->dev_topo.exp_list); |
| spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags); |
| return 0; |
| |
| out_fail: |
| if (sas_port) |
| leapraid_transport_port_remove(adapter, |
| topo_node_exp->sas_address, |
| sas_addr_parent, |
| topo_node_exp->card_port); |
| kfree(topo_node_exp->card_phy); |
| kfree(topo_node_exp); |
| return rc; |
| } |
| |
| static int leapraid_exp_add(struct leapraid_adapter *adapter, u16 hdl) |
| { |
| union cfg_param_1 cfgp1 = {0}; |
| union cfg_param_2 cfgp2 = {0}; |
| struct leapraid_topo_node *topo_node_exp; |
| struct leapraid_exp_p0 exp_pg0; |
| u16 parent_handle; |
| u64 sas_addr, sas_addr_parent; |
| unsigned long flags; |
| u8 port_id; |
| int rc; |
| |
| if (!hdl) { |
| dev_warn(&adapter->pdev->dev, "%s: Invalid hdl\n", __func__); |
| return -EPERM; |
| } |
| |
| if (adapter->access_ctrl.shost_recovering || |
| adapter->access_ctrl.pcie_recovering) { |
| dev_warn(&adapter->pdev->dev, |
| "%s: Failed, shost_recovering=%d pcie_recovering=%d\n", |
| __func__, adapter->access_ctrl.shost_recovering, |
| adapter->access_ctrl.pcie_recovering); |
| return -EPERM; |
| } |
| |
| cfgp1.form = LEAPRAID_SAS_EXP_CFD_PGAD_HDL; |
| cfgp2.handle = hdl; |
| if (leapraid_op_config_page(adapter, &exp_pg0, cfgp1, cfgp2, |
| GET_SAS_EXPANDER_PG0)) |
| return -EPERM; |
| |
| parent_handle = le16_to_cpu(exp_pg0.parent_dev_hdl); |
| if (leapraid_get_sas_address(adapter, parent_handle, &sas_addr_parent)) |
| return -EPERM; |
| |
| port_id = exp_pg0.physical_port; |
| if (sas_addr_parent != adapter->dev_topo.card.sas_address) { |
| spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags); |
| topo_node_exp = |
| leapraid_exp_find_by_sas_address( |
| adapter, |
| sas_addr_parent, |
| leapraid_get_port_by_id(adapter, |
| port_id, |
| false)); |
| spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, |
| flags); |
| if (!topo_node_exp) { |
| rc = leapraid_exp_add(adapter, parent_handle); |
| if (rc != 0) |
| return rc; |
| } |
| } |
| |
| spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags); |
| sas_addr = le64_to_cpu(exp_pg0.sas_address); |
| topo_node_exp = |
| leapraid_exp_find_by_sas_address( |
| adapter, |
| sas_addr, |
| leapraid_get_port_by_id(adapter, port_id, false)); |
| spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags); |
| |
| if (topo_node_exp) |
| return 0; |
| |
| return leapraid_internal_exp_add(adapter, &exp_pg0, &cfgp1, |
| &cfgp2, hdl); |
| } |
| |
| static void leapraid_exp_node_rm(struct leapraid_adapter *adapter, |
| struct leapraid_topo_node *topo_node_exp) |
| { |
| struct leapraid_sas_port *sas_port, *sas_port_next; |
| unsigned long flags; |
| int port_id; |
| |
| list_for_each_entry_safe(sas_port, sas_port_next, |
| &topo_node_exp->sas_port_list, |
| port_list) { |
| if (adapter->access_ctrl.shost_recovering) |
| return; |
| |
| switch (sas_port->remote_identify.device_type) { |
| case SAS_END_DEVICE: |
| leapraid_sas_dev_remove_by_sas_address( |
| adapter, |
| sas_port->remote_identify.sas_address, |
| sas_port->card_port); |
| break; |
| case SAS_EDGE_EXPANDER_DEVICE: |
| case SAS_FANOUT_EXPANDER_DEVICE: |
| leapraid_exp_rm( |
| adapter, |
| sas_port->remote_identify.sas_address, |
| sas_port->card_port); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| port_id = topo_node_exp->card_port->port_id; |
| leapraid_transport_port_remove(adapter, topo_node_exp->sas_address, |
| topo_node_exp->sas_address_parent, |
| topo_node_exp->card_port); |
| dev_info(&adapter->pdev->dev, |
| "removing exp: port=%d, SAS addr=0x%016llx, hdl=0x%04x\n", |
| port_id, (unsigned long long)topo_node_exp->sas_address, |
| topo_node_exp->hdl); |
| spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags); |
| list_del(&topo_node_exp->list); |
| spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags); |
| kfree(topo_node_exp->card_phy); |
| kfree(topo_node_exp); |
| } |
| |
| void leapraid_exp_rm(struct leapraid_adapter *adapter, u64 sas_addr, |
| struct leapraid_card_port *port) |
| { |
| struct leapraid_topo_node *topo_node_exp; |
| unsigned long flags; |
| |
| if (adapter->access_ctrl.shost_recovering) |
| return; |
| |
| if (!port) |
| return; |
| |
| spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags); |
| topo_node_exp = leapraid_exp_find_by_sas_address(adapter, |
| sas_addr, |
| port); |
| spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags); |
| |
| if (topo_node_exp) |
| leapraid_exp_node_rm(adapter, topo_node_exp); |
| } |
| |
| static void leapraid_internal_sas_topo_chg_evt( |
| struct leapraid_adapter *adapter, |
| struct leapraid_card_port *card_port, |
| struct leapraid_topo_node *topo_node_exp, |
| struct leapraid_fw_evt_work *fw_evt, |
| u64 sas_addr, u8 max_phys) |
| { |
| struct leapraid_evt_data_sas_topo_change_list *evt_data; |
| u8 phy_number; |
| u8 link_rate; |
| u16 reason_code; |
| u16 hdl; |
| int i; |
| |
| evt_data = fw_evt->evt_data; |
| for (i = 0; i < evt_data->entry_num; i++) { |
| if (fw_evt->ignore) |
| return; |
| |
| if (adapter->access_ctrl.host_removing || |
| adapter->access_ctrl.pcie_recovering) |
| return; |
| |
| phy_number = evt_data->start_phy_num + i; |
| if (phy_number >= max_phys) |
| continue; |
| |
| reason_code = evt_data->phy[i].phy_status & |
| LEAPRAID_EVT_SAS_TOPO_RC_MASK; |
| |
| hdl = le16_to_cpu(evt_data->phy[i].attached_dev_hdl); |
| if (!hdl || |
| hdl > adapter->adapter_attr.features.max_dev_handle) { |
| dev_warn(&adapter->pdev->dev, |
| "%s: Invalid device handle\n", __func__); |
| continue; |
| } |
| link_rate = evt_data->phy[i].link_rate >> |
| LEAPRAID_SAS_NEG_LINK_RATE_SHIFT; |
| switch (reason_code) { |
| case LEAPRAID_EVT_SAS_TOPO_RC_TARG_ADDED: |
| if (adapter->access_ctrl.shost_recovering) |
| break; |
| leapraid_transport_update_links(adapter, sas_addr, |
| hdl, phy_number, |
| link_rate, card_port); |
| if (link_rate < LEAPRAID_SAS_NEG_LINK_RATE_1_5) |
| break; |
| leapraid_add_dev(adapter, hdl); |
| break; |
| case LEAPRAID_EVT_SAS_TOPO_RC_TARG_NOT_RESPONDING: |
| leapraid_sas_dev_remove_by_hdl(adapter, hdl); |
| break; |
| } |
| } |
| |
| if (evt_data->exp_status == LEAPRAID_EVT_SAS_TOPO_ES_NOT_RESPONDING && |
| topo_node_exp) |
| leapraid_exp_rm(adapter, sas_addr, card_port); |
| } |
| |
| static void leapraid_sas_topo_chg_evt(struct leapraid_adapter *adapter, |
| struct leapraid_fw_evt_work *fw_evt) |
| { |
| struct leapraid_topo_node *topo_node_exp; |
| struct leapraid_card_port *card_port; |
| struct leapraid_evt_data_sas_topo_change_list *evt_data; |
| u16 phdl; |
| u8 max_phys; |
| u64 sas_addr; |
| unsigned long flags; |
| |
| if (adapter->access_ctrl.shost_recovering || |
| adapter->access_ctrl.host_removing || |
| adapter->access_ctrl.pcie_recovering) |
| return; |
| |
| evt_data = fw_evt->evt_data; |
| leapraid_sas_host_add(adapter, adapter->dev_topo.card.phys_num > 0); |
| |
| if (fw_evt->ignore) |
| return; |
| |
| phdl = le16_to_cpu(evt_data->exp_dev_hdl); |
| card_port = leapraid_get_port_by_id(adapter, |
| evt_data->physical_port, |
| false); |
| if (evt_data->exp_status == LEAPRAID_EVT_SAS_TOPO_ES_ADDED && |
| leapraid_exp_add(adapter, phdl) != 0) |
| return; |
| |
| spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags); |
| topo_node_exp = leapraid_exp_find_by_hdl(adapter, phdl); |
| if (topo_node_exp) { |
| sas_addr = topo_node_exp->sas_address; |
| max_phys = topo_node_exp->phys_num; |
| card_port = topo_node_exp->card_port; |
| } else if (phdl < adapter->dev_topo.card.phys_num) { |
| sas_addr = adapter->dev_topo.card.sas_address; |
| max_phys = adapter->dev_topo.card.phys_num; |
| } else { |
| spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, |
| flags); |
| return; |
| } |
| spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags); |
| |
| leapraid_internal_sas_topo_chg_evt(adapter, card_port, |
| topo_node_exp, fw_evt, |
| sas_addr, max_phys); |
| } |
| |
| static void leapraid_reprobe_lun(struct scsi_device *sdev, void *no_uld_attach) |
| { |
| sdev->no_uld_attach = no_uld_attach ? 1 : 0; |
| sdev_printk(KERN_INFO, sdev, |
| "%s RAID component to upper layer\n", |
| sdev->no_uld_attach ? "hide" : "expose"); |
| WARN_ON(scsi_device_reprobe(sdev)); |
| } |
| |
| static void leapraid_sas_pd_add( |
| struct leapraid_adapter *adapter, |
| struct leapraid_evt_data_ir_change *evt_data) |
| { |
| union cfg_param_1 cfgp1 = {0}; |
| union cfg_param_2 cfgp2 = {0}; |
| struct leapraid_sas_dev_p0 sas_dev_p0; |
| struct leapraid_sas_dev *sas_dev; |
| u64 sas_address; |
| u16 parent_hdl; |
| u16 hdl; |
| |
| hdl = le16_to_cpu(evt_data->phys_disk_dev_hdl); |
| if (!hdl || hdl > adapter->adapter_attr.features.max_dev_handle) { |
| dev_warn(&adapter->pdev->dev, |
| "%s: Invalid device handle\n", __func__); |
| return; |
| } |
| |
| set_bit(hdl, adapter->dev_topo.pd_hdls); |
| sas_dev = leapraid_get_sas_dev_by_hdl(adapter, hdl); |
| if (sas_dev) { |
| leapraid_sdev_put(sas_dev); |
| dev_warn(&adapter->pdev->dev, |
| "Dev handle 0x%x already exists\n", hdl); |
| return; |
| } |
| |
| cfgp1.form = LEAPRAID_SAS_DEV_CFG_PGAD_HDL; |
| cfgp2.handle = hdl; |
| if (leapraid_op_config_page(adapter, &sas_dev_p0, cfgp1, cfgp2, |
| GET_SAS_DEVICE_PG0)) { |
| dev_warn(&adapter->pdev->dev, "Failed to read dev page0\n"); |
| return; |
| } |
| |
| parent_hdl = le16_to_cpu(sas_dev_p0.parent_dev_hdl); |
| if (!leapraid_get_sas_address(adapter, parent_hdl, &sas_address)) |
| leapraid_transport_update_links(adapter, sas_address, hdl, |
| sas_dev_p0.phy_num, |
| LEAPRAID_SAS_NEG_LINK_RATE_1_5, |
| leapraid_get_port_by_id(adapter, |
| sas_dev_p0.physical_port, |
| false)); |
| leapraid_add_dev(adapter, hdl); |
| } |
| |
| static void leapraid_sas_pd_delete( |
| struct leapraid_adapter *adapter, |
| struct leapraid_evt_data_ir_change *evt_data) |
| { |
| u16 hdl; |
| |
| hdl = le16_to_cpu(evt_data->phys_disk_dev_hdl); |
| leapraid_sas_dev_remove_by_hdl(adapter, hdl); |
| } |
| |
| static void leapraid_sas_pd_hide( |
| struct leapraid_adapter *adapter, |
| struct leapraid_evt_data_ir_change *evt_data) |
| { |
| struct leapraid_starget_priv *starget_priv; |
| struct scsi_target *starget = NULL; |
| struct leapraid_sas_dev *sas_dev; |
| unsigned long flags; |
| u64 volume_wwid = 0; |
| u16 volume_hdl; |
| u16 hdl; |
| |
| hdl = le16_to_cpu(evt_data->phys_disk_dev_hdl); |
| if (!hdl || hdl > adapter->adapter_attr.features.max_dev_handle) { |
| dev_warn(&adapter->pdev->dev, |
| "%s: Invalid device handle\n", __func__); |
| return; |
| } |
| leapraid_cfg_get_volume_hdl(adapter, hdl, &volume_hdl); |
| if (volume_hdl) |
| leapraid_cfg_get_volume_wwid(adapter, |
| volume_hdl, |
| &volume_wwid); |
| |
| spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); |
| sas_dev = leapraid_hold_lock_get_sas_dev_by_hdl(adapter, hdl); |
| if (!sas_dev) { |
| spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); |
| return; |
| } |
| |
| set_bit(hdl, adapter->dev_topo.pd_hdls); |
| if (sas_dev->starget && sas_dev->starget->hostdata) { |
| starget = sas_dev->starget; |
| starget_priv = starget->hostdata; |
| starget_priv->flg |= LEAPRAID_TGT_FLG_RAID_MEMBER; |
| sas_dev->volume_hdl = volume_hdl; |
| sas_dev->volume_wwid = volume_wwid; |
| } |
| spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); |
| if (starget) { |
| starget_for_each_device(starget, |
| (void *)LEAPRAID_NO_ULD_ATTACH_FLAG, |
| leapraid_reprobe_lun); |
| } |
| |
| leapraid_sdev_put(sas_dev); |
| } |
| |
| static void leapraid_sas_pd_expose( |
| struct leapraid_adapter *adapter, |
| struct leapraid_evt_data_ir_change *evt_data) |
| { |
| struct leapraid_starget_priv *starget_priv; |
| struct scsi_target *starget = NULL; |
| struct leapraid_sas_dev *sas_dev; |
| unsigned long flags; |
| u16 hdl; |
| |
| hdl = le16_to_cpu(evt_data->phys_disk_dev_hdl); |
| if (!hdl || hdl > adapter->adapter_attr.features.max_dev_handle) { |
| dev_warn(&adapter->pdev->dev, |
| "%s: Invalid device handle\n", __func__); |
| return; |
| } |
| |
| spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); |
| sas_dev = leapraid_hold_lock_get_sas_dev_by_hdl(adapter, hdl); |
| if (!sas_dev) { |
| dev_warn(&adapter->pdev->dev, |
| "%s:%d: sas_dev not found, hdl=0x%x\n", |
| __func__, __LINE__, hdl); |
| spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); |
| return; |
| } |
| |
| sas_dev->volume_hdl = 0; |
| sas_dev->volume_wwid = 0; |
| clear_bit(hdl, adapter->dev_topo.pd_hdls); |
| if (sas_dev->starget && sas_dev->starget->hostdata) { |
| starget = sas_dev->starget; |
| starget_priv = starget->hostdata; |
| starget_priv->flg &= ~LEAPRAID_TGT_FLG_RAID_MEMBER; |
| sas_dev->led_on = 0; |
| } |
| spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); |
| |
| if (starget) { |
| starget_for_each_device(starget, |
| NULL, |
| leapraid_reprobe_lun); |
| } |
| |
| leapraid_sdev_put(sas_dev); |
| } |
| |
| static void leapraid_sas_vol_visibility( |
| struct leapraid_adapter *adapter, |
| struct leapraid_evt_data_ir_change *evt_data) |
| { |
| struct leapraid_raid_volume *raid_volume; |
| struct scsi_device *sdev; |
| bool reprobe_flg = false; |
| bool sdev_held = false; |
| unsigned long flags; |
| u16 hdl; |
| u8 rc; |
| |
| hdl = le16_to_cpu(evt_data->vol_dev_hdl); |
| rc = evt_data->reason_code; |
| raid_volume = leapraid_raid_volume_find_by_hdl(adapter, hdl); |
| if (!raid_volume) { |
| dev_warn(&adapter->pdev->dev, |
| "%s:%d: Volume handle 0x%x not found\n", |
| __func__, __LINE__, hdl); |
| return; |
| } |
| |
| spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); |
| sdev = raid_volume->sdev; |
| if (!sdev) { |
| spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, |
| flags); |
| leapraid_raid_volume_put(raid_volume); |
| dev_warn(&adapter->pdev->dev, |
| "%s:%d: Volume handle 0x%x has no sdev\n", |
| __func__, __LINE__, hdl); |
| return; |
| } |
| |
| if (sdev->no_uld_attach && |
| rc == LEAPRAID_EVT_IR_RC_VOLUME_UNHIDE) { |
| sdev->no_uld_attach = 0; |
| reprobe_flg = true; |
| } else if (!sdev->no_uld_attach && |
| rc == LEAPRAID_EVT_IR_RC_VOLUME_HIDE) { |
| sdev->no_uld_attach = 1; |
| reprobe_flg = true; |
| } |
| |
| if (reprobe_flg && !scsi_device_get(sdev)) |
| sdev_held = true; |
| spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags); |
| leapraid_raid_volume_put(raid_volume); |
| |
| if (!reprobe_flg) { |
| dev_warn(&adapter->pdev->dev, |
| "%s:rc(0x%x): Request matches, skipping\n", |
| __func__, rc); |
| return; |
| } |
| |
| if (!sdev_held) { |
| dev_warn(&adapter->pdev->dev, |
| "%s: Failed to hold sdev for reprobe, hdl=0x%x\n", |
| __func__, hdl); |
| return; |
| } |
| |
| if (sdev->no_uld_attach) |
| sdev_printk(KERN_INFO, sdev, "hide vol\n"); |
| else |
| sdev_printk(KERN_INFO, sdev, "unhide vol\n"); |
| |
| WARN_ON(scsi_device_reprobe(sdev)); |
| scsi_device_put(sdev); |
| } |
| |
| static void leapraid_sas_volume_add( |
| struct leapraid_adapter *adapter, |
| struct leapraid_evt_data_ir_change *evt_data) |
| { |
| struct leapraid_raid_volume *raid_volume; |
| unsigned long flags; |
| u64 wwid; |
| u16 hdl; |
| |
| hdl = le16_to_cpu(evt_data->vol_dev_hdl); |
| |
| if (leapraid_cfg_get_volume_wwid(adapter, hdl, &wwid)) { |
| dev_warn(&adapter->pdev->dev, "Failed to read volume page1\n"); |
| return; |
| } |
| |
| if (!wwid) { |
| dev_warn(&adapter->pdev->dev, "Invalid WWID(handle=0x%x)\n", |
| hdl); |
| return; |
| } |
| |
| raid_volume = leapraid_raid_volume_find_by_wwid(adapter, wwid); |
| if (raid_volume) { |
| dev_warn(&adapter->pdev->dev, |
| "Volume handle 0x%x already exists\n", hdl); |
| leapraid_raid_volume_put(raid_volume); |
| return; |
| } |
| |
| raid_volume = kzalloc(sizeof(*raid_volume), GFP_KERNEL); |
| if (!raid_volume) |
| return; |
| |
| INIT_LIST_HEAD(&raid_volume->list); |
| kref_init(&raid_volume->refcnt); |
| raid_volume->id = adapter->dev_topo.sas_id++; |
| raid_volume->channel = RAID_CHANNEL; |
| raid_volume->hdl = hdl; |
| raid_volume->wwid = wwid; |
| leapraid_raid_volume_add(adapter, raid_volume); |
| if (!adapter->scan_dev_desc.wait_scan_dev_done) { |
| if (scsi_add_device(adapter->shost, RAID_CHANNEL, |
| raid_volume->id, 0)) |
| leapraid_raid_volume_remove(adapter, raid_volume); |
| dev_info(&adapter->pdev->dev, |
| "add RAID volume: hdl=0x%x, wwid=0x%llx\n", hdl, wwid); |
| } else { |
| spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); |
| leapraid_check_boot_dev(adapter, raid_volume, RAID_CHANNEL); |
| spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, |
| flags); |
| } |
| leapraid_raid_volume_put(raid_volume); |
| } |
| |
| static void leapraid_sas_volume_delete_by_ptr( |
| struct leapraid_adapter *adapter, |
| struct leapraid_raid_volume *raid_volume) |
| { |
| struct leapraid_starget_priv *starget_priv; |
| struct scsi_target *starget = NULL; |
| unsigned long flags; |
| bool in_list; |
| |
| if (!raid_volume) |
| return; |
| |
| spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); |
| in_list = !list_empty(&raid_volume->list); |
| if (in_list && raid_volume->starget) { |
| starget = raid_volume->starget; |
| starget_priv = starget->hostdata; |
| if (starget_priv) |
| starget_priv->deleted = 1; |
| } |
| spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags); |
| |
| if (!in_list) |
| return; |
| |
| dev_info(&adapter->pdev->dev, |
| "delete RAID volume: hdl=0x%x, wwid=0x%llx\n", |
| raid_volume->hdl, raid_volume->wwid); |
| leapraid_raid_volume_remove(adapter, raid_volume); |
| |
| if (starget) |
| scsi_remove_target(&starget->dev); |
| } |
| |
| static void leapraid_sas_volume_delete(struct leapraid_adapter *adapter, |
| u16 hdl) |
| { |
| struct leapraid_raid_volume *raid_volume; |
| |
| raid_volume = leapraid_raid_volume_find_by_hdl(adapter, hdl); |
| if (!raid_volume) { |
| dev_warn(&adapter->pdev->dev, |
| "%s:%d: Volume handle 0x%x not found\n", |
| __func__, __LINE__, hdl); |
| return; |
| } |
| |
| leapraid_sas_volume_delete_by_ptr(adapter, raid_volume); |
| leapraid_raid_volume_put(raid_volume); |
| } |
| |
| static void leapraid_sas_ir_chg_evt(struct leapraid_adapter *adapter, |
| struct leapraid_fw_evt_work *fw_evt) |
| { |
| struct leapraid_evt_data_ir_change *evt_data; |
| |
| evt_data = fw_evt->evt_data; |
| |
| switch (evt_data->reason_code) { |
| case LEAPRAID_EVT_IR_RC_VOLUME_ADD: |
| leapraid_sas_volume_add(adapter, evt_data); |
| break; |
| case LEAPRAID_EVT_IR_RC_VOLUME_DELETE: |
| leapraid_sas_volume_delete(adapter, |
| le16_to_cpu(evt_data->vol_dev_hdl)); |
| break; |
| case LEAPRAID_EVT_IR_RC_PD_HIDDEN_TO_ADD: |
| leapraid_sas_pd_add(adapter, evt_data); |
| break; |
| case LEAPRAID_EVT_IR_RC_PD_UNHIDDEN_TO_DELETE: |
| leapraid_sas_pd_delete(adapter, evt_data); |
| break; |
| case LEAPRAID_EVT_IR_RC_PD_CREATED_TO_HIDE: |
| leapraid_sas_pd_hide(adapter, evt_data); |
| break; |
| case LEAPRAID_EVT_IR_RC_PD_DELETED_TO_EXPOSE: |
| leapraid_sas_pd_expose(adapter, evt_data); |
| break; |
| case LEAPRAID_EVT_IR_RC_VOLUME_HIDE: |
| case LEAPRAID_EVT_IR_RC_VOLUME_UNHIDE: |
| leapraid_sas_vol_visibility(adapter, evt_data); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| static void leapraid_sas_enc_dev_stat_add_node( |
| struct leapraid_adapter *adapter, u16 hdl) |
| { |
| union cfg_param_1 cfgp1 = {0}; |
| union cfg_param_2 cfgp2 = {0}; |
| struct leapraid_enc_node *enc_node; |
| struct leapraid_enc_node *enc_exist; |
| unsigned long flags; |
| int rc; |
| |
| enc_node = kzalloc_obj(*enc_node); |
| if (!enc_node) |
| return; |
| |
| cfgp1.form = LEAPRAID_SAS_ENC_CFG_PGAD_HDL; |
| cfgp2.handle = hdl; |
| rc = leapraid_op_config_page(adapter, &enc_node->pg0, cfgp1, cfgp2, |
| GET_SAS_ENCLOSURE_PG0); |
| if (rc) { |
| kfree(enc_node); |
| return; |
| } |
| |
| spin_lock_irqsave(&adapter->dev_topo.enc_lock, flags); |
| enc_exist = leapraid_enc_find_by_hdl(adapter, hdl); |
| if (enc_exist) { |
| spin_unlock_irqrestore(&adapter->dev_topo.enc_lock, flags); |
| kfree(enc_node); |
| return; |
| } |
| list_add_tail(&enc_node->list, &adapter->dev_topo.enc_list); |
| spin_unlock_irqrestore(&adapter->dev_topo.enc_lock, flags); |
| } |
| |
| static void leapraid_sas_enc_dev_stat_del_node( |
| struct leapraid_adapter *adapter, u16 hdl) |
| { |
| struct leapraid_enc_node *enc_node; |
| unsigned long flags; |
| |
| if (!hdl) |
| return; |
| |
| spin_lock_irqsave(&adapter->dev_topo.enc_lock, flags); |
| enc_node = leapraid_enc_find_by_hdl(adapter, hdl); |
| if (enc_node) |
| list_del(&enc_node->list); |
| spin_unlock_irqrestore(&adapter->dev_topo.enc_lock, flags); |
| |
| kfree(enc_node); |
| } |
| |
| static void leapraid_sas_enc_dev_stat_chg_evt( |
| struct leapraid_adapter *adapter, |
| struct leapraid_fw_evt_work *fw_evt) |
| { |
| struct leapraid_evt_data_sas_enc_dev_status_change *evt_data; |
| u16 enc_hdl; |
| |
| if (adapter->access_ctrl.shost_recovering) |
| return; |
| |
| evt_data = fw_evt->evt_data; |
| enc_hdl = le16_to_cpu(evt_data->enc_hdl); |
| switch (evt_data->reason_code) { |
| case LEAPRAID_EVT_SAS_ENCL_RC_ADDED: |
| if (enc_hdl) |
| leapraid_sas_enc_dev_stat_add_node(adapter, enc_hdl); |
| break; |
| case LEAPRAID_EVT_SAS_ENCL_RC_NOT_RESPONDING: |
| leapraid_sas_enc_dev_stat_del_node(adapter, enc_hdl); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| static void leapraid_remove_unresp_sas_end_dev( |
| struct leapraid_adapter *adapter) |
| { |
| struct leapraid_sas_dev *sas_dev, *sas_dev_next; |
| unsigned long flags; |
| LIST_HEAD(head); |
| |
| spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); |
| list_for_each_entry_safe(sas_dev, sas_dev_next, |
| &adapter->dev_topo.sas_dev_init_list, list) { |
| if (sas_dev->rphy || sas_dev->pend_sas_rphy_add) |
| continue; |
| |
| list_del_init(&sas_dev->list); |
| leapraid_clear_cached_boot_dev(adapter, sas_dev, 0); |
| leapraid_sdev_put(sas_dev); |
| } |
| list_for_each_entry_safe(sas_dev, sas_dev_next, |
| &adapter->dev_topo.sas_dev_list, list) { |
| if (!sas_dev->resp) |
| list_move_tail(&sas_dev->list, &head); |
| else |
| sas_dev->resp = 0; |
| } |
| spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); |
| |
| list_for_each_entry_safe(sas_dev, sas_dev_next, &head, list) { |
| leapraid_remove_device(adapter, sas_dev); |
| list_del_init(&sas_dev->list); |
| leapraid_sdev_put(sas_dev); |
| } |
| |
| dev_warn(&adapter->pdev->dev, |
| "Unresponsive SAS end devices removed\n"); |
| } |
| |
| static void leapraid_remove_unresp_raid_volumes( |
| struct leapraid_adapter *adapter) |
| { |
| unsigned long flags; |
| struct leapraid_raid_volume *raid_volume, *raid_volume_next; |
| LIST_HEAD(head); |
| |
| spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); |
| list_for_each_entry_safe(raid_volume, raid_volume_next, |
| &adapter->dev_topo.raid_volume_list, list) { |
| if (!raid_volume->resp) |
| list_move_tail(&raid_volume->list, &head); |
| else |
| raid_volume->resp = 0; |
| } |
| spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags); |
| |
| list_for_each_entry_safe(raid_volume, raid_volume_next, &head, list) { |
| leapraid_sas_volume_delete_by_ptr(adapter, raid_volume); |
| } |
| |
| dev_warn(&adapter->pdev->dev, |
| "Unresponsive RAID volumes removed\n"); |
| } |
| |
| static void leapraid_remove_unresp_sas_exp(struct leapraid_adapter *adapter) |
| { |
| struct leapraid_topo_node *topo_node_exp, *topo_node_exp_next; |
| unsigned long flags; |
| LIST_HEAD(head); |
| |
| spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags); |
| list_for_each_entry_safe(topo_node_exp, topo_node_exp_next, |
| &adapter->dev_topo.exp_list, list) { |
| if (!topo_node_exp->resp) |
| list_move_tail(&topo_node_exp->list, &head); |
| else |
| topo_node_exp->resp = 0; |
| } |
| spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags); |
| |
| list_for_each_entry_safe(topo_node_exp, topo_node_exp_next, |
| &head, list) |
| leapraid_exp_node_rm(adapter, topo_node_exp); |
| |
| dev_warn(&adapter->pdev->dev, |
| "Unresponsive SAS expanders removed\n"); |
| } |
| |
| static void leapraid_remove_unresp_dev(struct leapraid_adapter *adapter) |
| { |
| leapraid_remove_unresp_sas_end_dev(adapter); |
| if (adapter->adapter_attr.raid_support) |
| leapraid_remove_unresp_raid_volumes(adapter); |
| leapraid_remove_unresp_sas_exp(adapter); |
| leapraid_ublk_io_all_dev(adapter); |
| } |
| |
| static void leapraid_del_dirty_vphy(struct leapraid_adapter *adapter) |
| { |
| struct leapraid_card_port *card_port, *card_port_next; |
| struct leapraid_vphy *vphy, *vphy_next; |
| |
| list_for_each_entry_safe(card_port, card_port_next, |
| &adapter->dev_topo.card_port_list, list) { |
| if (!card_port->vphys_mask) |
| continue; |
| |
| list_for_each_entry_safe(vphy, vphy_next, |
| &card_port->vphys_list, list) { |
| if (!(vphy->flg & LEAPRAID_VPHY_FLG_DIRTY)) |
| continue; |
| |
| card_port->vphys_mask &= ~vphy->phy_mask; |
| list_del(&vphy->list); |
| kfree(vphy); |
| } |
| |
| if (!card_port->vphys_mask && !card_port->sas_address) |
| card_port->flg |= LEAPRAID_CARD_PORT_FLG_DIRTY; |
| } |
| } |
| |
| static void leapraid_del_dirty_card_port(struct leapraid_adapter *adapter) |
| { |
| struct leapraid_card_port *card_port, *card_port_next; |
| |
| list_for_each_entry_safe(card_port, card_port_next, |
| &adapter->dev_topo.card_port_list, list) { |
| if (!(card_port->flg & LEAPRAID_CARD_PORT_FLG_DIRTY) || |
| card_port->flg & LEAPRAID_CARD_PORT_FLG_NEW) |
| continue; |
| |
| list_del(&card_port->list); |
| kfree(card_port); |
| } |
| } |
| |
| static void leapraid_update_dev_qdepth(struct leapraid_adapter *adapter) |
| { |
| struct leapraid_sdev_priv *sdev_priv; |
| struct leapraid_sas_dev *sas_dev; |
| struct leapraid_adapter_attr *attr; |
| struct scsi_device *sdev; |
| u16 qdepth; |
| |
| attr = &adapter->adapter_attr; |
| shost_for_each_device(sdev, adapter->shost) { |
| sdev_priv = sdev->hostdata; |
| if (!sdev_priv || !sdev_priv->starget_priv) |
| continue; |
| sas_dev = sdev_priv->starget_priv->sas_dev; |
| if (sas_dev && sas_dev->dev_info & LEAPRAID_DEVTYP_SSP_TGT) |
| qdepth = (sas_dev->port_connection > 1) ? |
| attr->wideport_max_queue_depth : |
| attr->narrowport_max_queue_depth; |
| else if (sas_dev && sas_dev->dev_info & |
| LEAPRAID_DEVTYP_SATA_DEV) |
| qdepth = attr->sata_max_queue_depth; |
| else |
| continue; |
| |
| leapraid_change_queue_depth(sdev, qdepth); |
| } |
| } |
| |
| static void leapraid_update_exp_links(struct leapraid_adapter *adapter, |
| struct leapraid_topo_node *topo_node_exp, |
| u16 hdl) |
| { |
| union cfg_param_1 cfgp1 = {0}; |
| union cfg_param_2 cfgp2 = {0}; |
| struct leapraid_exp_p1 exp_p1; |
| int i; |
| |
| cfgp2.handle = hdl; |
| for (i = 0; i < topo_node_exp->phys_num; i++) { |
| cfgp1.phy_number = i; |
| if (leapraid_op_config_page(adapter, &exp_p1, cfgp1, cfgp2, |
| GET_SAS_EXPANDER_PG1)) |
| return; |
| |
| leapraid_transport_update_links( |
| adapter, |
| topo_node_exp->sas_address, |
| le16_to_cpu(exp_p1.attached_dev_hdl), |
| i, |
| exp_p1.neg_link_rate >> |
| LEAPRAID_SAS_NEG_LINK_RATE_SHIFT, |
| topo_node_exp->card_port); |
| } |
| } |
| |
| static void leapraid_scan_exp_after_reset(struct leapraid_adapter *adapter) |
| { |
| union cfg_param_1 cfgp1 = {0}; |
| union cfg_param_2 cfgp2 = {0}; |
| struct leapraid_topo_node *topo_node_exp; |
| struct leapraid_exp_p0 exp_p0; |
| unsigned long flags; |
| u16 hdl; |
| u8 port_id; |
| |
| cfgp1.form = LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP; |
| for (hdl = 0xFFFF, cfgp2.handle = hdl; |
| !leapraid_op_config_page(adapter, &exp_p0, cfgp1, cfgp2, |
| GET_SAS_EXPANDER_PG0); |
| cfgp2.handle = hdl) { |
| hdl = le16_to_cpu(exp_p0.dev_hdl); |
| port_id = exp_p0.physical_port; |
| spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags); |
| topo_node_exp = |
| leapraid_exp_find_by_sas_address( |
| adapter, |
| le64_to_cpu(exp_p0.sas_address), |
| leapraid_get_port_by_id(adapter, |
| port_id, |
| false)); |
| spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, |
| flags); |
| |
| if (topo_node_exp) { |
| leapraid_update_exp_links(adapter, topo_node_exp, hdl); |
| } else { |
| leapraid_exp_add(adapter, hdl); |
| |
| dev_info(&adapter->pdev->dev, |
| "add exp: hdl=0x%04x, SAS addr=0x%016llx\n", |
| hdl, |
| (unsigned long long)le64_to_cpu( |
| exp_p0.sas_address)); |
| } |
| } |
| } |
| |
| static void leapraid_scan_phy_disks_after_reset( |
| struct leapraid_adapter *adapter) |
| { |
| union cfg_param_1 cfgp1 = {0}; |
| union cfg_param_2 cfgp2 = {0}; |
| union cfg_param_1 cfgp1_extra = {0}; |
| union cfg_param_2 cfgp2_extra = {0}; |
| struct leapraid_sas_dev_p0 sas_dev_p0; |
| struct leapraid_raidpd_p0 raidpd_p0; |
| struct leapraid_sas_dev *sas_dev; |
| u8 phys_disk_num, port_id; |
| u16 hdl, parent_hdl; |
| u64 sas_addr; |
| |
| cfgp1.form = LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP; |
| for (phys_disk_num = 0xFF, cfgp2.form_specific = phys_disk_num; |
| !leapraid_op_config_page(adapter, &raidpd_p0, |
| cfgp1, cfgp2, GET_PHY_DISK_PG0); |
| cfgp2.form_specific = phys_disk_num) { |
| phys_disk_num = raidpd_p0.phys_disk_num; |
| hdl = le16_to_cpu(raidpd_p0.dev_hdl); |
| sas_dev = leapraid_get_sas_dev_by_hdl(adapter, hdl); |
| if (sas_dev) { |
| leapraid_sdev_put(sas_dev); |
| continue; |
| } |
| |
| cfgp1_extra.form = LEAPRAID_SAS_DEV_CFG_PGAD_HDL; |
| cfgp2_extra.handle = hdl; |
| if (leapraid_op_config_page(adapter, &sas_dev_p0, cfgp1_extra, |
| cfgp2_extra, GET_SAS_DEVICE_PG0) != |
| 0) |
| continue; |
| |
| parent_hdl = le16_to_cpu(sas_dev_p0.parent_dev_hdl); |
| if (!leapraid_get_sas_address(adapter, |
| parent_hdl, |
| &sas_addr)) { |
| port_id = sas_dev_p0.physical_port; |
| leapraid_transport_update_links( |
| adapter, sas_addr, hdl, |
| sas_dev_p0.phy_num, |
| LEAPRAID_SAS_NEG_LINK_RATE_1_5, |
| leapraid_get_port_by_id( |
| adapter, port_id, false)); |
| if (!hdl || hdl > |
| adapter->adapter_attr.features.max_dev_handle) { |
| dev_warn(&adapter->pdev->dev, |
| "%s: Invalid device handle\n", |
| __func__); |
| } else { |
| set_bit(hdl, adapter->dev_topo.pd_hdls); |
| leapraid_add_dev(adapter, hdl); |
| |
| dev_info(&adapter->pdev->dev, |
| "add pd hdl=0x%04x saddr=0x%016llx\n", |
| hdl, |
| (unsigned long long)le64_to_cpu( |
| sas_dev_p0.sas_address)); |
| } |
| } |
| } |
| } |
| |
| static void leapraid_scan_vol_after_reset(struct leapraid_adapter *adapter) |
| { |
| union cfg_param_1 cfgp1 = {0}; |
| union cfg_param_2 cfgp2 = {0}; |
| union cfg_param_1 cfgp1_extra = {0}; |
| union cfg_param_2 cfgp2_extra = {0}; |
| struct leapraid_evt_data_ir_change evt_data; |
| struct leapraid_raid_volume *raid_volume; |
| struct leapraid_raidvol_p1 *vol_p1; |
| struct leapraid_raidvol_p0 *vol_p0; |
| u16 hdl; |
| |
| vol_p0 = kzalloc_obj(*vol_p0); |
| if (!vol_p0) |
| return; |
| |
| vol_p1 = kzalloc_obj(*vol_p1); |
| if (!vol_p1) { |
| kfree(vol_p0); |
| return; |
| } |
| |
| cfgp1.form = LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP; |
| for (hdl = 0xFFFF, cfgp2.handle = hdl; |
| !leapraid_op_config_page(adapter, vol_p1, cfgp1, |
| cfgp2, GET_RAID_VOLUME_PG1); |
| cfgp2.handle = hdl) { |
| hdl = le16_to_cpu(vol_p1->dev_hdl); |
| raid_volume = leapraid_raid_volume_find_by_wwid( |
| adapter, |
| le64_to_cpu(vol_p1->wwid)); |
| if (raid_volume) { |
| leapraid_raid_volume_put(raid_volume); |
| continue; |
| } |
| |
| cfgp1_extra.size = sizeof(struct leapraid_raidvol_p0); |
| cfgp2_extra.handle = hdl; |
| if (leapraid_op_config_page(adapter, vol_p0, cfgp1_extra, |
| cfgp2_extra, GET_RAID_VOLUME_PG0)) |
| continue; |
| |
| if (vol_p0->volume_state == LEAPRAID_VOL_STATE_OPTIMAL || |
| vol_p0->volume_state == LEAPRAID_VOL_STATE_ONLINE || |
| vol_p0->volume_state == LEAPRAID_VOL_STATE_DEGRADED) { |
| memset(&evt_data, 0, |
| sizeof(struct leapraid_evt_data_ir_change)); |
| evt_data.reason_code = LEAPRAID_EVT_IR_RC_VOLUME_ADD; |
| evt_data.vol_dev_hdl = vol_p1->dev_hdl; |
| leapraid_sas_volume_add(adapter, &evt_data); |
| dev_info(&adapter->pdev->dev, |
| "add volume: hdl=0x%04x\n", |
| vol_p1->dev_hdl); |
| } |
| } |
| |
| kfree(vol_p0); |
| kfree(vol_p1); |
| } |
| |
| static void leapraid_scan_sas_dev_after_reset(struct leapraid_adapter *adapter) |
| { |
| union cfg_param_1 cfgp1 = {0}; |
| union cfg_param_2 cfgp2 = {0}; |
| struct leapraid_sas_dev_p0 sas_dev_p0; |
| struct leapraid_sas_dev *sas_dev; |
| u16 hdl, parent_hdl; |
| u64 sas_address; |
| u8 port_id; |
| |
| cfgp1.form = LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP; |
| for (hdl = 0xFFFF, cfgp2.handle = hdl; |
| !leapraid_op_config_page(adapter, &sas_dev_p0, cfgp1, cfgp2, |
| GET_SAS_DEVICE_PG0); |
| cfgp2.handle = hdl) { |
| hdl = le16_to_cpu(sas_dev_p0.dev_hdl); |
| if (!hdl || |
| hdl > adapter->adapter_attr.features.max_dev_handle) { |
| dev_warn(&adapter->pdev->dev, |
| "%s: Invalid device handle\n", __func__); |
| continue; |
| } |
| |
| if (!(leapraid_is_end_dev(le32_to_cpu(sas_dev_p0.dev_info)))) |
| continue; |
| |
| port_id = sas_dev_p0.physical_port; |
| sas_dev = leapraid_get_sas_dev_by_addr( |
| adapter, |
| le64_to_cpu(sas_dev_p0.sas_address), |
| leapraid_get_port_by_id( |
| adapter, |
| port_id, |
| false)); |
| if (sas_dev) { |
| leapraid_sdev_put(sas_dev); |
| continue; |
| } |
| |
| parent_hdl = le16_to_cpu(sas_dev_p0.parent_dev_hdl); |
| if (!leapraid_get_sas_address(adapter, parent_hdl, |
| &sas_address)) { |
| leapraid_transport_update_links( |
| adapter, |
| sas_address, |
| hdl, |
| sas_dev_p0.phy_num, |
| LEAPRAID_SAS_NEG_LINK_RATE_1_5, |
| leapraid_get_port_by_id(adapter, |
| port_id, |
| false)); |
| leapraid_add_dev(adapter, hdl); |
| dev_info(&adapter->pdev->dev, |
| "Add SAS dev: hdl=0x%04x, saddr=0x%016llx\n", |
| hdl, |
| (unsigned long long)le64_to_cpu( |
| sas_dev_p0.sas_address)); |
| } |
| } |
| } |
| |
| static void leapraid_scan_all_dev_after_reset(struct leapraid_adapter *adapter) |
| { |
| leapraid_sas_host_add(adapter, adapter->dev_topo.card.phys_num > 0); |
| leapraid_scan_exp_after_reset(adapter); |
| if (adapter->adapter_attr.raid_support) { |
| leapraid_scan_phy_disks_after_reset(adapter); |
| leapraid_scan_vol_after_reset(adapter); |
| } |
| leapraid_scan_sas_dev_after_reset(adapter); |
| } |
| |
| static void leapraid_hardreset_async_logic(struct leapraid_adapter *adapter) |
| { |
| unsigned long flags; |
| |
| leapraid_remove_unresp_dev(adapter); |
| leapraid_del_dirty_vphy(adapter); |
| leapraid_del_dirty_card_port(adapter); |
| leapraid_update_dev_qdepth(adapter); |
| leapraid_scan_all_dev_after_reset(adapter); |
| |
| if (adapter->scan_dev_desc.driver_loading) |
| leapraid_scan_dev_done(adapter); |
| spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags); |
| adapter->access_ctrl.shost_recover_async = 0; |
| spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags); |
| wake_up(&adapter->access_ctrl.shost_recover_wq); |
| } |
| |
| static int leapraid_send_enc_cmd(struct leapraid_adapter *adapter, |
| struct leapraid_sep_rep *sep_rep, |
| struct leapraid_sep_req *sep_req) |
| { |
| void *req; |
| bool reset_flg = false; |
| int rc; |
| u16 smid; |
| |
| mutex_lock(&adapter->driver_cmds.enc_cmd.mutex); |
| rc = leapraid_check_adapter_is_op(adapter, LEAPRAID_DB_WAIT_OP_SHORT, |
| __func__); |
| if (rc) |
| goto unlock; |
| |
| adapter->driver_cmds.enc_cmd.status = LEAPRAID_CMD_PENDING; |
| smid = adapter->driver_cmds.enc_cmd.inter_taskid; |
| req = leapraid_get_task_desc(adapter, smid); |
| memset(req, 0, LEAPRAID_REQUEST_SIZE); |
| memcpy(req, sep_req, sizeof(struct leapraid_sep_req)); |
| init_completion(&adapter->driver_cmds.enc_cmd.done); |
| leapraid_fire_task(adapter, smid); |
| wait_for_completion_timeout(&adapter->driver_cmds.enc_cmd.done, |
| LEAPRAID_ENC_CMD_TIMEOUT * HZ); |
| if (!(adapter->driver_cmds.enc_cmd.status & LEAPRAID_CMD_DONE)) { |
| dev_err(&adapter->pdev->dev, |
| "%s: SEP command timeout, status=0x%x\n", |
| __func__, adapter->driver_cmds.enc_cmd.status); |
| leapraid_log_req_context(adapter, smid, sep_req); |
| reset_flg = |
| leapraid_check_reset( |
| adapter->driver_cmds.enc_cmd.status); |
| rc = -EFAULT; |
| goto do_hard_reset; |
| } |
| |
| if (adapter->driver_cmds.enc_cmd.status & LEAPRAID_CMD_REPLY_VALID) |
| memcpy(sep_rep, &adapter->driver_cmds.enc_cmd.reply, |
| sizeof(struct leapraid_sep_rep)); |
| do_hard_reset: |
| if (reset_flg) { |
| dev_info(&adapter->pdev->dev, "%s:%d: call hard_reset\n", |
| __func__, __LINE__); |
| leapraid_hard_reset_handler(adapter, FULL_RESET); |
| } |
| |
| adapter->driver_cmds.enc_cmd.status = LEAPRAID_CMD_NOT_USED; |
| unlock: |
| mutex_unlock(&adapter->driver_cmds.enc_cmd.mutex); |
| return rc; |
| } |
| |
| static void leapraid_set_led(struct leapraid_adapter *adapter, |
| struct leapraid_sas_dev *sas_dev, bool on) |
| { |
| struct leapraid_sep_rep sep_rep; |
| struct leapraid_sep_req sep_req; |
| |
| if (!sas_dev) |
| return; |
| |
| memset(&sep_req, 0, sizeof(struct leapraid_sep_req)); |
| memset(&sep_rep, 0, sizeof(struct leapraid_sep_rep)); |
| sep_req.func = LEAPRAID_FUNC_SCSI_ENC_PROCESSOR; |
| sep_req.act = LEAPRAID_SEP_REQ_ACT_WRITE_STATUS; |
| if (on) { |
| u32 status = LEAPRAID_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT; |
| |
| sep_req.slot_status = cpu_to_le32(status); |
| sep_req.dev_hdl = cpu_to_le16(sas_dev->hdl); |
| sep_req.flg = LEAPRAID_SEP_REQ_FLG_DEVHDL_ADDRESS; |
| if (leapraid_send_enc_cmd(adapter, &sep_rep, &sep_req)) { |
| leapraid_sdev_put(sas_dev); |
| return; |
| } |
| |
| sas_dev->led_on = 1; |
| leapraid_sdev_put(sas_dev); |
| } else { |
| sep_req.slot_status = 0; |
| sep_req.slot = cpu_to_le16(sas_dev->slot); |
| sep_req.dev_hdl = 0; |
| sep_req.enc_hdl = cpu_to_le16(sas_dev->enc_hdl); |
| sep_req.flg = LEAPRAID_SEP_REQ_FLG_ENCLOSURE_SLOT_ADDRESS; |
| leapraid_send_enc_cmd(adapter, &sep_rep, &sep_req); |
| } |
| } |
| |
| static int leapraid_wait_adapter_recovery(struct leapraid_adapter *adapter) |
| { |
| unsigned long flags; |
| |
| while (leapraid_shost_in_recovery(adapter->shost) || |
| READ_ONCE(adapter->access_ctrl.shost_recovering)) { |
| if (READ_ONCE(adapter->access_ctrl.host_removing) || |
| READ_ONCE(adapter->fw_evt_s.fw_evt_cleanup)) { |
| spin_lock_irqsave( |
| &adapter->reset_desc.adapter_reset_lock, |
| flags); |
| adapter->access_ctrl.shost_recover_async = 0; |
| spin_unlock_irqrestore( |
| &adapter->reset_desc.adapter_reset_lock, |
| flags); |
| |
| wake_up(&adapter->access_ctrl.shost_recover_wq); |
| |
| dev_warn(&adapter->pdev->dev, |
| "%s: Failed, shost %d, host %d\n", |
| __func__, |
| adapter->access_ctrl.shost_recovering, |
| adapter->access_ctrl.host_removing); |
| |
| return -EFAULT; |
| } |
| |
| wait_event_timeout( |
| adapter->access_ctrl.recovery_waitq, |
| (!leapraid_shost_in_recovery(adapter->shost) && |
| !READ_ONCE(adapter->access_ctrl.shost_recovering)), |
| msecs_to_jiffies(1000)); |
| } |
| |
| return 0; |
| } |
| |
| static void leapraid_fw_work(struct leapraid_adapter *adapter, |
| struct leapraid_fw_evt_work *fw_evt) |
| { |
| struct leapraid_sas_dev *sas_dev; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags); |
| adapter->fw_evt_s.cur_evt = fw_evt; |
| adapter->fw_evt_s.cur_evt_task = current; |
| spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags); |
| leapraid_del_fw_evt_from_list(adapter, fw_evt); |
| if (adapter->access_ctrl.host_removing || |
| adapter->access_ctrl.pcie_recovering) { |
| spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags); |
| leapraid_fw_evt_put(fw_evt); |
| adapter->fw_evt_s.cur_evt = NULL; |
| adapter->fw_evt_s.cur_evt_task = NULL; |
| spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags); |
| return; |
| } |
| switch (fw_evt->evt_type) { |
| case LEAPRAID_EVT_SAS_TOPO_CHANGE_LIST: |
| leapraid_sas_topo_chg_evt(adapter, fw_evt); |
| break; |
| case LEAPRAID_EVT_IR_CHANGE: |
| leapraid_sas_ir_chg_evt(adapter, fw_evt); |
| break; |
| case LEAPRAID_EVT_SAS_ENCL_DEV_STATUS_CHANGE: |
| leapraid_sas_enc_dev_stat_chg_evt(adapter, fw_evt); |
| break; |
| case LEAPRAID_EVT_REMOVE_DEAD_DEV: |
| if (leapraid_wait_adapter_recovery(adapter)) |
| goto out_cleanup; |
| |
| leapraid_hardreset_async_logic(adapter); |
| break; |
| case LEAPRAID_EVT_TURN_ON_PFA_LED: |
| sas_dev = leapraid_get_sas_dev_by_hdl(adapter, |
| fw_evt->dev_handle); |
| leapraid_set_led(adapter, sas_dev, true); |
| break; |
| case LEAPRAID_EVT_SCAN_DEV_DONE: |
| adapter->scan_dev_desc.scan_start = 0; |
| break; |
| default: |
| break; |
| } |
| out_cleanup: |
| spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags); |
| leapraid_fw_evt_put(fw_evt); |
| adapter->fw_evt_s.cur_evt = NULL; |
| adapter->fw_evt_s.cur_evt_task = NULL; |
| spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags); |
| } |
| |
| static void leapraid_sas_dev_stat_chg_evt( |
| struct leapraid_adapter *adapter, |
| struct leapraid_evt_data_sas_dev_status_change *event_data) |
| { |
| struct leapraid_starget_priv *starget_priv; |
| struct leapraid_sas_dev *sas_dev; |
| u64 sas_address; |
| unsigned long flags; |
| |
| switch (event_data->reason_code) { |
| case LEAPRAID_EVT_SAS_DEV_STAT_RC_INTERNAL_DEV_RESET: |
| case LEAPRAID_EVT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET: |
| break; |
| default: |
| return; |
| } |
| |
| spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); |
| |
| sas_address = le64_to_cpu(event_data->sas_address); |
| sas_dev = leapraid_hold_lock_get_sas_dev_by_addr( |
| adapter, |
| sas_address, |
| leapraid_get_port_by_id(adapter, |
| event_data->physical_port, |
| false)); |
| if (!sas_dev || !sas_dev->starget) |
| goto out_unlock; |
| |
| starget_priv = sas_dev->starget->hostdata; |
| if (starget_priv) { |
| switch (event_data->reason_code) { |
| case LEAPRAID_EVT_SAS_DEV_STAT_RC_INTERNAL_DEV_RESET: |
| starget_priv->tm_busy = 1; |
| break; |
| case LEAPRAID_EVT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET: |
| starget_priv->tm_busy = 0; |
| break; |
| } |
| } |
| |
| out_unlock: |
| if (sas_dev) |
| leapraid_sdev_put(sas_dev); |
| spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); |
| } |
| |
| static void leapraid_set_volume_delete_flag(struct leapraid_adapter *adapter, |
| u16 handle) |
| { |
| struct leapraid_raid_volume *raid_volume; |
| struct leapraid_starget_priv *sas_target_priv_data; |
| unsigned long flags; |
| |
| raid_volume = leapraid_raid_volume_find_by_hdl(adapter, handle); |
| if (raid_volume) { |
| spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); |
| if (raid_volume->starget && |
| raid_volume->starget->hostdata) { |
| sas_target_priv_data = raid_volume->starget->hostdata; |
| sas_target_priv_data->deleted = 1; |
| } |
| spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, |
| flags); |
| leapraid_raid_volume_put(raid_volume); |
| } |
| } |
| |
| static void leapraid_check_ir_change_evt( |
| struct leapraid_adapter *adapter, |
| struct leapraid_evt_data_ir_change *evt_data) |
| { |
| u16 phys_disk_dev_hdl; |
| |
| switch (evt_data->reason_code) { |
| case LEAPRAID_EVT_IR_RC_VOLUME_DELETE: |
| leapraid_set_volume_delete_flag( |
| adapter, |
| le16_to_cpu(evt_data->vol_dev_hdl)); |
| break; |
| case LEAPRAID_EVT_IR_RC_PD_UNHIDDEN_TO_DELETE: |
| phys_disk_dev_hdl = |
| le16_to_cpu(evt_data->phys_disk_dev_hdl); |
| if (!phys_disk_dev_hdl || |
| phys_disk_dev_hdl > |
| adapter->adapter_attr.features.max_dev_handle) { |
| dev_warn(&adapter->pdev->dev, |
| "%s: Invalid device handle\n", __func__); |
| } else { |
| clear_bit(phys_disk_dev_hdl, |
| adapter->dev_topo.pd_hdls); |
| leapraid_tgt_rst_send(adapter, phys_disk_dev_hdl); |
| } |
| break; |
| } |
| } |
| |
| static void leapraid_topo_del_evts_process_exp_status( |
| struct leapraid_adapter *adapter, |
| struct leapraid_evt_data_sas_topo_change_list *evt_data) |
| { |
| struct leapraid_fw_evt_work *fw_evt = NULL; |
| struct leapraid_evt_data_sas_topo_change_list *loc_evt_data; |
| unsigned long flags; |
| u16 exp_hdl; |
| |
| exp_hdl = le16_to_cpu(evt_data->exp_dev_hdl); |
| |
| switch (evt_data->exp_status) { |
| case LEAPRAID_EVT_SAS_TOPO_ES_NOT_RESPONDING: |
| spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags); |
| list_for_each_entry(fw_evt, |
| &adapter->fw_evt_s.fw_evt_list, list) { |
| if (fw_evt->evt_type != |
| LEAPRAID_EVT_SAS_TOPO_CHANGE_LIST || |
| fw_evt->ignore) |
| continue; |
| |
| loc_evt_data = fw_evt->evt_data; |
| if ((loc_evt_data->exp_status == |
| LEAPRAID_EVT_SAS_TOPO_ES_ADDED || |
| loc_evt_data->exp_status == |
| LEAPRAID_EVT_SAS_TOPO_ES_RESPONDING) && |
| le16_to_cpu(loc_evt_data->exp_dev_hdl) == exp_hdl) |
| fw_evt->ignore = 1; |
| } |
| spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| static void leapraid_check_topo_del_evts( |
| struct leapraid_adapter *adapter, |
| struct leapraid_evt_data_sas_topo_change_list *evt_data) |
| { |
| int reason_code; |
| u16 hdl; |
| int i; |
| |
| for (i = 0; i < evt_data->entry_num; i++) { |
| hdl = le16_to_cpu(evt_data->phy[i].attached_dev_hdl); |
| if (!hdl) |
| continue; |
| |
| reason_code = evt_data->phy[i].phy_status & |
| LEAPRAID_EVT_SAS_TOPO_RC_MASK; |
| if (reason_code == |
| LEAPRAID_EVT_SAS_TOPO_RC_TARG_NOT_RESPONDING) |
| leapraid_tgt_not_responding(adapter, hdl); |
| } |
| leapraid_topo_del_evts_process_exp_status(adapter, evt_data); |
| } |
| |
| static bool leapraid_async_evt_validate( |
| struct leapraid_adapter *adapter, |
| struct leapraid_evt_notify_rep *event_notify_rep) |
| { |
| size_t msg_len; |
| size_t evt_sz; |
| size_t evt_avail; |
| u16 evt; |
| |
| msg_len = event_notify_rep->msg_len * sizeof(u32); |
| if (msg_len > LEAPRAID_REPLY_SIZE || |
| msg_len < offsetof(struct leapraid_evt_notify_rep, evt_data)) { |
| dev_warn(&adapter->pdev->dev, |
| "%s: Invalid async event msg_len=%zu\n", |
| __func__, msg_len); |
| return false; |
| } |
| |
| evt_sz = le16_to_cpu(event_notify_rep->evt_data_len) * sizeof(u32); |
| evt_avail = msg_len - |
| offsetof(struct leapraid_evt_notify_rep, evt_data); |
| if (evt_sz > evt_avail) { |
| dev_warn(&adapter->pdev->dev, |
| "%s: Invalid async event evt_data_len=%zu\n", |
| __func__, evt_sz); |
| return false; |
| } |
| |
| evt = le16_to_cpu(event_notify_rep->evt); |
| switch (evt) { |
| case LEAPRAID_EVT_SAS_DEV_STATUS_CHANGE: |
| if (evt_sz < |
| sizeof(struct leapraid_evt_data_sas_dev_status_change)) |
| goto invalid_evt_sz; |
| break; |
| case LEAPRAID_EVT_IR_CHANGE: |
| if (evt_sz < sizeof(struct leapraid_evt_data_ir_change)) |
| goto invalid_evt_sz; |
| break; |
| case LEAPRAID_EVT_SAS_TOPO_CHANGE_LIST: |
| { |
| struct leapraid_evt_data_sas_topo_change_list *evt_data = |
| (void *)event_notify_rep->evt_data; |
| size_t hdr_sz; |
| |
| hdr_sz = |
| offsetof(struct leapraid_evt_data_sas_topo_change_list, |
| phy); |
| if (evt_sz < hdr_sz) |
| goto invalid_evt_sz; |
| |
| if (evt_data->entry_num > |
| (evt_sz - hdr_sz) / sizeof(evt_data->phy[0])) |
| goto invalid_evt_sz; |
| break; |
| } |
| case LEAPRAID_EVT_SAS_ENCL_DEV_STATUS_CHANGE: |
| if (evt_sz < |
| sizeof(struct leapraid_evt_data_sas_enc_dev_status_change)) |
| goto invalid_evt_sz; |
| break; |
| default: |
| break; |
| } |
| |
| return true; |
| |
| invalid_evt_sz: |
| dev_warn(&adapter->pdev->dev, |
| "%s: Invalid async event size=%zu for evt=0x%x\n", |
| __func__, evt_sz, evt); |
| return false; |
| } |
| |
| static bool leapraid_async_process_evt( |
| struct leapraid_adapter *adapter, |
| struct leapraid_evt_notify_rep *event_notify_rep) |
| { |
| u16 evt = le16_to_cpu(event_notify_rep->evt); |
| bool exit_flag = false; |
| |
| if (adapter->access_ctrl.host_removing || |
| adapter->access_ctrl.pcie_recovering) |
| return true; |
| |
| switch (evt) { |
| case LEAPRAID_EVT_SAS_DEV_STATUS_CHANGE: |
| leapraid_sas_dev_stat_chg_evt( |
| adapter, |
| (struct leapraid_evt_data_sas_dev_status_change |
| *)event_notify_rep->evt_data); |
| break; |
| case LEAPRAID_EVT_IR_CHANGE: |
| leapraid_check_ir_change_evt( |
| adapter, |
| (struct leapraid_evt_data_ir_change |
| *)event_notify_rep->evt_data); |
| break; |
| case LEAPRAID_EVT_SAS_TOPO_CHANGE_LIST: |
| leapraid_check_topo_del_evts( |
| adapter, |
| (struct leapraid_evt_data_sas_topo_change_list |
| *)event_notify_rep->evt_data); |
| if (adapter->access_ctrl.shost_recovering) { |
| exit_flag = true; |
| return exit_flag; |
| } |
| break; |
| case LEAPRAID_EVT_SAS_ENCL_DEV_STATUS_CHANGE: |
| break; |
| default: |
| exit_flag = true; |
| return exit_flag; |
| } |
| |
| return exit_flag; |
| } |
| |
| static void leapraid_async_evt_cb_enqueue( |
| struct leapraid_adapter *adapter, |
| struct leapraid_evt_notify_rep *evt_notify_rep) |
| { |
| struct leapraid_fw_evt_work *fw_evt; |
| u16 evt_sz; |
| |
| fw_evt = leapraid_alloc_fw_evt_work(); |
| if (!fw_evt) |
| return; |
| |
| evt_sz = le16_to_cpu(evt_notify_rep->evt_data_len) * sizeof(u32); |
| fw_evt->evt_data = kmemdup(evt_notify_rep->evt_data, |
| evt_sz, GFP_ATOMIC); |
| if (!fw_evt->evt_data) { |
| leapraid_fw_evt_put(fw_evt); |
| return; |
| } |
| fw_evt->adapter = adapter; |
| fw_evt->evt_type = le16_to_cpu(evt_notify_rep->evt); |
| leapraid_fw_evt_add(adapter, fw_evt); |
| leapraid_fw_evt_put(fw_evt); |
| } |
| |
| static void leapraid_async_evt_cb(struct leapraid_adapter *adapter, |
| u8 msix_index, u32 rep_paddr) |
| { |
| struct leapraid_evt_notify_rep *evt_notify_rep; |
| |
| if (adapter->access_ctrl.host_removing || |
| adapter->access_ctrl.pcie_recovering) |
| return; |
| |
| evt_notify_rep = leapraid_get_reply_vaddr(adapter, rep_paddr); |
| if (unlikely(!evt_notify_rep)) |
| return; |
| |
| if (!leapraid_async_evt_validate(adapter, evt_notify_rep)) |
| return; |
| |
| if (leapraid_async_process_evt(adapter, evt_notify_rep)) |
| return; |
| |
| leapraid_async_evt_cb_enqueue(adapter, evt_notify_rep); |
| } |
| |
| static void leapraid_handle_async_event(struct leapraid_adapter *adapter, |
| u8 msix_index, u32 reply) |
| { |
| struct leapraid_evt_notify_rep *leap_mpi_rep = |
| leapraid_get_reply_vaddr(adapter, reply); |
| |
| if (!leap_mpi_rep) |
| return; |
| |
| if (leap_mpi_rep->func != LEAPRAID_FUNC_EVENT_NOTIFY) |
| return; |
| |
| leapraid_async_evt_cb(adapter, msix_index, reply); |
| } |
| |
| void leapraid_async_turn_on_led(struct leapraid_adapter *adapter, u16 handle) |
| { |
| struct leapraid_fw_evt_work *fw_event; |
| |
| fw_event = leapraid_alloc_fw_evt_work(); |
| if (!fw_event) |
| return; |
| |
| fw_event->dev_handle = handle; |
| fw_event->adapter = adapter; |
| fw_event->evt_type = LEAPRAID_EVT_TURN_ON_PFA_LED; |
| leapraid_fw_evt_add(adapter, fw_event); |
| leapraid_fw_evt_put(fw_event); |
| } |
| |
| static void leapraid_hardreset_barrier(struct leapraid_adapter *adapter) |
| { |
| struct leapraid_fw_evt_work *fw_event; |
| |
| fw_event = leapraid_alloc_fw_evt_work(); |
| if (!fw_event) |
| return; |
| |
| fw_event->adapter = adapter; |
| fw_event->evt_type = LEAPRAID_EVT_REMOVE_DEAD_DEV; |
| leapraid_fw_evt_add(adapter, fw_event); |
| leapraid_fw_evt_put(fw_event); |
| } |
| |
| static void leapraid_scan_dev_complete(struct leapraid_adapter *adapter) |
| { |
| struct leapraid_fw_evt_work *fw_evt; |
| |
| fw_evt = leapraid_alloc_fw_evt_work(); |
| if (!fw_evt) |
| return; |
| |
| fw_evt->evt_type = LEAPRAID_EVT_SCAN_DEV_DONE; |
| fw_evt->adapter = adapter; |
| leapraid_fw_evt_add(adapter, fw_evt); |
| leapraid_fw_evt_put(fw_evt); |
| } |
| |
| static void leapraid_handle_scan_cb(struct leapraid_adapter *adapter, |
| struct leapraid_driver_cmd *cmd, |
| struct leapraid_rep *rep) |
| { |
| u16 status; |
| |
| cmd->status &= ~LEAPRAID_CMD_PENDING; |
| |
| status = le16_to_cpu(rep->adapter_status) & |
| LEAPRAID_ADAPTER_STATUS_MASK; |
| if (status != LEAPRAID_ADAPTER_STATUS_SUCCESS) |
| adapter->scan_dev_desc.scan_dev_failed = 1; |
| |
| if (!cmd->async_scan_dev) { |
| complete(&cmd->done); |
| return; |
| } |
| |
| if (status == LEAPRAID_ADAPTER_STATUS_SUCCESS) |
| leapraid_scan_dev_complete(adapter); |
| else |
| adapter->scan_dev_desc.scan_start_failed = status; |
| } |
| |
| static void leapraid_handle_ctl_cb(struct leapraid_adapter *adapter, |
| struct leapraid_rep *rep, |
| u16 taskid) |
| { |
| struct leapraid_scsiio_rep *scsiio_reply; |
| |
| if (rep->function != LEAPRAID_FUNC_SCSIIO && |
| rep->function != LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH) |
| return; |
| |
| scsiio_reply = (struct leapraid_scsiio_rep *)rep; |
| if (!(scsiio_reply->scsi_state & |
| LEAPRAID_SCSI_STATE_AUTOSENSE_VALID)) |
| return; |
| |
| memcpy(&adapter->driver_cmds.ctl_cmd.sense, |
| leapraid_get_sense_buffer(adapter, taskid), |
| min_t(u32, SCSI_SENSE_BUFFERSIZE, |
| le32_to_cpu(scsiio_reply->sense_count))); |
| } |
| |
| static bool leapraid_driver_cmds_done(struct leapraid_adapter *adapter, |
| u16 taskid, u8 msix_index, |
| u32 rep_paddr, u8 cb_idx) |
| { |
| struct leapraid_rep *leap_mpi_rep = |
| leapraid_get_reply_vaddr(adapter, rep_paddr); |
| struct leapraid_driver_cmd *sp_cmd, *_sp_cmd = NULL; |
| u8 reply_len; |
| |
| list_for_each_entry(sp_cmd, &adapter->driver_cmds.special_cmd_list, |
| list) |
| if (cb_idx == sp_cmd->cb_idx) { |
| _sp_cmd = sp_cmd; |
| break; |
| } |
| |
| if (WARN_ON(!_sp_cmd)) |
| return true; |
| if (WARN_ON(_sp_cmd->status == LEAPRAID_CMD_NOT_USED)) |
| return true; |
| if (WARN_ON(taskid != _sp_cmd->hp_taskid && |
| taskid != _sp_cmd->taskid && |
| taskid != _sp_cmd->inter_taskid)) |
| return true; |
| _sp_cmd->status |= LEAPRAID_CMD_DONE; |
| if (leap_mpi_rep) { |
| reply_len = leap_mpi_rep->msg_len * sizeof(u32); |
| if (reply_len > LEAPRAID_REPLY_SIZE) |
| reply_len = LEAPRAID_REPLY_SIZE; |
| memcpy(&_sp_cmd->reply, leap_mpi_rep, reply_len); |
| _sp_cmd->status |= LEAPRAID_CMD_REPLY_VALID; |
| |
| if (_sp_cmd->cb_idx == LEAPRAID_SCAN_DEV_CB_IDX) { |
| leapraid_handle_scan_cb(adapter, _sp_cmd, |
| leap_mpi_rep); |
| return true; |
| } |
| |
| if (_sp_cmd->cb_idx == LEAPRAID_CTL_CB_IDX) |
| leapraid_handle_ctl_cb(adapter, leap_mpi_rep, taskid); |
| } |
| |
| _sp_cmd->status &= ~LEAPRAID_CMD_PENDING; |
| complete(&_sp_cmd->done); |
| |
| return true; |
| } |
| |
| static void leapraid_complete_task(struct leapraid_adapter *adapter, |
| u16 taskid, u8 msix_idx, u32 rep) |
| { |
| bool scsiio_task = taskid <= adapter->shost->can_queue; |
| |
| if (scsiio_task) { |
| if (leapraid_scsiio_done(adapter, taskid, msix_idx, rep)) |
| leapraid_free_taskid(adapter, taskid); |
| return; |
| } |
| |
| if (leapraid_driver_cmds_done(adapter, taskid, msix_idx, rep, |
| leapraid_get_cb_idx(adapter, taskid))) |
| leapraid_free_taskid(adapter, taskid); |
| } |
| |
| static void leapraid_request_descript_handler( |
| struct leapraid_adapter *adapter, |
| union leapraid_rep_desc_union *rpf, |
| u8 req_desc_type, u8 msix_idx) |
| { |
| u32 rep; |
| u16 taskid; |
| |
| rep = 0; |
| taskid = le16_to_cpu(rpf->dflt_rep.taskid); |
| switch (req_desc_type) { |
| case LEAPRAID_RPY_DESC_FLG_FP_SCSI_IO_SUCCESS: |
| case LEAPRAID_RPY_DESC_FLG_SCSI_IO_SUCCESS: |
| leapraid_complete_task(adapter, taskid, msix_idx, 0); |
| break; |
| case LEAPRAID_RPY_DESC_FLG_ADDRESS_REPLY: |
| rep = le32_to_cpu(rpf->addr_rep.rep_frame_addr); |
| if (rep > (u32)adapter->mem_desc.rep_msg_dma + |
| adapter->adapter_attr.rep_msg_qd * |
| LEAPRAID_REPLY_SIZE || |
| rep < (u32)adapter->mem_desc.rep_msg_dma) |
| rep = 0; |
| |
| if (taskid) |
| leapraid_complete_task(adapter, taskid, msix_idx, rep); |
| else |
| leapraid_handle_async_event(adapter, msix_idx, rep); |
| |
| if (!rep) |
| return; |
| |
| adapter->rep_msg_host_idx = |
| (adapter->rep_msg_host_idx == |
| (adapter->adapter_attr.rep_msg_qd - 1)) ? |
| 0 : adapter->rep_msg_host_idx + 1; |
| adapter->mem_desc.rep_msg_addr[adapter->rep_msg_host_idx] = |
| cpu_to_le32(rep); |
| wmb(); /* Make sure that all write ops are in order */ |
| writel(adapter->rep_msg_host_idx, |
| &adapter->iomem_base->rep_msg_host_idx); |
| |
| break; |
| default: |
| break; |
| } |
| } |
| |
| int leapraid_rep_queue_handler(struct leapraid_rq *rq) |
| { |
| struct leapraid_adapter *adapter = rq->adapter; |
| union leapraid_rep_desc_union *rep_desc; |
| u8 req_desc_type; |
| u64 finish_cmds; |
| u8 msix_idx; |
| |
| msix_idx = rq->msix_idx; |
| finish_cmds = 0; |
| if (!atomic_add_unless(&rq->busy, LEAPRAID_BUSY_LIMIT, |
| LEAPRAID_BUSY_LIMIT)) |
| return finish_cmds; |
| |
| rep_desc = &rq->rep_desc[rq->rep_post_host_idx]; |
| req_desc_type = rep_desc->dflt_rep.rep_flg & |
| LEAPRAID_RPY_DESC_FLG_TYPE_MASK; |
| if (req_desc_type == LEAPRAID_RPY_DESC_FLG_UNUSED) { |
| atomic_dec(&rq->busy); |
| return finish_cmds; |
| } |
| |
| for (;;) { |
| if (rep_desc->u.low == UINT_MAX || |
| rep_desc->u.high == UINT_MAX) |
| break; |
| |
| leapraid_request_descript_handler(adapter, rep_desc, |
| req_desc_type, msix_idx); |
| dev_dbg(&adapter->pdev->dev, |
| "LEAPRAID_SCSIIO: Handled Desc taskid %d, msix %d\n", |
| rep_desc->dflt_rep.taskid, msix_idx); |
| rep_desc->words = cpu_to_le64(ULLONG_MAX); |
| rq->rep_post_host_idx = |
| (rq->rep_post_host_idx == |
| (adapter->adapter_attr.rep_desc_qd - |
| LEAPRAID_BUSY_LIMIT)) ? |
| 0 : rq->rep_post_host_idx + 1; |
| req_desc_type = |
| rq->rep_desc[rq->rep_post_host_idx].dflt_rep.rep_flg & |
| LEAPRAID_RPY_DESC_FLG_TYPE_MASK; |
| finish_cmds++; |
| if (req_desc_type == LEAPRAID_RPY_DESC_FLG_UNUSED) |
| break; |
| rep_desc = rq->rep_desc + rq->rep_post_host_idx; |
| } |
| |
| if (!finish_cmds) { |
| atomic_dec(&rq->busy); |
| return finish_cmds; |
| } |
| |
| wmb(); /* Make sure that all write ops are in order */ |
| writel(rq->rep_post_host_idx | ((msix_idx & LEAPRAID_MSIX_GROUP_MASK) << |
| LEAPRAID_RPHI_MSIX_IDX_SHIFT), |
| &adapter->iomem_base->rep_post_reg_idx[ |
| msix_idx / LEAPRAID_MSIX_GROUP_SIZE].idx); |
| atomic_dec(&rq->busy); |
| return finish_cmds; |
| } |
| |
| static irqreturn_t leapraid_irq_handler(int irq, void *bus_id) |
| { |
| struct leapraid_rq *rq = bus_id; |
| struct leapraid_adapter *adapter = rq->adapter; |
| |
| dev_dbg(&adapter->pdev->dev, |
| "LEAPRAID_SCSIIO: Receive a interrupt, irq %d msix %d\n", |
| irq, rq->msix_idx); |
| |
| if (adapter->mask_int) |
| return IRQ_NONE; |
| |
| return (leapraid_rep_queue_handler(rq) > 0 ? |
| IRQ_HANDLED : IRQ_NONE); |
| } |
| |
| void leapraid_sync_irqs(struct leapraid_adapter *adapter, bool poll) |
| { |
| struct leapraid_int_rq *int_rq; |
| struct leapraid_blk_mq_poll_rq *blk_mq_poll_rq; |
| unsigned int i; |
| |
| if (!adapter->notification_desc.msix_enable) |
| return; |
| |
| if (adapter->access_ctrl.shost_recovering || |
| adapter->access_ctrl.host_removing || |
| adapter->access_ctrl.pcie_recovering) |
| return; |
| |
| for (i = 0; i < adapter->notification_desc.iopoll_qdex; i++) { |
| int_rq = &adapter->notification_desc.int_rqs[i]; |
| if (adapter->access_ctrl.shost_recovering || |
| adapter->access_ctrl.host_removing || |
| adapter->access_ctrl.pcie_recovering) |
| return; |
| |
| if (int_rq->rq.msix_idx == 0) |
| continue; |
| |
| synchronize_irq(pci_irq_vector(adapter->pdev, |
| int_rq->rq.msix_idx)); |
| if (poll) |
| leapraid_rep_queue_handler(&int_rq->rq); |
| } |
| |
| for (i = 0; i < adapter->notification_desc.iopoll_qcnt; i++) { |
| blk_mq_poll_rq = |
| &adapter->notification_desc.blk_mq_poll_rqs[i]; |
| if (adapter->access_ctrl.shost_recovering || |
| adapter->access_ctrl.host_removing || |
| adapter->access_ctrl.pcie_recovering) |
| return; |
| |
| if (blk_mq_poll_rq->rq.msix_idx == 0) |
| continue; |
| |
| leapraid_rep_queue_handler(&blk_mq_poll_rq->rq); |
| } |
| } |
| |
| static void leapraid_sync_irqs_for_cleanup(struct leapraid_adapter *adapter) |
| { |
| struct leapraid_notification_desc *desc = &adapter->notification_desc; |
| struct leapraid_int_rq *int_rq; |
| unsigned int i; |
| |
| if (!adapter->mask_int && leapraid_pci_active(adapter)) |
| leapraid_mask_int(adapter); |
| |
| for (i = 0; i < desc->int_rqs_allocated; i++) { |
| int_rq = &desc->int_rqs[i]; |
| synchronize_irq(pci_irq_vector(adapter->pdev, |
| int_rq->rq.msix_idx)); |
| } |
| } |
| |
| void leapraid_mq_polling_pause(struct leapraid_adapter *adapter) |
| { |
| struct leapraid_notification_desc *desc; |
| int iopoll_q_count; |
| int qid; |
| |
| desc = &adapter->notification_desc; |
| iopoll_q_count = adapter->adapter_attr.rq_cnt - desc->iopoll_qdex; |
| |
| for (qid = 0; qid < iopoll_q_count; qid++) |
| atomic_set(&desc->blk_mq_poll_rqs[qid].pause, 1); |
| |
| for (qid = 0; qid < iopoll_q_count; qid++) { |
| while (atomic_read(&desc->blk_mq_poll_rqs[qid].busy)) { |
| cpu_relax(); |
| udelay(LEAPRAID_IO_POLL_DELAY_US); |
| } |
| } |
| } |
| |
| void leapraid_mq_polling_resume(struct leapraid_adapter *adapter) |
| { |
| struct leapraid_notification_desc *desc; |
| int iopoll_q_count; |
| int qid; |
| |
| desc = &adapter->notification_desc; |
| iopoll_q_count = adapter->adapter_attr.rq_cnt - desc->iopoll_qdex; |
| |
| for (qid = 0; qid < iopoll_q_count; qid++) |
| atomic_set(&desc->blk_mq_poll_rqs[qid].pause, 0); |
| } |
| |
| static int leapraid_unlock_host_diag(struct leapraid_adapter *adapter, |
| u32 *host_diag) |
| { |
| const u32 unlock_seq[] = { |
| LEAPRAID_WRSEQ_FLUSH_KEY_VALUE, |
| LEAPRAID_WRSEQ_1ST_KEY_VALUE, |
| LEAPRAID_WRSEQ_2ND_KEY_VALUE, |
| LEAPRAID_WRSEQ_3RD_KEY_VALUE, |
| LEAPRAID_WRSEQ_4TH_KEY_VALUE, |
| LEAPRAID_WRSEQ_5TH_KEY_VALUE, |
| LEAPRAID_WRSEQ_6TH_KEY_VALUE |
| }; |
| |
| const int max_retries = LEAPRAID_UNLOCK_RETRY_LIMIT; |
| int retry = 0; |
| unsigned int i; |
| |
| *host_diag = 0; |
| while (retry++ <= max_retries) { |
| for (i = 0; i < ARRAY_SIZE(unlock_seq); i++) |
| writel(unlock_seq[i], &adapter->iomem_base->ws); |
| |
| msleep(LEAPRAID_UNLOCK_SLEEP_MS); |
| |
| *host_diag = leapraid_readl(&adapter->iomem_base->host_diag); |
| if (*host_diag & LEAPRAID_DIAG_WRITE_ENABLE) |
| return 0; |
| } |
| |
| dev_err(&adapter->pdev->dev, "Try host reset timeout!\n"); |
| return -EFAULT; |
| } |
| |
| static int leapraid_host_diag_reset(struct leapraid_adapter *adapter) |
| { |
| u32 host_diag; |
| |
| pci_cfg_access_lock(adapter->pdev); |
| |
| mutex_lock(&adapter->reset_desc.host_diag_mutex); |
| if (leapraid_unlock_host_diag(adapter, &host_diag)) |
| goto out_cleanup; |
| |
| writel(host_diag | LEAPRAID_DIAG_RESET, |
| &adapter->iomem_base->host_diag); |
| |
| msleep(LEAPRAID_MSLEEP_EXTRA_LONG_MS); |
| msleep(LEAPRAID_MSLEEP_NORMAL_MS); |
| |
| host_diag = leapraid_readl(&adapter->iomem_base->host_diag); |
| if (host_diag == LEAPRAID_INVALID_HOST_DIAG_VAL || |
| host_diag & LEAPRAID_DIAG_RESET) |
| goto out_cleanup; |
| |
| writel(0x0, &adapter->iomem_base->ws); |
| mutex_unlock(&adapter->reset_desc.host_diag_mutex); |
| if (!leapraid_wait_adapter_ready(adapter)) |
| goto out_failed; |
| |
| pci_cfg_access_unlock(adapter->pdev); |
| return 0; |
| |
| out_cleanup: |
| mutex_unlock(&adapter->reset_desc.host_diag_mutex); |
| out_failed: |
| pci_cfg_access_unlock(adapter->pdev); |
| dev_err(&adapter->pdev->dev, "Host diag failed\n"); |
| return -EFAULT; |
| } |
| |
| static int leapraid_find_matching_port( |
| struct leapraid_card_port *card_port_table, |
| u8 count, u8 port_id, u64 sas_addr) |
| { |
| int i; |
| |
| for (i = 0; i < count; i++) |
| if (card_port_table[i].port_id == port_id && |
| card_port_table[i].sas_address == sas_addr) |
| return i; |
| |
| return LEAPRAID_INVALID_INDEX; |
| } |
| |
| static u8 leapraid_fill_card_port_table( |
| struct leapraid_adapter *adapter, |
| struct leapraid_sas_io_unit_p0 *sas_iounit_p0, |
| struct leapraid_card_port *new_card_port_table) |
| { |
| u8 port_entry_num = 0, port_id; |
| u16 attached_hdl; |
| u64 attached_sas_addr; |
| int i, idx; |
| |
| for (i = 0; i < adapter->dev_topo.card.phys_num; i++) { |
| if (sas_iounit_p0->phy_info[i].neg_link_rate >> |
| LEAPRAID_SAS_NEG_LINK_RATE_SHIFT < |
| LEAPRAID_SAS_NEG_LINK_RATE_1_5) |
| continue; |
| |
| attached_hdl = le16_to_cpu(sas_iounit_p0->phy_info[i] |
| .attached_dev_hdl); |
| if (leapraid_get_sas_address(adapter, |
| attached_hdl, |
| &attached_sas_addr) != 0) |
| continue; |
| |
| port_id = sas_iounit_p0->phy_info[i].port; |
| |
| idx = leapraid_find_matching_port(new_card_port_table, |
| port_entry_num, |
| port_id, |
| attached_sas_addr); |
| if (idx >= 0) { |
| new_card_port_table[idx].phy_mask |= BIT(i); |
| } else { |
| new_card_port_table[port_entry_num].port_id = port_id; |
| new_card_port_table[port_entry_num].phy_mask = BIT(i); |
| new_card_port_table[port_entry_num].sas_address = |
| attached_sas_addr; |
| port_entry_num++; |
| } |
| } |
| |
| return port_entry_num; |
| } |
| |
| static u8 leapraid_set_new_card_port_table_after_reset( |
| struct leapraid_adapter *adapter, |
| struct leapraid_card_port *new_card_port_table) |
| { |
| union cfg_param_1 cfgp1 = {0}; |
| union cfg_param_2 cfgp2 = {0}; |
| struct leapraid_sas_io_unit_p0 *sas_iounit_p0; |
| u8 port_entry_num = 0; |
| u16 sz; |
| |
| sz = offsetof(struct leapraid_sas_io_unit_p0, phy_info) + |
| (adapter->dev_topo.card.phys_num * |
| sizeof(struct leapraid_sas_io_unit0_phy_info)); |
| sas_iounit_p0 = kzalloc(sz, GFP_KERNEL); |
| if (!sas_iounit_p0) |
| return port_entry_num; |
| |
| cfgp1.size = sz; |
| if (leapraid_op_config_page(adapter, sas_iounit_p0, cfgp1, cfgp2, |
| GET_SAS_IOUNIT_PG0) != 0) |
| goto out_free; |
| |
| port_entry_num = leapraid_fill_card_port_table(adapter, |
| sas_iounit_p0, |
| new_card_port_table); |
| out_free: |
| kfree(sas_iounit_p0); |
| return port_entry_num; |
| } |
| |
| static void leapraid_update_existing_port(struct leapraid_adapter *adapter, |
| struct leapraid_card_port *new_table, |
| int entry_idx, int port_entry_num) |
| { |
| struct leapraid_card_port *matched_card_port; |
| int matched_code; |
| int count, lcount = 0; |
| u64 sas_addr; |
| int i; |
| |
| matched_code = leapraid_check_card_port(adapter, |
| &new_table[entry_idx], |
| &matched_card_port, |
| &count); |
| |
| if (!matched_card_port) |
| return; |
| |
| if (matched_code == SAME_PORT_WITH_PARTIALLY_CHANGED_PHYS || |
| matched_code == SAME_ADDR_WITH_PARTIALLY_CHANGED_PHYS) { |
| leapraid_add_or_del_phys_from_existing_port(adapter, |
| matched_card_port, |
| new_table, |
| entry_idx, |
| port_entry_num); |
| } else if (matched_code == SAME_ADDR_ONLY) { |
| sas_addr = new_table[entry_idx].sas_address; |
| for (i = 0; i < port_entry_num; i++) |
| if (new_table[i].sas_address == sas_addr) |
| lcount++; |
| |
| if (count > 1 || lcount > 1) |
| return; |
| |
| leapraid_add_or_del_phys_from_existing_port(adapter, |
| matched_card_port, |
| new_table, |
| entry_idx, |
| port_entry_num); |
| } |
| |
| if (matched_card_port->port_id != new_table[entry_idx].port_id) |
| matched_card_port->port_id = new_table[entry_idx].port_id; |
| |
| matched_card_port->flg &= ~LEAPRAID_CARD_PORT_FLG_DIRTY; |
| matched_card_port->phy_mask = new_table[entry_idx].phy_mask; |
| } |
| |
| static void leapraid_update_card_port_after_reset( |
| struct leapraid_adapter *adapter) |
| { |
| struct leapraid_card_port *new_card_port_table; |
| struct leapraid_card_port *matched_card_port; |
| u8 port_entry_num; |
| u8 nr_phys; |
| int i; |
| |
| if (leapraid_get_adapter_phys(adapter, &nr_phys) || !nr_phys) |
| return; |
| |
| if (!adapter->dev_topo.card.card_phy) { |
| adapter->dev_topo.card.card_phy = |
| kcalloc(nr_phys, sizeof(struct leapraid_card_phy), |
| GFP_KERNEL); |
| if (!adapter->dev_topo.card.card_phy) |
| return; |
| } |
| |
| adapter->dev_topo.card.phys_num = nr_phys; |
| |
| new_card_port_table = kcalloc(adapter->dev_topo.card.phys_num, |
| sizeof(struct leapraid_card_port), |
| GFP_KERNEL); |
| if (!new_card_port_table) |
| return; |
| |
| port_entry_num = |
| leapraid_set_new_card_port_table_after_reset( |
| adapter, |
| new_card_port_table); |
| if (!port_entry_num) |
| goto out_free_port_table; |
| |
| list_for_each_entry(matched_card_port, |
| &adapter->dev_topo.card_port_list, list) |
| matched_card_port->flg |= LEAPRAID_CARD_PORT_FLG_DIRTY; |
| |
| matched_card_port = NULL; |
| for (i = 0; i < port_entry_num; i++) |
| leapraid_update_existing_port(adapter, |
| new_card_port_table, |
| i, port_entry_num); |
| out_free_port_table: |
| kfree(new_card_port_table); |
| } |
| |
| static bool leapraid_is_valid_vphy( |
| struct leapraid_adapter *adapter, |
| struct leapraid_sas_io_unit_p0 *sas_io_unit_p0, |
| int phy_index) |
| { |
| union cfg_param_1 cfgp1 = {0}; |
| union cfg_param_2 cfgp2 = {0}; |
| struct leapraid_sas_phy_p0 phy_p0; |
| u32 dev_info; |
| |
| if (sas_io_unit_p0->phy_info[phy_index].neg_link_rate >> |
| LEAPRAID_SAS_NEG_LINK_RATE_SHIFT < |
| LEAPRAID_SAS_NEG_LINK_RATE_1_5) |
| return false; |
| |
| dev_info = le32_to_cpu(sas_io_unit_p0->phy_info[phy_index] |
| .controller_phy_dev_info); |
| if (!(dev_info & LEAPRAID_DEVTYP_SEP)) |
| return false; |
| |
| cfgp1.phy_number = phy_index; |
| if (leapraid_op_config_page(adapter, &phy_p0, cfgp1, cfgp2, |
| GET_PHY_PG0)) |
| return false; |
| |
| if (!(le32_to_cpu(phy_p0.phy_info) & LEAPRAID_SAS_PHYINFO_VPHY)) |
| return false; |
| |
| return true; |
| } |
| |
| static void leapraid_update_vphy_binding(struct leapraid_adapter *adapter, |
| struct leapraid_card_port *card_port, |
| struct leapraid_vphy *vphy, |
| int phy_index, u8 may_new_port_id, |
| u64 attached_sas_addr) |
| { |
| struct leapraid_card_port *may_new_card_port; |
| struct leapraid_sas_dev *sas_dev; |
| |
| may_new_card_port = leapraid_get_port_by_id(adapter, |
| may_new_port_id, |
| true); |
| if (!may_new_card_port) { |
| may_new_card_port = kzalloc_obj(*may_new_card_port); |
| if (!may_new_card_port) |
| return; |
| may_new_card_port->port_id = may_new_port_id; |
| dev_err(&adapter->pdev->dev, |
| "%s: New card port %p added, port=%d\n", |
| __func__, may_new_card_port, may_new_port_id); |
| list_add_tail(&may_new_card_port->list, |
| &adapter->dev_topo.card_port_list); |
| } |
| |
| if (card_port != may_new_card_port) { |
| if (!may_new_card_port->vphys_mask) |
| INIT_LIST_HEAD(&may_new_card_port->vphys_list); |
| may_new_card_port->vphys_mask |= BIT(phy_index); |
| card_port->vphys_mask &= ~BIT(phy_index); |
| list_move(&vphy->list, &may_new_card_port->vphys_list); |
| |
| sas_dev = leapraid_get_sas_dev_by_addr(adapter, |
| attached_sas_addr, |
| card_port); |
| if (sas_dev) { |
| sas_dev->card_port = may_new_card_port; |
| leapraid_sdev_put(sas_dev); |
| } |
| } |
| |
| if (may_new_card_port->flg & LEAPRAID_CARD_PORT_FLG_DIRTY) { |
| may_new_card_port->sas_address = 0; |
| may_new_card_port->phy_mask = 0; |
| may_new_card_port->flg &= ~LEAPRAID_CARD_PORT_FLG_DIRTY; |
| } |
| vphy->flg &= ~LEAPRAID_VPHY_FLG_DIRTY; |
| } |
| |
| static void leapraid_update_vphys_after_reset(struct leapraid_adapter *adapter) |
| { |
| union cfg_param_1 cfgp1 = {0}; |
| union cfg_param_2 cfgp2 = {0}; |
| struct leapraid_sas_io_unit_p0 *sas_iounit_p0; |
| struct leapraid_card_port *card_port, *card_port_next; |
| struct leapraid_vphy *vphy, *vphy_next; |
| u64 attached_sas_addr; |
| u16 sz; |
| u16 attached_hdl; |
| bool found = false; |
| u8 port_id; |
| int i; |
| |
| list_for_each_entry_safe(card_port, card_port_next, |
| &adapter->dev_topo.card_port_list, list) { |
| if (!card_port->vphys_mask) |
| continue; |
| |
| list_for_each_entry_safe(vphy, vphy_next, |
| &card_port->vphys_list, list) |
| vphy->flg |= LEAPRAID_VPHY_FLG_DIRTY; |
| } |
| |
| sz = offsetof(struct leapraid_sas_io_unit_p0, phy_info) + |
| (adapter->dev_topo.card.phys_num * |
| sizeof(struct leapraid_sas_io_unit0_phy_info)); |
| sas_iounit_p0 = kzalloc(sz, GFP_KERNEL); |
| if (!sas_iounit_p0) |
| return; |
| |
| cfgp1.size = sz; |
| if (leapraid_op_config_page(adapter, sas_iounit_p0, cfgp1, cfgp2, |
| GET_SAS_IOUNIT_PG0) != 0) |
| goto out_free; |
| |
| for (i = 0; i < adapter->dev_topo.card.phys_num; i++) { |
| if (!leapraid_is_valid_vphy(adapter, sas_iounit_p0, i)) |
| continue; |
| |
| attached_hdl = |
| le16_to_cpu(sas_iounit_p0->phy_info[i] |
| .attached_dev_hdl); |
| if (leapraid_get_sas_address(adapter, attached_hdl, |
| &attached_sas_addr) != 0) |
| continue; |
| |
| found = false; |
| card_port = NULL; |
| card_port_next = NULL; |
| list_for_each_entry_safe(card_port, card_port_next, |
| &adapter->dev_topo.card_port_list, |
| list) { |
| if (!card_port->vphys_mask) |
| continue; |
| |
| list_for_each_entry_safe(vphy, vphy_next, |
| &card_port->vphys_list, |
| list) { |
| if (!(vphy->flg & LEAPRAID_VPHY_FLG_DIRTY)) |
| continue; |
| |
| if (vphy->sas_address != attached_sas_addr) |
| continue; |
| |
| if (!(vphy->phy_mask & BIT(i))) |
| vphy->phy_mask = BIT(i); |
| |
| port_id = sas_iounit_p0->phy_info[i].port; |
| |
| leapraid_update_vphy_binding(adapter, |
| card_port, |
| vphy, |
| i, |
| port_id, |
| attached_sas_addr); |
| |
| found = true; |
| break; |
| } |
| if (found) |
| break; |
| } |
| } |
| out_free: |
| kfree(sas_iounit_p0); |
| } |
| |
| static void leapraid_mark_all_dev_deleted(struct leapraid_adapter *adapter) |
| { |
| struct leapraid_sdev_priv *sdev_priv; |
| struct scsi_device *sdev; |
| |
| shost_for_each_device(sdev, adapter->shost) { |
| sdev_priv = sdev->hostdata; |
| if (sdev_priv && sdev_priv->starget_priv) |
| sdev_priv->starget_priv->deleted = 1; |
| } |
| } |
| |
| static void leapraid_free_enc_list(struct leapraid_adapter *adapter) |
| { |
| struct leapraid_enc_node *enc_dev, *enc_dev_next; |
| LIST_HEAD(free_list); |
| unsigned long flags; |
| |
| spin_lock_irqsave(&adapter->dev_topo.enc_lock, flags); |
| list_splice_init(&adapter->dev_topo.enc_list, &free_list); |
| spin_unlock_irqrestore(&adapter->dev_topo.enc_lock, flags); |
| |
| list_for_each_entry_safe(enc_dev, enc_dev_next, &free_list, list) { |
| list_del(&enc_dev->list); |
| kfree(enc_dev); |
| } |
| } |
| |
| static void leapraid_rebuild_enc_list_after_reset( |
| struct leapraid_adapter *adapter) |
| { |
| union cfg_param_1 cfgp1 = {0}; |
| union cfg_param_2 cfgp2 = {0}; |
| struct leapraid_enc_node *enc_node; |
| u16 enc_hdl; |
| unsigned long flags; |
| int rc; |
| |
| leapraid_free_enc_list(adapter); |
| |
| cfgp1.form = LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP; |
| for (enc_hdl = 0xFFFF; ; enc_hdl = le16_to_cpu(enc_node->pg0.enc_hdl)) { |
| enc_node = kzalloc_obj(*enc_node); |
| if (!enc_node) |
| return; |
| |
| cfgp2.handle = enc_hdl; |
| rc = leapraid_op_config_page(adapter, &enc_node->pg0, cfgp1, |
| cfgp2, GET_SAS_ENCLOSURE_PG0); |
| if (rc) { |
| kfree(enc_node); |
| return; |
| } |
| |
| spin_lock_irqsave(&adapter->dev_topo.enc_lock, flags); |
| list_add_tail(&enc_node->list, &adapter->dev_topo.enc_list); |
| spin_unlock_irqrestore(&adapter->dev_topo.enc_lock, flags); |
| } |
| } |
| |
| static void leapraid_mark_resp_sas_dev(struct leapraid_adapter *adapter, |
| struct leapraid_sas_dev_p0 *sas_dev_p0) |
| { |
| struct leapraid_starget_priv *starget_priv; |
| struct leapraid_enc_node *enc_node = NULL; |
| struct leapraid_card_port *card_port; |
| struct leapraid_sas_dev *sas_dev; |
| struct scsi_target *starget; |
| unsigned long flags; |
| unsigned long enc_flags = 0; |
| u16 enc_hdl; |
| u64 enc_lid = 0; |
| |
| card_port = leapraid_get_port_by_id(adapter, sas_dev_p0->physical_port, |
| false); |
| enc_hdl = le16_to_cpu(sas_dev_p0->enc_hdl); |
| if (enc_hdl) { |
| spin_lock_irqsave(&adapter->dev_topo.enc_lock, enc_flags); |
| enc_node = leapraid_enc_find_by_hdl(adapter, enc_hdl); |
| if (enc_node) |
| enc_lid = le64_to_cpu(enc_node->pg0.enc_lid); |
| spin_unlock_irqrestore(&adapter->dev_topo.enc_lock, enc_flags); |
| if (!enc_node) |
| dev_info(&adapter->pdev->dev, |
| "enc hdl 0x%04x has no matched enc dev\n", |
| enc_hdl); |
| } |
| |
| spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); |
| list_for_each_entry(sas_dev, &adapter->dev_topo.sas_dev_list, list) { |
| if (sas_dev->sas_addr != le64_to_cpu(sas_dev_p0->sas_address) || |
| sas_dev->slot != le16_to_cpu(sas_dev_p0->slot) || |
| sas_dev->card_port != card_port) |
| continue; |
| |
| sas_dev->resp = 1; |
| starget = sas_dev->starget; |
| if (starget && starget->hostdata) { |
| starget_priv = starget->hostdata; |
| starget_priv->tm_busy = 0; |
| starget_priv->deleted = 0; |
| } else { |
| starget_priv = NULL; |
| } |
| |
| if (starget) { |
| starget_printk( |
| KERN_INFO, starget, |
| "dev: hdl=0x%04x saddr=0x%016llx port_id=%d\n", |
| sas_dev->hdl, |
| (unsigned long long)sas_dev->sas_addr, |
| sas_dev->card_port->port_id); |
| if (sas_dev->enc_hdl != 0) |
| starget_printk( |
| KERN_INFO, |
| starget, |
| "enc info: enc_lid=0x%016llx slot=%d\n", |
| (unsigned long long)sas_dev->enc_lid, |
| sas_dev->slot); |
| } |
| |
| if (le16_to_cpu(sas_dev_p0->flg) & |
| LEAPRAID_SAS_DEV_P0_FLG_ENC_LEVEL_VALID) { |
| sas_dev->enc_level = sas_dev_p0->enc_level; |
| memcpy(sas_dev->connector_name, |
| sas_dev_p0->connector_name, |
| LEAPRAID_SAS_DEV_P0_CON_NAME_LEN); |
| sas_dev->connector_name[ |
| LEAPRAID_SAS_DEV_P0_CON_NAME_LEN] = '\0'; |
| } else { |
| sas_dev->enc_level = 0; |
| sas_dev->connector_name[0] = '\0'; |
| } |
| |
| sas_dev->enc_hdl = enc_hdl; |
| sas_dev->enc_lid = enc_lid; |
| |
| if (sas_dev->hdl == le16_to_cpu(sas_dev_p0->dev_hdl)) |
| goto unlock; |
| |
| dev_info(&adapter->pdev->dev, |
| "hdl changed: 0x%04x -> 0x%04x\n", |
| sas_dev->hdl, sas_dev_p0->dev_hdl); |
| sas_dev->hdl = le16_to_cpu(sas_dev_p0->dev_hdl); |
| if (starget_priv) |
| starget_priv->hdl = le16_to_cpu(sas_dev_p0->dev_hdl); |
| |
| goto unlock; |
| } |
| unlock: |
| spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); |
| } |
| |
| static void leapraid_search_resp_sas_dev(struct leapraid_adapter *adapter) |
| { |
| union cfg_param_1 cfgp1 = {0}; |
| union cfg_param_2 cfgp2 = {0}; |
| struct leapraid_sas_dev_p0 sas_dev_p0; |
| u32 device_info; |
| |
| if (list_empty(&adapter->dev_topo.sas_dev_list)) |
| return; |
| |
| cfgp1.form = LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP; |
| for (cfgp2.handle = 0xFFFF; |
| !leapraid_op_config_page(adapter, &sas_dev_p0, |
| cfgp1, cfgp2, GET_SAS_DEVICE_PG0); |
| cfgp2.handle = le16_to_cpu(sas_dev_p0.dev_hdl)) { |
| device_info = le32_to_cpu(sas_dev_p0.dev_info); |
| if (!(leapraid_is_end_dev(device_info))) |
| continue; |
| |
| leapraid_mark_resp_sas_dev(adapter, &sas_dev_p0); |
| } |
| } |
| |
| static void leapraid_mark_resp_raid_volume(struct leapraid_adapter *adapter, |
| u64 wwid, u16 hdl) |
| { |
| struct leapraid_raid_volume *raid_volume; |
| struct leapraid_starget_priv *starget_priv = NULL; |
| struct scsi_target *starget; |
| unsigned long flags; |
| u64 volume_wwid; |
| u16 old_hdl; |
| |
| raid_volume = leapraid_raid_volume_find_by_wwid(adapter, wwid); |
| if (!raid_volume) |
| return; |
| |
| spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); |
| if (!raid_volume->starget) { |
| spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, |
| flags); |
| leapraid_raid_volume_put(raid_volume); |
| return; |
| } |
| |
| starget = raid_volume->starget; |
| if (starget->hostdata) { |
| starget_priv = starget->hostdata; |
| starget_priv->deleted = 0; |
| } |
| |
| raid_volume->resp = 1; |
| volume_wwid = raid_volume->wwid; |
| old_hdl = raid_volume->hdl; |
| if (old_hdl != hdl) { |
| raid_volume->hdl = hdl; |
| if (starget_priv) |
| starget_priv->hdl = hdl; |
| } |
| spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags); |
| |
| starget_printk(KERN_INFO, starget, |
| "raid volume: hdl=0x%04x, wwid=0x%016llx\n", |
| hdl, (unsigned long long)volume_wwid); |
| if (old_hdl != hdl) |
| dev_info(&adapter->pdev->dev, |
| "hdl changed: 0x%04x -> 0x%04x\n", |
| old_hdl, hdl); |
| |
| leapraid_raid_volume_put(raid_volume); |
| } |
| |
| static void leapraid_search_resp_raid_volume(struct leapraid_adapter *adapter) |
| { |
| union cfg_param_1 cfgp1 = {0}; |
| union cfg_param_1 cfgp1_extra = {0}; |
| union cfg_param_2 cfgp2 = {0}; |
| union cfg_param_2 cfgp2_extra = {0}; |
| struct leapraid_raidvol_p1 raidvol_p1; |
| struct leapraid_raidvol_p0 raidvol_p0; |
| struct leapraid_raidpd_p0 raidpd_p0; |
| unsigned long flags; |
| u16 hdl; |
| u8 phys_disk_num; |
| bool is_empty; |
| |
| if (!adapter->adapter_attr.raid_support) |
| return; |
| |
| spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); |
| is_empty = list_empty(&adapter->dev_topo.raid_volume_list); |
| spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags); |
| if (is_empty) |
| return; |
| |
| cfgp1.form = LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP; |
| for (hdl = 0xFFFF, cfgp2.handle = hdl; |
| !leapraid_op_config_page(adapter, &raidvol_p1, cfgp1, cfgp2, |
| GET_RAID_VOLUME_PG1); |
| cfgp2.handle = hdl) { |
| hdl = le16_to_cpu(raidvol_p1.dev_hdl); |
| cfgp1_extra.size = sizeof(struct leapraid_raidvol_p0); |
| cfgp2_extra.handle = hdl; |
| if (leapraid_op_config_page(adapter, &raidvol_p0, cfgp1_extra, |
| cfgp2_extra, GET_RAID_VOLUME_PG0)) |
| continue; |
| |
| if (raidvol_p0.volume_state == LEAPRAID_VOL_STATE_OPTIMAL || |
| raidvol_p0.volume_state == LEAPRAID_VOL_STATE_ONLINE || |
| raidvol_p0.volume_state == LEAPRAID_VOL_STATE_DEGRADED) |
| leapraid_mark_resp_raid_volume( |
| adapter, |
| le64_to_cpu(raidvol_p1.wwid), |
| hdl); |
| } |
| |
| memset(adapter->dev_topo.pd_hdls, 0, adapter->dev_topo.pd_hdls_sz); |
| cfgp1.form = LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP; |
| for (phys_disk_num = 0xFF, cfgp2.form_specific = phys_disk_num; |
| !leapraid_op_config_page(adapter, &raidpd_p0, cfgp1, cfgp2, |
| GET_PHY_DISK_PG0); |
| cfgp2.form_specific = phys_disk_num) { |
| phys_disk_num = raidpd_p0.phys_disk_num; |
| hdl = le16_to_cpu(raidpd_p0.dev_hdl); |
| if (!hdl || |
| hdl > adapter->adapter_attr.features.max_dev_handle) |
| dev_warn(&adapter->pdev->dev, |
| "%s: Invalid device handle\n", __func__); |
| else |
| set_bit(hdl, adapter->dev_topo.pd_hdls); |
| } |
| } |
| |
| static void leapraid_mark_resp_exp(struct leapraid_adapter *adapter, |
| struct leapraid_exp_p0 *exp_pg0) |
| { |
| struct leapraid_enc_node *enc_node = NULL; |
| struct leapraid_topo_node *topo_node_exp; |
| u16 enc_hdl = le16_to_cpu(exp_pg0->enc_hdl); |
| u64 sas_address = le64_to_cpu(exp_pg0->sas_address); |
| u16 hdl = le16_to_cpu(exp_pg0->dev_hdl); |
| u8 port_id = exp_pg0->physical_port; |
| struct leapraid_card_port *card_port = leapraid_get_port_by_id(adapter, |
| port_id, |
| false); |
| unsigned long flags; |
| unsigned long enc_flags = 0; |
| u64 enc_lid = 0; |
| int i; |
| |
| if (enc_hdl) { |
| spin_lock_irqsave(&adapter->dev_topo.enc_lock, enc_flags); |
| enc_node = leapraid_enc_find_by_hdl(adapter, enc_hdl); |
| if (enc_node) |
| enc_lid = le64_to_cpu(enc_node->pg0.enc_lid); |
| spin_unlock_irqrestore(&adapter->dev_topo.enc_lock, enc_flags); |
| } |
| |
| spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags); |
| list_for_each_entry(topo_node_exp, &adapter->dev_topo.exp_list, list) { |
| if (topo_node_exp->sas_address != sas_address || |
| topo_node_exp->card_port != card_port) |
| continue; |
| |
| topo_node_exp->resp = 1; |
| topo_node_exp->enc_hdl = enc_hdl; |
| topo_node_exp->enc_lid = enc_lid; |
| if (topo_node_exp->hdl == hdl) |
| goto unlock; |
| |
| dev_info(&adapter->pdev->dev, |
| "hdl changed: 0x%04x -> 0x%04x\n", |
| topo_node_exp->hdl, hdl); |
| topo_node_exp->hdl = hdl; |
| for (i = 0; i < topo_node_exp->phys_num; i++) |
| topo_node_exp->card_phy[i].hdl = hdl; |
| goto unlock; |
| } |
| unlock: |
| spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags); |
| } |
| |
| static void leapraid_search_resp_exp(struct leapraid_adapter *adapter) |
| { |
| union cfg_param_1 cfgp1 = {0}; |
| union cfg_param_2 cfgp2 = {0}; |
| struct leapraid_exp_p0 exp_p0; |
| u64 sas_address; |
| u16 hdl; |
| u8 port; |
| |
| if (list_empty(&adapter->dev_topo.exp_list)) |
| return; |
| |
| cfgp1.form = LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP; |
| for (hdl = 0xFFFF, cfgp2.handle = hdl; |
| !leapraid_op_config_page(adapter, &exp_p0, cfgp1, cfgp2, |
| GET_SAS_EXPANDER_PG0); |
| cfgp2.handle = hdl) { |
| hdl = le16_to_cpu(exp_p0.dev_hdl); |
| sas_address = le64_to_cpu(exp_p0.sas_address); |
| port = exp_p0.physical_port; |
| |
| dev_dbg(&adapter->pdev->dev, |
| "exp detected: hdl=0x%04x, sas=0x%016llx, port=%u", |
| hdl, (unsigned long long)sas_address, |
| adapter->adapter_attr.enable_mp ? port : |
| LEAPRAID_DISABLE_MP_PORT_ID); |
| leapraid_mark_resp_exp(adapter, &exp_p0); |
| } |
| } |
| |
| void leapraid_wait_cmds_done(struct leapraid_adapter *adapter) |
| { |
| struct leapraid_io_req_tracker *io_req_tracker; |
| unsigned long flags; |
| u16 i; |
| |
| adapter->reset_desc.pending_io_cnt = 0; |
| if (!leapraid_pci_active(adapter)) { |
| dev_err(&adapter->pdev->dev, |
| "%s %s: PCI error, device reset or unplugged!\n", |
| adapter->adapter_attr.name, __func__); |
| return; |
| } |
| |
| if (leapraid_get_adapter_state(adapter) != LEAPRAID_DB_OPERATIONAL) |
| return; |
| |
| spin_lock_irqsave(&adapter->dynamic_task_desc.task_lock, flags); |
| for (i = 1; i <= adapter->shost->can_queue; i++) { |
| io_req_tracker = leapraid_get_io_tracker_from_taskid(adapter, |
| i); |
| if (io_req_tracker && io_req_tracker->taskid != 0 && |
| io_req_tracker->scmd) |
| adapter->reset_desc.pending_io_cnt++; |
| } |
| spin_unlock_irqrestore(&adapter->dynamic_task_desc.task_lock, flags); |
| |
| if (!adapter->reset_desc.pending_io_cnt) |
| return; |
| |
| wait_event_timeout(adapter->reset_desc.reset_wait_queue, |
| adapter->reset_desc.pending_io_cnt == 0, |
| LEAPRAID_IO_CMD_TIMEOUT * HZ); |
| } |
| |
| int leapraid_hard_reset_handler(struct leapraid_adapter *adapter, |
| enum reset_type type) |
| { |
| unsigned long flags; |
| bool wake = false; |
| int rc; |
| |
| mutex_lock(&adapter->reset_desc.adapter_reset_mutex); |
| |
| if (adapter->access_ctrl.shost_recover_async) { |
| rc = adapter->reset_desc.adapter_reset_results; |
| dev_info(&adapter->pdev->dev, |
| "Skip nested hard reset, async evt running, rc=%d\n", |
| rc); |
| mutex_unlock(&adapter->reset_desc.adapter_reset_mutex); |
| return rc; |
| } |
| |
| if (!leapraid_pci_active(adapter)) { |
| if (leapraid_pci_removed(adapter)) { |
| dev_info(&adapter->pdev->dev, |
| "pci_dev removed, pause poll, clean cmds\n"); |
| leapraid_mq_polling_pause(adapter); |
| leapraid_clean_active_scsi_cmds(adapter); |
| leapraid_mq_polling_resume(adapter); |
| } |
| dev_err(&adapter->pdev->dev, "PCIe unavailable!\n"); |
| mutex_unlock(&adapter->reset_desc.adapter_reset_mutex); |
| return -ENXIO; |
| } |
| |
| dev_info(&adapter->pdev->dev, "Starting hard reset\n"); |
| |
| spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags); |
| adapter->access_ctrl.shost_recovering = 1; |
| adapter->access_ctrl.shost_recover_async = 1; |
| spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags); |
| |
| leapraid_wait_cmds_done(adapter); |
| leapraid_mask_int(adapter); |
| leapraid_mq_polling_pause(adapter); |
| rc = leapraid_make_adapter_ready(adapter, type); |
| if (rc) { |
| dev_err(&adapter->pdev->dev, |
| "Failed to make adapter ready, rc=%d\n", rc); |
| goto out_cleanup; |
| } |
| |
| rc = leapraid_fw_log_init(adapter); |
| if (rc) { |
| dev_err(&adapter->pdev->dev, "Firmware log init failed\n"); |
| goto out_cleanup; |
| } |
| |
| leapraid_clean_active_cmds(adapter); |
| if (adapter->scan_dev_desc.driver_loading && |
| adapter->scan_dev_desc.scan_dev_failed) { |
| dev_err(&adapter->pdev->dev, |
| "Previous device scan failed or driver loading\n"); |
| adapter->access_ctrl.host_removing = 1; |
| rc = -EFAULT; |
| goto out_cleanup; |
| } |
| |
| rc = leapraid_make_adapter_available(adapter); |
| if (!rc) { |
| dev_info(&adapter->pdev->dev, |
| "Adapter is now available, rebuilding topology\n"); |
| if (adapter->adapter_attr.enable_mp) { |
| leapraid_update_card_port_after_reset(adapter); |
| leapraid_update_vphys_after_reset(adapter); |
| } |
| leapraid_mark_all_dev_deleted(adapter); |
| leapraid_rebuild_enc_list_after_reset(adapter); |
| leapraid_search_resp_sas_dev(adapter); |
| leapraid_search_resp_raid_volume(adapter); |
| leapraid_search_resp_exp(adapter); |
| leapraid_hardreset_barrier(adapter); |
| } |
| out_cleanup: |
| if (rc) |
| dev_err(&adapter->pdev->dev, "Hard reset failed\n"); |
| |
| spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags); |
| adapter->reset_desc.adapter_reset_results = rc; |
| adapter->access_ctrl.shost_recovering = 0; |
| wake_up(&adapter->access_ctrl.recovery_waitq); |
| if (rc) { |
| adapter->access_ctrl.shost_recover_async = 0; |
| wake = true; |
| } |
| spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags); |
| if (wake) |
| wake_up(&adapter->access_ctrl.shost_recover_wq); |
| |
| adapter->reset_desc.reset_cnt++; |
| mutex_unlock(&adapter->reset_desc.adapter_reset_mutex); |
| |
| if (rc) |
| leapraid_clean_active_scsi_cmds(adapter); |
| leapraid_mq_polling_resume(adapter); |
| |
| return rc; |
| } |
| |
| static int leapraid_get_adapter_features(struct leapraid_adapter *adapter) |
| { |
| struct leapraid_adapter_features_req leap_mpi_req; |
| struct leapraid_adapter_features_rep leap_mpi_rep; |
| u8 fw_major, fw_minor, fw_build, fw_release; |
| u32 req_sz; |
| u32 rep_sz; |
| u32 db; |
| int r; |
| |
| db = leapraid_readl(&adapter->iomem_base->db); |
| if (db & LEAPRAID_DB_USED || |
| (db & LEAPRAID_DB_MASK) == LEAPRAID_DB_FAULT) { |
| dev_err(&adapter->pdev->dev, |
| "%s: Doorbell used or fault\n", __func__); |
| return -EFAULT; |
| } |
| |
| if ((db & LEAPRAID_DB_MASK) != LEAPRAID_DB_READY && |
| (db & LEAPRAID_DB_MASK) != LEAPRAID_DB_OPERATIONAL && |
| !leapraid_wait_adapter_ready(adapter)) { |
| dev_err(&adapter->pdev->dev, |
| "%s: adapter not ready\n", __func__); |
| return -EFAULT; |
| } |
| |
| req_sz = sizeof(struct leapraid_adapter_features_req); |
| rep_sz = sizeof(struct leapraid_adapter_features_rep); |
| memset(&leap_mpi_req, 0, req_sz); |
| memset(&leap_mpi_rep, 0, rep_sz); |
| leap_mpi_req.func = LEAPRAID_FUNC_GET_ADAPTER_FEATURES; |
| r = leapraid_handshake_func(adapter, |
| req_sz, |
| (u32 *)&leap_mpi_req, |
| rep_sz, |
| (u16 *)&leap_mpi_rep); |
| if (r) { |
| dev_err(&adapter->pdev->dev, |
| "%s %s: Handshake failed, r=%d\n", |
| adapter->adapter_attr.name, __func__, r); |
| return r; |
| } |
| |
| memset(&adapter->adapter_attr.features, 0, |
| sizeof(struct leapraid_adapter_features)); |
| adapter->adapter_attr.features.req_slot = |
| le16_to_cpu(leap_mpi_rep.req_slot); |
| adapter->adapter_attr.features.hp_slot = |
| le16_to_cpu(leap_mpi_rep.hp_slot); |
| adapter->adapter_attr.features.adapter_caps = |
| le32_to_cpu(leap_mpi_rep.adapter_caps); |
| adapter->adapter_attr.features.max_msix_vectors = |
| leap_mpi_rep.max_msix_vectors; |
| adapter->adapter_attr.features.max_volumes = |
| leap_mpi_rep.max_volumes; |
| if (!adapter->adapter_attr.features.max_volumes) |
| adapter->adapter_attr.features.max_volumes = |
| LEAPRAID_MAX_VOLUMES_DEFAULT; |
| adapter->adapter_attr.features.max_dev_handle = |
| le16_to_cpu(leap_mpi_rep.max_dev_hdl); |
| if (!adapter->adapter_attr.features.max_dev_handle) |
| adapter->adapter_attr.features.max_dev_handle = |
| LEAPRAID_MAX_DEV_HANDLE_DEFAULT; |
| adapter->adapter_attr.features.min_dev_handle = |
| le16_to_cpu(leap_mpi_rep.min_dev_hdl); |
| if (adapter->adapter_attr.features.adapter_caps & |
| LEAPRAID_ADAPTER_FEATURES_CAP_INTEGRATED_RAID) |
| adapter->adapter_attr.raid_support = 1; |
| adapter->adapter_attr.wideport_max_queue_depth = |
| le16_to_cpu(leap_mpi_rep.sas_wide_max_qdepth) ? |
| le16_to_cpu(leap_mpi_rep.sas_wide_max_qdepth) : |
| LEAPRAID_SAS_QUEUE_DEPTH; |
| adapter->adapter_attr.narrowport_max_queue_depth = |
| le16_to_cpu(leap_mpi_rep.sas_narrow_max_qdepth) ? |
| le16_to_cpu(leap_mpi_rep.sas_narrow_max_qdepth) : |
| LEAPRAID_SAS_QUEUE_DEPTH; |
| adapter->adapter_attr.sata_max_queue_depth = |
| leap_mpi_rep.sata_max_qdepth ? |
| leap_mpi_rep.sata_max_qdepth : |
| LEAPRAID_SATA_QUEUE_DEPTH; |
| adapter->adapter_attr.raid_volume_max_queue_depth = |
| LEAPRAID_RAID_QUEUE_DEPTH; |
| dev_info(&adapter->pdev->dev, |
| "max: wp qd=%d, np qd=%d, SATA qd=%d, raid qd=%d\n", |
| adapter->adapter_attr.wideport_max_queue_depth, |
| adapter->adapter_attr.narrowport_max_queue_depth, |
| adapter->adapter_attr.sata_max_queue_depth, |
| adapter->adapter_attr.raid_volume_max_queue_depth); |
| if (WARN_ON(!(adapter->adapter_attr.features.adapter_caps & |
| LEAPRAID_ADAPTER_FEATURES_CAP_ATOMIC_REQ))) |
| return -EFAULT; |
| adapter->adapter_attr.features.fw_version = |
| le32_to_cpu(leap_mpi_rep.fw_version); |
| |
| fw_major = (adapter->adapter_attr.features.fw_version >> |
| LEAPRAID_VER_MAJOR_SHIFT) & LEAPRAID_VER_MASK; |
| fw_minor = (adapter->adapter_attr.features.fw_version >> |
| LEAPRAID_VER_MINOR_SHIFT) & LEAPRAID_VER_MASK; |
| fw_build = (adapter->adapter_attr.features.fw_version >> |
| LEAPRAID_VER_BUILD_SHIFT) & LEAPRAID_VER_MASK; |
| fw_release = |
| adapter->adapter_attr.features.fw_version & LEAPRAID_VER_MASK; |
| |
| dev_info(&adapter->pdev->dev, |
| "Firmware version: %u.%u.%u.%u (0x%08x)\n", |
| fw_major, fw_minor, fw_build, fw_release, |
| adapter->adapter_attr.features.fw_version); |
| |
| adapter->adapter_attr.features.msg_ver = |
| le16_to_cpu(leap_mpi_rep.msg_ver); |
| adapter->adapter_attr.features.product_id = |
| le16_to_cpu(leap_mpi_rep.product_id); |
| dev_info(&adapter->pdev->dev, |
| "message version: 0x%x, product id 0x%x\n", |
| adapter->adapter_attr.features.msg_ver, |
| adapter->adapter_attr.features.product_id); |
| |
| if (adapter->adapter_attr.features.msg_ver < 0x1000) { |
| dev_err(&adapter->pdev->dev, "Device not supported\n"); |
| return -EFAULT; |
| } |
| adapter->shost->max_id = LEAPRAID_INVALID_INITIAL_VALUE; |
| |
| return 0; |
| } |
| |
| static inline void leapraid_disable_pcie(struct leapraid_adapter *adapter) |
| { |
| mutex_lock(&adapter->access_ctrl.pci_access_lock); |
| if (adapter->iomem_base) { |
| iounmap(adapter->iomem_base); |
| adapter->iomem_base = NULL; |
| } |
| if (pci_is_enabled(adapter->pdev)) { |
| pci_release_regions(adapter->pdev); |
| pci_disable_device(adapter->pdev); |
| } |
| mutex_unlock(&adapter->access_ctrl.pci_access_lock); |
| } |
| |
| static int leapraid_enable_pcie(struct leapraid_adapter *adapter) |
| { |
| u64 dma_mask; |
| int rc; |
| |
| rc = pci_enable_device(adapter->pdev); |
| if (rc) { |
| dev_err(&adapter->pdev->dev, "Failed to enable PCI device\n"); |
| return rc; |
| } |
| |
| rc = pci_request_regions(adapter->pdev, LEAPRAID_DRIVER_NAME); |
| if (rc) { |
| dev_err(&adapter->pdev->dev, |
| "Failed to obtain PCI resources\n"); |
| return rc; |
| } |
| |
| if (sizeof(dma_addr_t) > 4) { |
| dma_mask = DMA_BIT_MASK(DMA_64_BITS); |
| adapter->adapter_attr.use_32_dma_mask = 0; |
| } else { |
| dma_mask = DMA_BIT_MASK(DMA_32_BITS); |
| adapter->adapter_attr.use_32_dma_mask = 1; |
| } |
| |
| rc = dma_set_mask_and_coherent(&adapter->pdev->dev, dma_mask); |
| if (rc) { |
| dev_err(&adapter->pdev->dev, |
| "Failed to set %lld DMA mask\n", dma_mask); |
| return rc; |
| } |
| adapter->iomem_base = ioremap(pci_resource_start(adapter->pdev, 0), |
| sizeof(struct leapraid_reg_base)); |
| if (!adapter->iomem_base) { |
| dev_err(&adapter->pdev->dev, |
| "Failed to map memory for controller registers\n"); |
| return -ENOMEM; |
| } |
| |
| pci_set_master(adapter->pdev); |
| |
| return 0; |
| } |
| |
| static void leapraid_cpus_on_irq(struct leapraid_adapter *adapter) |
| { |
| struct leapraid_int_rq *int_rq; |
| unsigned int i, base_group, this_group; |
| unsigned int cpu, nr_cpus, total_msix, index; |
| |
| total_msix = adapter->notification_desc.iopoll_qdex; |
| nr_cpus = num_online_cpus(); |
| |
| if (!nr_cpus || !total_msix) |
| return; |
| base_group = nr_cpus / total_msix; |
| |
| cpu = cpumask_first(cpu_online_mask); |
| for (index = 0; index < adapter->notification_desc.iopoll_qdex; |
| index++) { |
| int_rq = &adapter->notification_desc.int_rqs[index]; |
| |
| if (cpu >= adapter->notification_desc.msix_cpu_map_sz) |
| break; |
| |
| this_group = base_group + |
| (index < (nr_cpus % total_msix) ? 1 : 0); |
| |
| for (i = 0 ; i < this_group ; i++) { |
| if (cpu >= adapter->notification_desc.msix_cpu_map_sz) |
| break; |
| |
| adapter->notification_desc.msix_cpu_map[cpu] = |
| int_rq->rq.msix_idx; |
| cpu = cpumask_next(cpu, cpu_online_mask); |
| } |
| } |
| } |
| |
| static void leapraid_map_msix_to_cpu(struct leapraid_adapter *adapter) |
| { |
| struct leapraid_int_rq *int_rq; |
| const cpumask_t *affinity_mask; |
| int cpu; |
| u32 i; |
| |
| if (!adapter->adapter_attr.rq_cnt) |
| return; |
| |
| for (i = 0; i < adapter->notification_desc.iopoll_qdex; i++) { |
| int_rq = &adapter->notification_desc.int_rqs[i]; |
| affinity_mask = pci_irq_get_affinity(adapter->pdev, |
| int_rq->rq.msix_idx); |
| if (!affinity_mask) { |
| dev_warn(&adapter->pdev->dev, |
| "%s: IRQ affinity NULL, msix_idx=%d\n", |
| __func__, int_rq->rq.msix_idx); |
| goto out_apply_irq_affinity; |
| } |
| |
| for_each_cpu_and(cpu, affinity_mask, cpu_online_mask) { |
| if (cpu >= adapter->notification_desc.msix_cpu_map_sz) |
| continue; |
| |
| adapter->notification_desc.msix_cpu_map[cpu] = |
| int_rq->rq.msix_idx; |
| } |
| } |
| return; |
| out_apply_irq_affinity: |
| leapraid_cpus_on_irq(adapter); |
| } |
| |
| static int leapraid_alloc_msix_cpu_map(struct leapraid_adapter *adapter) |
| { |
| adapter->notification_desc.msix_cpu_map_sz = nr_cpu_ids; |
| adapter->notification_desc.msix_cpu_map = |
| kzalloc(adapter->notification_desc.msix_cpu_map_sz, |
| GFP_KERNEL); |
| if (!adapter->notification_desc.msix_cpu_map) |
| return -ENOMEM; |
| |
| return 0; |
| } |
| |
| static void leapraid_configure_reply_queue_affinity( |
| struct leapraid_adapter *adapter) |
| { |
| if (!adapter || !adapter->notification_desc.msix_enable) |
| return; |
| |
| leapraid_map_msix_to_cpu(adapter); |
| } |
| |
| static void leapraid_free_irq(struct leapraid_adapter *adapter) |
| { |
| struct leapraid_int_rq *int_rq; |
| unsigned int i; |
| int irq; |
| |
| for (i = 0; adapter->notification_desc.int_rqs && |
| i < adapter->notification_desc.int_rqs_allocated; i++) { |
| int_rq = &adapter->notification_desc.int_rqs[i]; |
| if (!int_rq) |
| continue; |
| |
| irq = pci_irq_vector(adapter->pdev, int_rq->rq.msix_idx); |
| irq_set_affinity_hint(irq, NULL); |
| free_irq(irq, &int_rq->rq); |
| } |
| adapter->notification_desc.int_rqs_allocated = 0; |
| |
| if (adapter->notification_desc.irq_vectors_allocated) { |
| pci_free_irq_vectors(adapter->pdev); |
| adapter->notification_desc.irq_vectors_allocated = 0; |
| } |
| |
| adapter->notification_desc.msix_enable = 0; |
| |
| kfree(adapter->notification_desc.blk_mq_poll_rqs); |
| adapter->notification_desc.blk_mq_poll_rqs = NULL; |
| |
| kfree(adapter->notification_desc.int_rqs); |
| adapter->notification_desc.int_rqs = NULL; |
| |
| kfree(adapter->notification_desc.msix_cpu_map); |
| adapter->notification_desc.msix_cpu_map = NULL; |
| adapter->notification_desc.msix_cpu_map_sz = 0; |
| adapter->notification_desc.iopoll_qdex = 0; |
| adapter->notification_desc.iopoll_qcnt = 0; |
| } |
| |
| static int leapraid_setup_irqs(struct leapraid_adapter *adapter) |
| { |
| int irq_mode = adapter->notification_desc.irq_mode; |
| unsigned int i; |
| int rc = 0; |
| |
| if (irq_mode == LEAPRAID_INTERRUPT_MODE_MSIX) { |
| rc = pci_alloc_irq_vectors_affinity( |
| adapter->pdev, |
| adapter->notification_desc.iopoll_qdex, |
| adapter->notification_desc.iopoll_qdex, |
| PCI_IRQ_MSIX | PCI_IRQ_AFFINITY, NULL); |
| if (rc < 0) { |
| dev_err(&adapter->pdev->dev, |
| "%d MSI/MSIX vectors allocated failed!\n", |
| adapter->notification_desc.iopoll_qdex); |
| return rc; |
| } |
| |
| adapter->notification_desc.irq_vectors_allocated = 1; |
| } |
| |
| for (i = 0; i < adapter->notification_desc.iopoll_qdex; i++) { |
| adapter->notification_desc.int_rqs[i].rq.adapter = adapter; |
| adapter->notification_desc.int_rqs[i].rq.msix_idx = i; |
| atomic_set(&adapter->notification_desc.int_rqs[i].rq.busy, 0); |
| if (irq_mode == LEAPRAID_INTERRUPT_MODE_MSIX) |
| snprintf(adapter->notification_desc.int_rqs[i].rq.name, |
| LEAPRAID_NAME_LENGTH, "%s%u-MSIx%u", |
| LEAPRAID_DRIVER_NAME, |
| adapter->adapter_attr.id, i); |
| else if (irq_mode == LEAPRAID_INTERRUPT_MODE_MSI) |
| snprintf(adapter->notification_desc.int_rqs[i].rq.name, |
| LEAPRAID_NAME_LENGTH, "%s%u-MSI%u", |
| LEAPRAID_DRIVER_NAME, |
| adapter->adapter_attr.id, i); |
| |
| rc = request_irq(pci_irq_vector(adapter->pdev, i), |
| leapraid_irq_handler, |
| IRQF_SHARED, |
| adapter->notification_desc.int_rqs[i].rq.name, |
| &adapter->notification_desc.int_rqs[i].rq); |
| if (rc) { |
| dev_err(&adapter->pdev->dev, |
| "MSI/MSIx: request_irq %s failed!\n", |
| adapter->notification_desc.int_rqs[i].rq.name); |
| return rc; |
| } |
| adapter->notification_desc.int_rqs_allocated++; |
| } |
| |
| return 0; |
| } |
| |
| static int leapraid_setup_legacy_int(struct leapraid_adapter *adapter) |
| { |
| int rc; |
| |
| adapter->notification_desc.int_rqs[0].rq.adapter = adapter; |
| adapter->notification_desc.int_rqs[0].rq.msix_idx = 0; |
| atomic_set(&adapter->notification_desc.int_rqs[0].rq.busy, 0); |
| snprintf(adapter->notification_desc.int_rqs[0].rq.name, |
| LEAPRAID_NAME_LENGTH, "%s%d-LegacyInt", |
| LEAPRAID_DRIVER_NAME, adapter->adapter_attr.id); |
| |
| rc = pci_alloc_irq_vectors_affinity( |
| adapter->pdev, |
| adapter->notification_desc.iopoll_qdex, |
| adapter->notification_desc.iopoll_qdex, |
| PCI_IRQ_INTX | PCI_IRQ_AFFINITY, |
| NULL); |
| if (rc < 0) { |
| dev_err(&adapter->pdev->dev, |
| "Legacy irq allocated failed!\n"); |
| return rc; |
| } |
| |
| adapter->notification_desc.irq_vectors_allocated = 1; |
| adapter->notification_desc.irq_mode = LEAPRAID_INTERRUPT_MODE_LEGACY; |
| rc = request_irq(pci_irq_vector(adapter->pdev, 0), |
| leapraid_irq_handler, |
| IRQF_SHARED, |
| adapter->notification_desc.int_rqs[0].rq.name, |
| &adapter->notification_desc.int_rqs[0].rq); |
| if (rc) { |
| irq_set_affinity_hint(pci_irq_vector(adapter->pdev, 0), NULL); |
| pci_free_irq_vectors(adapter->pdev); |
| adapter->notification_desc.irq_vectors_allocated = 0; |
| dev_err(&adapter->pdev->dev, |
| "Legacy Int: request_irq %s failed!\n", |
| adapter->notification_desc.int_rqs[0].rq.name); |
| return -EBUSY; |
| } |
| adapter->notification_desc.int_rqs_allocated = 1; |
| return rc; |
| } |
| |
| static int leapraid_set_legacy_int(struct leapraid_adapter *adapter) |
| { |
| int rc; |
| |
| rc = leapraid_alloc_msix_cpu_map(adapter); |
| if (rc) |
| return rc; |
| |
| adapter->adapter_attr.rq_cnt = 1; |
| adapter->notification_desc.iopoll_qdex = |
| adapter->adapter_attr.rq_cnt; |
| adapter->notification_desc.iopoll_qcnt = 0; |
| dev_info(&adapter->pdev->dev, |
| "Legacy Intr: req queue cnt=%d intr=%d/poll=%d rep queues!\n", |
| adapter->adapter_attr.rq_cnt, |
| adapter->notification_desc.iopoll_qdex, |
| adapter->notification_desc.iopoll_qcnt); |
| adapter->notification_desc.int_rqs = |
| kcalloc(adapter->notification_desc.iopoll_qdex, |
| sizeof(struct leapraid_int_rq), GFP_KERNEL); |
| if (!adapter->notification_desc.int_rqs) |
| return -ENOMEM; |
| |
| return leapraid_setup_legacy_int(adapter); |
| } |
| |
| static int leapraid_set_msix(struct leapraid_adapter *adapter) |
| { |
| int iopoll_qcnt = 0; |
| unsigned int i; |
| int rc, msix_cnt; |
| |
| msix_cnt = pci_msix_vec_count(adapter->pdev); |
| if (msix_cnt <= 0 || |
| adapter->adapter_attr.features.max_msix_vectors == 0) { |
| dev_info(&adapter->pdev->dev, "MSIX unsupported!\n"); |
| return -EOPNOTSUPP; |
| } |
| |
| msix_cnt = min_t(int, msix_cnt, |
| adapter->adapter_attr.features.max_msix_vectors); |
| |
| if (reset_devices) |
| adapter->adapter_attr.rq_cnt = 1; |
| else |
| adapter->adapter_attr.rq_cnt = min_t(int, |
| num_online_cpus(), |
| msix_cnt); |
| |
| if (max_msix_vectors > 0) |
| adapter->adapter_attr.rq_cnt = min_t( |
| int, max_msix_vectors, adapter->adapter_attr.rq_cnt); |
| |
| if (adapter->adapter_attr.rq_cnt <= 1) |
| adapter->shost->host_tagset = 0; |
| if (adapter->shost->host_tagset) { |
| iopoll_qcnt = poll_queues; |
| if (iopoll_qcnt >= adapter->adapter_attr.rq_cnt) |
| iopoll_qcnt = 0; |
| } |
| if (iopoll_qcnt) { |
| adapter->notification_desc.blk_mq_poll_rqs = |
| kcalloc(iopoll_qcnt, |
| sizeof(struct leapraid_blk_mq_poll_rq), |
| GFP_KERNEL); |
| if (!adapter->notification_desc.blk_mq_poll_rqs) |
| return -ENOMEM; |
| adapter->adapter_attr.rq_cnt = |
| min(adapter->adapter_attr.rq_cnt + iopoll_qcnt, |
| msix_cnt); |
| } |
| |
| adapter->notification_desc.iopoll_qdex = |
| adapter->adapter_attr.rq_cnt - iopoll_qcnt; |
| |
| adapter->notification_desc.iopoll_qcnt = iopoll_qcnt; |
| dev_info(&adapter->pdev->dev, |
| "MSIx: req queue cnt=%d, intr=%d/poll=%d rep queues!\n", |
| adapter->adapter_attr.rq_cnt, |
| adapter->notification_desc.iopoll_qdex, |
| adapter->notification_desc.iopoll_qcnt); |
| |
| adapter->notification_desc.int_rqs = |
| kcalloc(adapter->notification_desc.iopoll_qdex, |
| sizeof(struct leapraid_int_rq), GFP_KERNEL); |
| if (!adapter->notification_desc.int_rqs) |
| return -ENOMEM; |
| |
| for (i = 0; i < adapter->notification_desc.iopoll_qcnt; i++) { |
| adapter->notification_desc.blk_mq_poll_rqs[i].rq.adapter = |
| adapter; |
| adapter->notification_desc.blk_mq_poll_rqs[i].rq.msix_idx = |
| i + adapter->notification_desc.iopoll_qdex; |
| atomic_set( |
| &adapter->notification_desc.blk_mq_poll_rqs[i].rq.busy, |
| 0); |
| snprintf(adapter->notification_desc.blk_mq_poll_rqs[i].rq.name, |
| LEAPRAID_NAME_LENGTH, |
| "%s%u-MQ-Poll%u", LEAPRAID_DRIVER_NAME, |
| adapter->adapter_attr.id, i); |
| atomic_set(&adapter->notification_desc.blk_mq_poll_rqs[i].busy, |
| 0); |
| atomic_set(&adapter->notification_desc.blk_mq_poll_rqs[i].pause, |
| 0); |
| } |
| |
| rc = leapraid_alloc_msix_cpu_map(adapter); |
| if (rc) |
| return rc; |
| |
| adapter->notification_desc.irq_mode = LEAPRAID_INTERRUPT_MODE_MSIX; |
| adapter->notification_desc.msix_enable = 1; |
| rc = leapraid_setup_irqs(adapter); |
| if (rc) { |
| leapraid_free_irq(adapter); |
| adapter->notification_desc.msix_enable = 0; |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| static int leapraid_set_msi(struct leapraid_adapter *adapter) |
| { |
| int iopoll_qcnt = 0; |
| unsigned int i; |
| int rc, msi_cnt; |
| |
| msi_cnt = pci_msi_vec_count(adapter->pdev); |
| if (msi_cnt <= 0 || |
| adapter->adapter_attr.features.max_msix_vectors == 0) { |
| dev_info(&adapter->pdev->dev, "MSI unsupported!\n"); |
| return -EOPNOTSUPP; |
| } |
| |
| msi_cnt = min_t(int, msi_cnt, |
| adapter->adapter_attr.features.max_msix_vectors); |
| |
| if (reset_devices) |
| adapter->adapter_attr.rq_cnt = 1; |
| else |
| adapter->adapter_attr.rq_cnt = min_t(int, |
| num_online_cpus(), |
| msi_cnt); |
| |
| if (max_msix_vectors > 0) |
| adapter->adapter_attr.rq_cnt = min_t( |
| int, max_msix_vectors, adapter->adapter_attr.rq_cnt); |
| |
| if (adapter->adapter_attr.rq_cnt <= 1) |
| adapter->shost->host_tagset = 0; |
| if (adapter->shost->host_tagset) { |
| iopoll_qcnt = poll_queues; |
| if (iopoll_qcnt >= adapter->adapter_attr.rq_cnt) |
| iopoll_qcnt = 0; |
| } |
| |
| if (iopoll_qcnt) { |
| adapter->notification_desc.blk_mq_poll_rqs = |
| kcalloc(iopoll_qcnt, |
| sizeof(struct leapraid_blk_mq_poll_rq), |
| GFP_KERNEL); |
| if (!adapter->notification_desc.blk_mq_poll_rqs) |
| return -ENOMEM; |
| |
| adapter->adapter_attr.rq_cnt = |
| min(adapter->adapter_attr.rq_cnt + iopoll_qcnt, |
| msi_cnt); |
| } |
| |
| adapter->notification_desc.iopoll_qdex = |
| adapter->adapter_attr.rq_cnt - iopoll_qcnt; |
| rc = pci_alloc_irq_vectors_affinity( |
| adapter->pdev, |
| 1, |
| adapter->notification_desc.iopoll_qdex, |
| PCI_IRQ_MSI | PCI_IRQ_AFFINITY, NULL); |
| if (rc < 0) { |
| dev_err(&adapter->pdev->dev, |
| "%d MSI vectors allocated failed!\n", |
| adapter->notification_desc.iopoll_qdex); |
| leapraid_free_irq(adapter); |
| return rc; |
| } |
| adapter->notification_desc.irq_vectors_allocated = 1; |
| if (rc != adapter->notification_desc.iopoll_qdex) { |
| adapter->notification_desc.iopoll_qdex = rc; |
| adapter->adapter_attr.rq_cnt = |
| adapter->notification_desc.iopoll_qdex + iopoll_qcnt; |
| } |
| adapter->notification_desc.iopoll_qcnt = iopoll_qcnt; |
| dev_info(&adapter->pdev->dev, |
| "MSI: req queue cnt=%d, intr=%d/poll=%d rep queues!\n", |
| adapter->adapter_attr.rq_cnt, |
| adapter->notification_desc.iopoll_qdex, |
| adapter->notification_desc.iopoll_qcnt); |
| |
| adapter->notification_desc.int_rqs = |
| kcalloc(adapter->notification_desc.iopoll_qdex, |
| sizeof(struct leapraid_int_rq), |
| GFP_KERNEL); |
| if (!adapter->notification_desc.int_rqs) |
| return -ENOMEM; |
| |
| for (i = 0; i < adapter->notification_desc.iopoll_qcnt; i++) { |
| adapter->notification_desc.blk_mq_poll_rqs[i].rq.adapter = |
| adapter; |
| adapter->notification_desc.blk_mq_poll_rqs[i].rq.msix_idx = |
| i + adapter->notification_desc.iopoll_qdex; |
| atomic_set( |
| &adapter->notification_desc.blk_mq_poll_rqs[i].rq.busy, |
| 0); |
| snprintf(adapter->notification_desc.blk_mq_poll_rqs[i].rq.name, |
| LEAPRAID_NAME_LENGTH, |
| "%s%u-MQ-Poll%u", LEAPRAID_DRIVER_NAME, |
| adapter->adapter_attr.id, i); |
| atomic_set( |
| &adapter->notification_desc.blk_mq_poll_rqs[i].busy, |
| 0); |
| atomic_set( |
| &adapter->notification_desc.blk_mq_poll_rqs[i].pause, |
| 0); |
| } |
| |
| rc = leapraid_alloc_msix_cpu_map(adapter); |
| if (rc) |
| return rc; |
| |
| adapter->notification_desc.irq_mode = LEAPRAID_INTERRUPT_MODE_MSI; |
| adapter->notification_desc.msix_enable = 1; |
| rc = leapraid_setup_irqs(adapter); |
| if (rc) { |
| leapraid_free_irq(adapter); |
| adapter->notification_desc.msix_enable = 0; |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| static int leapraid_set_notification_auto(struct leapraid_adapter *adapter) |
| { |
| int rc; |
| |
| rc = leapraid_set_msix(adapter); |
| if (!rc) |
| return 0; |
| |
| leapraid_free_irq(adapter); |
| dev_info(&adapter->pdev->dev, |
| "MSI-X setup failed (%d), trying MSI\n", rc); |
| |
| rc = leapraid_set_msi(adapter); |
| if (!rc) |
| return 0; |
| |
| leapraid_free_irq(adapter); |
| dev_info(&adapter->pdev->dev, |
| "MSI setup failed (%d), back to legacy INTx\n", rc); |
| |
| rc = leapraid_set_legacy_int(adapter); |
| if (rc) |
| dev_err(&adapter->pdev->dev, |
| "%s: Enable legacy irq failed!\n", __func__); |
| |
| return rc; |
| } |
| |
| int leapraid_set_pcie_and_notification(struct leapraid_adapter *adapter) |
| { |
| int rc; |
| |
| rc = leapraid_enable_pcie(adapter); |
| if (rc) |
| goto out_fail; |
| |
| leapraid_mask_int(adapter); |
| |
| rc = leapraid_make_adapter_ready(adapter, PART_RESET); |
| if (rc) { |
| dev_err(&adapter->pdev->dev, "Make adapter ready failure\n"); |
| goto out_fail; |
| } |
| |
| rc = leapraid_get_adapter_features(adapter); |
| if (rc) { |
| dev_err(&adapter->pdev->dev, "Get adapter feature failure\n"); |
| goto out_fail; |
| } |
| |
| rc = leapraid_set_notification_auto(adapter); |
| if (rc) |
| goto out_fail; |
| |
| pci_save_state(adapter->pdev); |
| |
| return 0; |
| |
| out_fail: |
| leapraid_free_irq(adapter); |
| leapraid_disable_pcie(adapter); |
| return rc; |
| } |
| |
| void leapraid_disable_controller(struct leapraid_adapter *adapter) |
| { |
| if (!adapter->iomem_base) |
| return; |
| |
| leapraid_mask_int(adapter); |
| |
| adapter->access_ctrl.shost_recovering = 1; |
| leapraid_make_adapter_ready(adapter, PART_RESET); |
| adapter->access_ctrl.shost_recovering = 0; |
| wake_up(&adapter->access_ctrl.recovery_waitq); |
| |
| leapraid_free_irq(adapter); |
| leapraid_disable_pcie(adapter); |
| } |
| |
| static int leapraid_adapter_unit_reset(struct leapraid_adapter *adapter) |
| { |
| int rc = 0; |
| |
| writel(LEAPRAID_FUNC_ADAPTER_UNIT_RESET << LEAPRAID_DB_FUNC_SHIFT, |
| &adapter->iomem_base->db); |
| if (leapraid_db_wait_ack_and_clear_int(adapter)) |
| rc = -EFAULT; |
| |
| if (!leapraid_wait_adapter_ready(adapter)) { |
| dev_err(&adapter->pdev->dev, "unit reset failed\n"); |
| return -EFAULT; |
| } |
| |
| return rc; |
| } |
| |
| static int leapraid_make_adapter_ready(struct leapraid_adapter *adapter, |
| enum reset_type type) |
| { |
| u32 db; |
| int count; |
| |
| if (!leapraid_pci_active(adapter)) |
| return 0; |
| |
| count = 0; |
| db = leapraid_readl(&adapter->iomem_base->db); |
| if ((db & LEAPRAID_DB_MASK) == LEAPRAID_DB_RESET) { |
| while ((db & LEAPRAID_DB_MASK) != LEAPRAID_DB_READY) { |
| if (count++ == LEAPRAID_DB_RETRY_COUNT_MAX) { |
| dev_err(&adapter->pdev->dev, |
| "Wait adapter ready timeout\n"); |
| return -EFAULT; |
| } |
| ssleep(1); |
| db = leapraid_readl(&adapter->iomem_base->db); |
| dev_info(&adapter->pdev->dev, |
| "Wait adapter ready, count=%d, db=0x%x\n", |
| count, db); |
| } |
| } |
| if ((db & LEAPRAID_DB_MASK) == LEAPRAID_DB_READY) |
| return 0; |
| |
| if (db & LEAPRAID_DB_USED) |
| goto full_reset; |
| |
| if ((db & LEAPRAID_DB_MASK) == LEAPRAID_DB_FAULT) |
| goto full_reset; |
| |
| if (type == FULL_RESET) |
| goto full_reset; |
| |
| if ((db & LEAPRAID_DB_MASK) == LEAPRAID_DB_OPERATIONAL && |
| !leapraid_adapter_unit_reset(adapter)) |
| return 0; |
| |
| full_reset: |
| return leapraid_host_diag_reset(adapter); |
| } |
| |
| static void leapraid_fw_log_exit(struct leapraid_adapter *adapter) |
| { |
| if (!adapter->fw_log_desc.open_pcie_trace) |
| return; |
| |
| if (adapter->fw_log_desc.fw_log_buffer) { |
| wait_event(adapter->fw_log_desc.mmap_waitq, |
| !atomic_read(&adapter->fw_log_desc.mmap_refcnt)); |
| dma_free_coherent(&adapter->pdev->dev, |
| (LEAPRAID_SYS_LOG_BUF_SIZE + |
| LEAPRAID_SYS_LOG_BUF_RESERVE), |
| adapter->fw_log_desc.fw_log_buffer, |
| adapter->fw_log_desc.fw_log_buffer_dma); |
| adapter->fw_log_desc.fw_log_buffer = NULL; |
| } |
| } |
| |
| static int leapraid_fw_log_init(struct leapraid_adapter *adapter) |
| { |
| struct leapraid_adapter_log_req adapter_log_req; |
| struct leapraid_adapter_log_rep adapter_log_rep; |
| u16 adapter_status; |
| u64 buf_addr; |
| u32 rc; |
| |
| if (!adapter->fw_log_desc.open_pcie_trace) |
| return 0; |
| |
| if (!adapter->fw_log_desc.fw_log_buffer) { |
| adapter->fw_log_desc.fw_log_buffer = |
| dma_alloc_coherent( |
| &adapter->pdev->dev, |
| (LEAPRAID_SYS_LOG_BUF_SIZE + |
| LEAPRAID_SYS_LOG_BUF_RESERVE), |
| &adapter->fw_log_desc.fw_log_buffer_dma, |
| GFP_KERNEL); |
| if (!adapter->fw_log_desc.fw_log_buffer) |
| return -ENOMEM; |
| } |
| |
| memset(&adapter_log_req, 0, sizeof(struct leapraid_adapter_log_req)); |
| adapter_log_req.func = LEAPRAID_FUNC_LOGBUF_INIT; |
| buf_addr = adapter->fw_log_desc.fw_log_buffer_dma; |
| |
| adapter_log_req.mbox.w[0] = |
| cpu_to_le32((u32)(buf_addr & 0xFFFFFFFF)); |
| adapter_log_req.mbox.w[1] = |
| cpu_to_le32((u32)((buf_addr >> 32) & 0xFFFFFFFF)); |
| adapter_log_req.mbox.w[2] = |
| cpu_to_le32(LEAPRAID_SYS_LOG_BUF_SIZE); |
| rc = leapraid_handshake_func(adapter, |
| sizeof(struct leapraid_adapter_log_req), |
| (u32 *)&adapter_log_req, |
| sizeof(struct leapraid_adapter_log_rep), |
| (u16 *)&adapter_log_rep); |
| if (rc != 0) { |
| dev_err(&adapter->pdev->dev, "%s: Handshake failed, rc=%d\n", |
| __func__, rc); |
| return rc; |
| } |
| |
| adapter_status = le16_to_cpu(adapter_log_rep.adapter_status) & |
| LEAPRAID_ADAPTER_STATUS_MASK; |
| if (adapter_status != LEAPRAID_ADAPTER_STATUS_SUCCESS) { |
| dev_err(&adapter->pdev->dev, "%s: failed!\n", __func__); |
| rc = -EIO; |
| } |
| |
| return rc; |
| } |
| |
| static void leapraid_free_host_memory(struct leapraid_adapter *adapter) |
| { |
| unsigned int i; |
| |
| if (adapter->mem_desc.task_desc) { |
| dma_free_coherent(&adapter->pdev->dev, |
| adapter->adapter_attr.task_desc_dma_size, |
| adapter->mem_desc.task_desc, |
| adapter->mem_desc.task_desc_dma); |
| adapter->mem_desc.task_desc = NULL; |
| } |
| |
| if (adapter->mem_desc.sense_data) { |
| dma_free_coherent( |
| &adapter->pdev->dev, |
| adapter->adapter_attr.io_qd * SCSI_SENSE_BUFFERSIZE, |
| adapter->mem_desc.sense_data, |
| adapter->mem_desc.sense_data_dma); |
| adapter->mem_desc.sense_data = NULL; |
| } |
| |
| if (adapter->mem_desc.rep_msg) { |
| dma_free_coherent( |
| &adapter->pdev->dev, |
| adapter->adapter_attr.rep_msg_qd * LEAPRAID_REPLY_SIZE, |
| adapter->mem_desc.rep_msg, |
| adapter->mem_desc.rep_msg_dma); |
| adapter->mem_desc.rep_msg = NULL; |
| } |
| |
| if (adapter->mem_desc.rep_msg_addr) { |
| dma_free_coherent(&adapter->pdev->dev, |
| adapter->adapter_attr.rep_msg_qd * |
| LEAPRAID_REP_MSG_ADDR_SIZE, |
| adapter->mem_desc.rep_msg_addr, |
| adapter->mem_desc.rep_msg_addr_dma); |
| adapter->mem_desc.rep_msg_addr = NULL; |
| } |
| |
| if (adapter->mem_desc.rep_desc_seg_maint) { |
| for (i = 0; i < adapter->adapter_attr.rep_desc_q_seg_cnt; |
| i++) { |
| if (adapter->mem_desc.rep_desc_seg_maint[i] |
| .rep_desc_seg) { |
| dma_free_coherent( |
| &adapter->pdev->dev, |
| (adapter->adapter_attr.rep_desc_qd * |
| LEAPRAID_REP_DESC_ENTRY_SIZE) * |
| LEAPRAID_REP_DESC_CHUNK_SIZE, |
| adapter->mem_desc.rep_desc_seg_maint[i] |
| .rep_desc_seg, |
| adapter->mem_desc.rep_desc_seg_maint[i] |
| .rep_desc_seg_dma); |
| adapter->mem_desc.rep_desc_seg_maint[i] |
| .rep_desc_seg = NULL; |
| } |
| } |
| |
| if (adapter->mem_desc.rep_desc_q_arr) { |
| dma_free_coherent( |
| &adapter->pdev->dev, |
| adapter->adapter_attr.rq_cnt * |
| LEAPRAID_REP_RQ_CNT_SIZE, |
| adapter->mem_desc.rep_desc_q_arr, |
| adapter->mem_desc.rep_desc_q_arr_dma); |
| adapter->mem_desc.rep_desc_q_arr = NULL; |
| } |
| |
| for (i = 0; i < adapter->adapter_attr.rep_desc_q_seg_cnt; i++) { |
| struct leapraid_mem_desc *mem_desc = |
| &adapter->mem_desc; |
| kfree(adapter->mem_desc.rep_desc_seg_maint[i] |
| .rep_desc_maint); |
| mem_desc->rep_desc_seg_maint[i].rep_desc_maint = NULL; |
| } |
| kfree(adapter->mem_desc.rep_desc_seg_maint); |
| adapter->mem_desc.rep_desc_seg_maint = NULL; |
| } |
| |
| kfree(adapter->mem_desc.taskid_to_uniq_tag); |
| adapter->mem_desc.taskid_to_uniq_tag = NULL; |
| |
| dma_pool_destroy(adapter->mem_desc.sg_chain_pool); |
| adapter->mem_desc.sg_chain_pool = NULL; |
| } |
| |
| static inline bool leapraid_is_in_same_4g_seg(dma_addr_t start, u32 size) |
| { |
| return upper_32_bits(start) == upper_32_bits(start + size - 1); |
| } |
| |
| int leapraid_internal_init_cmd_priv(struct leapraid_adapter *adapter, |
| struct leapraid_io_req_tracker *io_tracker) |
| { |
| io_tracker->chain = |
| dma_pool_alloc(adapter->mem_desc.sg_chain_pool, |
| GFP_KERNEL, |
| &io_tracker->chain_dma); |
| |
| if (!io_tracker->chain) |
| return -ENOMEM; |
| |
| return 0; |
| } |
| |
| void leapraid_internal_exit_cmd_priv(struct leapraid_adapter *adapter, |
| struct leapraid_io_req_tracker *io_tracker) |
| { |
| if (io_tracker && io_tracker->chain) |
| dma_pool_free(adapter->mem_desc.sg_chain_pool, |
| io_tracker->chain, |
| io_tracker->chain_dma); |
| } |
| |
| static int leapraid_request_host_memory(struct leapraid_adapter *adapter) |
| { |
| struct leapraid_adapter_features *facts = |
| &adapter->adapter_attr.features; |
| u16 rep_desc_q_cnt_allocated; |
| unsigned int i, j; |
| int rc; |
| |
| /* Scatter-gather table size. */ |
| adapter->shost->sg_tablesize = LEAPRAID_SG_DEPTH; |
| if (reset_devices) |
| adapter->shost->sg_tablesize = |
| LEAPRAID_KDUMP_MIN_PHYS_SEGMENTS; |
| /* High-priority commands queue depth. */ |
| adapter->dynamic_task_desc.hp_cmd_qd = LEAPRAID_FIXED_HP_CMDS; |
| /* Internal commands queue depth. */ |
| adapter->dynamic_task_desc.inter_cmd_qd = LEAPRAID_FIXED_INTER_CMDS; |
| /* Adapter commands total queue depth. */ |
| if (reset_devices) |
| adapter->adapter_attr.adapter_total_qd = |
| LEAPRAID_DEFAULT_CMD_QD_OFFSET + |
| adapter->dynamic_task_desc.inter_cmd_qd + |
| adapter->dynamic_task_desc.hp_cmd_qd; |
| else |
| adapter->adapter_attr.adapter_total_qd = facts->req_slot + |
| adapter->dynamic_task_desc.hp_cmd_qd; |
| /* Reply message queue depth. */ |
| adapter->adapter_attr.rep_msg_qd = |
| adapter->adapter_attr.adapter_total_qd + |
| LEAPRAID_DEFAULT_CMD_QD_OFFSET; |
| /* Reply descriptor queue depth. */ |
| adapter->adapter_attr.rep_desc_qd = |
| round_up(adapter->adapter_attr.adapter_total_qd + |
| adapter->adapter_attr.rep_msg_qd + |
| LEAPRAID_TASKID_OFFSET_CTRL_CMD, |
| LEAPRAID_REPLY_QD_ALIGNMENT); |
| /* SCSI command I/O depth. */ |
| adapter->adapter_attr.io_qd = |
| adapter->adapter_attr.adapter_total_qd - |
| adapter->dynamic_task_desc.hp_cmd_qd - |
| adapter->dynamic_task_desc.inter_cmd_qd; |
| /* SCSI host can queue. */ |
| adapter->shost->can_queue = adapter->adapter_attr.io_qd - |
| LEAPRAID_TASKID_OFFSET_CTRL_CMD; |
| adapter->driver_cmds.ctl_cmd.taskid = adapter->shost->can_queue + |
| LEAPRAID_TASKID_OFFSET_CTRL_CMD; |
| |
| /* Allocate task descriptor. */ |
| try_again: |
| adapter->adapter_attr.task_desc_dma_size = |
| (adapter->adapter_attr.adapter_total_qd + |
| LEAPRAID_TASKID_OFFSET_CTRL_CMD) * |
| LEAPRAID_REQUEST_SIZE; |
| adapter->mem_desc.task_desc = |
| dma_alloc_coherent(&adapter->pdev->dev, |
| adapter->adapter_attr.task_desc_dma_size, |
| &adapter->mem_desc.task_desc_dma, |
| GFP_KERNEL); |
| if (!adapter->mem_desc.task_desc) |
| return -ENOMEM; |
| |
| /* Allocate chain message pool. */ |
| adapter->mem_desc.sg_chain_pool_size = |
| LEAPRAID_DEFAULT_CHAINS_PER_IO * LEAPRAID_CHAIN_SEG_SIZE; |
| adapter->mem_desc.sg_chain_pool = |
| dma_pool_create("leapraid chain pool", |
| &adapter->pdev->dev, |
| adapter->mem_desc.sg_chain_pool_size, |
| LEAPRAID_DMA_ALIGN, 0); |
| if (!adapter->mem_desc.sg_chain_pool) |
| return -ENOMEM; |
| |
| /* Allocate I/O tracker to SCSI I/O. */ |
| adapter->mem_desc.taskid_to_uniq_tag = |
| kcalloc(adapter->shost->can_queue, sizeof(u16), GFP_KERNEL); |
| if (!adapter->mem_desc.taskid_to_uniq_tag) |
| return -ENOMEM; |
| |
| adapter->dynamic_task_desc.hp_taskid = |
| adapter->adapter_attr.io_qd + |
| LEAPRAID_HP_TASKID_OFFSET_CTL_CMD; |
| /* Allocate static high-priority task ID. */ |
| adapter->driver_cmds.ctl_cmd.hp_taskid = |
| adapter->dynamic_task_desc.hp_taskid; |
| adapter->driver_cmds.tm_cmd.hp_taskid = |
| adapter->dynamic_task_desc.hp_taskid + |
| LEAPRAID_HP_TASKID_OFFSET_TM_CMD; |
| |
| adapter->dynamic_task_desc.inter_taskid = |
| adapter->dynamic_task_desc.hp_taskid + |
| adapter->dynamic_task_desc.hp_cmd_qd; |
| adapter->driver_cmds.scan_dev_cmd.inter_taskid = |
| adapter->dynamic_task_desc.inter_taskid; |
| adapter->driver_cmds.cfg_op_cmd.inter_taskid = |
| adapter->dynamic_task_desc.inter_taskid + |
| LEAPRAID_TASKID_OFFSET_CFG_OP_CMD; |
| adapter->driver_cmds.transport_cmd.inter_taskid = |
| adapter->dynamic_task_desc.inter_taskid + |
| LEAPRAID_TASKID_OFFSET_TRANSPORT_CMD; |
| adapter->driver_cmds.enc_cmd.inter_taskid = |
| adapter->dynamic_task_desc.inter_taskid + |
| LEAPRAID_TASKID_OFFSET_ENC_CMD; |
| adapter->driver_cmds.notify_event_cmd.inter_taskid = |
| adapter->dynamic_task_desc.inter_taskid + |
| LEAPRAID_TASKID_OFFSET_NOTIFY_EVENT_CMD; |
| dev_info(&adapter->pdev->dev, "queue depth:\n"); |
| dev_info(&adapter->pdev->dev, " host->can_queue: %d\n", |
| adapter->shost->can_queue); |
| dev_info(&adapter->pdev->dev, " io_qd: %d\n", |
| adapter->adapter_attr.io_qd); |
| dev_info(&adapter->pdev->dev, " hpr_cmd_qd: %d\n", |
| adapter->dynamic_task_desc.hp_cmd_qd); |
| dev_info(&adapter->pdev->dev, " inter_cmd_qd: %d\n", |
| adapter->dynamic_task_desc.inter_cmd_qd); |
| dev_info(&adapter->pdev->dev, " adapter_total_qd: %d\n", |
| adapter->adapter_attr.adapter_total_qd); |
| |
| dev_info(&adapter->pdev->dev, "taskid range:\n"); |
| dev_info(&adapter->pdev->dev, |
| " adapter->dynamic_task_desc.hp_taskid: %d\n", |
| adapter->dynamic_task_desc.hp_taskid); |
| dev_info(&adapter->pdev->dev, |
| " adapter->dynamic_task_desc.inter_taskid: %d\n", |
| adapter->dynamic_task_desc.inter_taskid); |
| |
| /* |
| * Allocate sense data DMA buffer. |
| * Constraint: Must reside within the same 4GB segment |
| * (driver-maintained). |
| */ |
| adapter->mem_desc.sense_data = |
| dma_alloc_coherent( |
| &adapter->pdev->dev, |
| adapter->adapter_attr.io_qd * SCSI_SENSE_BUFFERSIZE, |
| &adapter->mem_desc.sense_data_dma, |
| GFP_KERNEL); |
| if (!adapter->mem_desc.sense_data) |
| return -ENOMEM; |
| |
| if (!leapraid_is_in_same_4g_seg(adapter->mem_desc.sense_data_dma, |
| adapter->adapter_attr.io_qd * |
| SCSI_SENSE_BUFFERSIZE)) { |
| dev_warn(&adapter->pdev->dev, |
| "Try 32bit DMA: Sense not in same 4G\n"); |
| rc = -EAGAIN; |
| goto out_fail; |
| } |
| |
| /* |
| * Allocate reply frame buffer. |
| * Constraint: Must reside in the same 4GB segment as the sense DMA. |
| */ |
| adapter->mem_desc.rep_msg = |
| dma_alloc_coherent(&adapter->pdev->dev, |
| adapter->adapter_attr.rep_msg_qd * |
| LEAPRAID_REPLY_SIZE, |
| &adapter->mem_desc.rep_msg_dma, |
| GFP_KERNEL); |
| if (!adapter->mem_desc.rep_msg) { |
| rc = -ENOMEM; |
| goto out_fail; |
| } |
| |
| if (!leapraid_is_in_same_4g_seg(adapter->mem_desc.rep_msg_dma, |
| adapter->adapter_attr.rep_msg_qd * |
| LEAPRAID_REPLY_SIZE)) { |
| dev_warn(&adapter->pdev->dev, |
| "Use 32 bit DMA due to rep msg is not in same 4g!\n"); |
| rc = -EAGAIN; |
| goto out_fail; |
| } |
| |
| /* Address of reply frame. */ |
| adapter->mem_desc.rep_msg_addr = |
| dma_alloc_coherent(&adapter->pdev->dev, |
| adapter->adapter_attr.rep_msg_qd * |
| LEAPRAID_REP_MSG_ADDR_SIZE, |
| &adapter->mem_desc.rep_msg_addr_dma, |
| GFP_KERNEL); |
| if (!adapter->mem_desc.rep_msg_addr) |
| return -ENOMEM; |
| adapter->adapter_attr.rep_desc_q_seg_cnt = |
| DIV_ROUND_UP(adapter->adapter_attr.rq_cnt, |
| LEAPRAID_REP_DESC_CHUNK_SIZE); |
| adapter->mem_desc.rep_desc_seg_maint = |
| kcalloc(adapter->adapter_attr.rep_desc_q_seg_cnt, |
| sizeof(struct leapraid_rep_desc_seg_maint), |
| GFP_KERNEL); |
| if (!adapter->mem_desc.rep_desc_seg_maint) |
| return -ENOMEM; |
| |
| rep_desc_q_cnt_allocated = 0; |
| for (i = 0; i < adapter->adapter_attr.rep_desc_q_seg_cnt; i++) { |
| adapter->mem_desc.rep_desc_seg_maint[i].rep_desc_maint = |
| kcalloc(LEAPRAID_REP_DESC_CHUNK_SIZE, |
| sizeof(struct leapraid_rep_desc_maint), |
| GFP_KERNEL); |
| if (!adapter->mem_desc.rep_desc_seg_maint[i].rep_desc_maint) |
| return -ENOMEM; |
| |
| adapter->mem_desc.rep_desc_seg_maint[i].rep_desc_seg = |
| dma_alloc_coherent( |
| &adapter->pdev->dev, |
| (adapter->adapter_attr.rep_desc_qd * |
| LEAPRAID_REP_DESC_ENTRY_SIZE) * |
| LEAPRAID_REP_DESC_CHUNK_SIZE, |
| &adapter->mem_desc.rep_desc_seg_maint[i] |
| .rep_desc_seg_dma, |
| GFP_KERNEL); |
| if (!adapter->mem_desc.rep_desc_seg_maint[i].rep_desc_seg) |
| return -ENOMEM; |
| |
| for (j = 0; j < LEAPRAID_REP_DESC_CHUNK_SIZE; j++) { |
| if (rep_desc_q_cnt_allocated >= |
| adapter->adapter_attr.rq_cnt) |
| break; |
| adapter->mem_desc |
| .rep_desc_seg_maint[i] |
| .rep_desc_maint[j] |
| .rep_desc = |
| (void *)((u8 *)( |
| adapter->mem_desc |
| .rep_desc_seg_maint[i] |
| .rep_desc_seg) + |
| j * |
| (adapter->adapter_attr.rep_desc_qd * |
| LEAPRAID_REP_DESC_ENTRY_SIZE)); |
| adapter->mem_desc |
| .rep_desc_seg_maint[i] |
| .rep_desc_maint[j] |
| .rep_desc_dma = |
| adapter->mem_desc |
| .rep_desc_seg_maint[i] |
| .rep_desc_seg_dma + |
| j * |
| (adapter->adapter_attr.rep_desc_qd * |
| LEAPRAID_REP_DESC_ENTRY_SIZE); |
| rep_desc_q_cnt_allocated++; |
| } |
| } |
| |
| if (!reset_devices) { |
| adapter->mem_desc.rep_desc_q_arr = |
| dma_alloc_coherent( |
| &adapter->pdev->dev, |
| adapter->adapter_attr.rq_cnt * |
| LEAPRAID_REP_RQ_CNT_SIZE, |
| &adapter->mem_desc.rep_desc_q_arr_dma, |
| GFP_KERNEL); |
| if (!adapter->mem_desc.rep_desc_q_arr) |
| return -ENOMEM; |
| } |
| |
| return 0; |
| out_fail: |
| if (rc == -EAGAIN) { |
| leapraid_free_host_memory(adapter); |
| adapter->adapter_attr.use_32_dma_mask = 1; |
| rc = dma_set_mask_and_coherent(&adapter->pdev->dev, |
| DMA_BIT_MASK(DMA_32_BITS)); |
| if (rc) { |
| dev_err(&adapter->pdev->dev, |
| "Failed to set 32 DMA mask\n"); |
| return rc; |
| } |
| goto try_again; |
| } |
| return rc; |
| } |
| |
| static int leapraid_alloc_dev_topo_bitmaps(struct leapraid_adapter *adapter) |
| { |
| u16 pd_hdls_sz; |
| |
| pd_hdls_sz = |
| BITS_TO_LONGS( |
| adapter->adapter_attr.features.max_dev_handle + 1) * |
| sizeof(unsigned long); |
| |
| adapter->dev_topo.pd_hdls_sz = pd_hdls_sz; |
| adapter->dev_topo.pd_hdls = |
| kzalloc(adapter->dev_topo.pd_hdls_sz, GFP_KERNEL); |
| if (!adapter->dev_topo.pd_hdls) |
| return -ENOMEM; |
| |
| adapter->dev_topo.blocking_hdls = |
| kzalloc(adapter->dev_topo.pd_hdls_sz, GFP_KERNEL); |
| if (!adapter->dev_topo.blocking_hdls) |
| return -ENOMEM; |
| |
| return 0; |
| } |
| |
| static void leapraid_free_dev_topo_bitmaps(struct leapraid_adapter *adapter) |
| { |
| kfree(adapter->dev_topo.pd_hdls); |
| kfree(adapter->dev_topo.blocking_hdls); |
| } |
| |
| static int leapraid_init_driver_cmds(struct leapraid_adapter *adapter) |
| { |
| INIT_LIST_HEAD(&adapter->driver_cmds.special_cmd_list); |
| |
| adapter->driver_cmds.scan_dev_cmd.status = LEAPRAID_CMD_NOT_USED; |
| adapter->driver_cmds.scan_dev_cmd.cb_idx = LEAPRAID_SCAN_DEV_CB_IDX; |
| list_add_tail(&adapter->driver_cmds.scan_dev_cmd.list, |
| &adapter->driver_cmds.special_cmd_list); |
| |
| adapter->driver_cmds.cfg_op_cmd.status = LEAPRAID_CMD_NOT_USED; |
| adapter->driver_cmds.cfg_op_cmd.cb_idx = LEAPRAID_CONFIG_CB_IDX; |
| mutex_init(&adapter->driver_cmds.cfg_op_cmd.mutex); |
| list_add_tail(&adapter->driver_cmds.cfg_op_cmd.list, |
| &adapter->driver_cmds.special_cmd_list); |
| |
| adapter->driver_cmds.transport_cmd.status = LEAPRAID_CMD_NOT_USED; |
| adapter->driver_cmds.transport_cmd.cb_idx = LEAPRAID_TRANSPORT_CB_IDX; |
| mutex_init(&adapter->driver_cmds.transport_cmd.mutex); |
| list_add_tail(&adapter->driver_cmds.transport_cmd.list, |
| &adapter->driver_cmds.special_cmd_list); |
| |
| adapter->driver_cmds.enc_cmd.status = LEAPRAID_CMD_NOT_USED; |
| adapter->driver_cmds.enc_cmd.cb_idx = LEAPRAID_ENC_CB_IDX; |
| mutex_init(&adapter->driver_cmds.enc_cmd.mutex); |
| list_add_tail(&adapter->driver_cmds.enc_cmd.list, |
| &adapter->driver_cmds.special_cmd_list); |
| |
| adapter->driver_cmds.notify_event_cmd.status = LEAPRAID_CMD_NOT_USED; |
| adapter->driver_cmds.notify_event_cmd.cb_idx = |
| LEAPRAID_NOTIFY_EVENT_CB_IDX; |
| mutex_init(&adapter->driver_cmds.notify_event_cmd.mutex); |
| list_add_tail(&adapter->driver_cmds.notify_event_cmd.list, |
| &adapter->driver_cmds.special_cmd_list); |
| |
| adapter->driver_cmds.ctl_cmd.status = LEAPRAID_CMD_NOT_USED; |
| adapter->driver_cmds.ctl_cmd.cb_idx = LEAPRAID_CTL_CB_IDX; |
| mutex_init(&adapter->driver_cmds.ctl_cmd.mutex); |
| list_add_tail(&adapter->driver_cmds.ctl_cmd.list, |
| &adapter->driver_cmds.special_cmd_list); |
| |
| adapter->driver_cmds.tm_cmd.status = LEAPRAID_CMD_NOT_USED; |
| adapter->driver_cmds.tm_cmd.cb_idx = LEAPRAID_TM_CB_IDX; |
| mutex_init(&adapter->driver_cmds.tm_cmd.mutex); |
| list_add_tail(&adapter->driver_cmds.tm_cmd.list, |
| &adapter->driver_cmds.special_cmd_list); |
| |
| return 0; |
| } |
| |
| static void leapraid_unmask_evts(struct leapraid_adapter *adapter, u16 evt) |
| { |
| if (evt >= LEAPRAID_MAX_EVENT_NUM) |
| return; |
| |
| clear_bit(evt, (unsigned long *)adapter->fw_evt_s.leapraid_evt_masks); |
| } |
| |
| static void leapraid_init_event_mask(struct leapraid_adapter *adapter) |
| { |
| int i; |
| |
| for (i = 0; i < LEAPRAID_EVT_MASK_COUNT; i++) |
| adapter->fw_evt_s.leapraid_evt_masks[i] = |
| LEAPRAID_INVALID_INITIAL_VALUE; |
| |
| leapraid_unmask_evts(adapter, LEAPRAID_EVT_SAS_TOPO_CHANGE_LIST); |
| leapraid_unmask_evts(adapter, LEAPRAID_EVT_SAS_ENCL_DEV_STATUS_CHANGE); |
| leapraid_unmask_evts(adapter, LEAPRAID_EVT_SAS_DEV_STATUS_CHANGE); |
| leapraid_unmask_evts(adapter, LEAPRAID_EVT_IR_CHANGE); |
| } |
| |
| static void leapraid_prepare_adapter_init_req( |
| struct leapraid_adapter *adapter, |
| struct leapraid_adapter_init_req *init_req) |
| { |
| ktime_t cur_time; |
| int i, chunk; |
| u32 reply_post_free_ary_sz; |
| |
| memset(init_req, 0, sizeof(struct leapraid_adapter_init_req)); |
| init_req->func = LEAPRAID_FUNC_ADAPTER_INIT; |
| init_req->who_init = LEAPRAID_WHOINIT_LINUX_DRIVER; |
| init_req->msg_ver = cpu_to_le16(LEAPRAID_MSG_VERSION); |
| init_req->header_ver = cpu_to_le16(LEAPRAID_HEADER_VERSION); |
| init_req->driver_ver = |
| cpu_to_le32((LEAPRAID_MAJOR_VERSION << |
| LEAPRAID_VER_MAJOR_SHIFT) | |
| (LEAPRAID_MINOR_VERSION << LEAPRAID_VER_MINOR_SHIFT) | |
| (LEAPRAID_BUILD_VERSION << LEAPRAID_VER_BUILD_SHIFT) | |
| LEAPRAID_RELEASE_VERSION); |
| if (adapter->notification_desc.msix_enable) |
| init_req->host_msix_vectors = adapter->adapter_attr.rq_cnt; |
| |
| init_req->req_frame_size = |
| cpu_to_le16(LEAPRAID_REQUEST_SIZE / LEAPRAID_DWORDS_BYTE_SIZE); |
| init_req->rep_desc_qd = |
| cpu_to_le16(adapter->adapter_attr.rep_desc_qd); |
| init_req->rep_msg_qd = |
| cpu_to_le16(adapter->adapter_attr.rep_msg_qd); |
| init_req->sense_buffer_add_high = |
| cpu_to_le32((u64)adapter->mem_desc.sense_data_dma >> 32); |
| init_req->rep_msg_dma_high = |
| cpu_to_le32((u64)adapter->mem_desc.rep_msg_dma >> 32); |
| init_req->task_desc_base_addr = |
| cpu_to_le64((u64)adapter->mem_desc.task_desc_dma); |
| init_req->rep_msg_addr_dma = |
| cpu_to_le64((u64)adapter->mem_desc.rep_msg_addr_dma); |
| if (!reset_devices) { |
| reply_post_free_ary_sz = |
| adapter->adapter_attr.rq_cnt * LEAPRAID_REP_RQ_CNT_SIZE; |
| memset(adapter->mem_desc.rep_desc_q_arr, 0, |
| reply_post_free_ary_sz); |
| |
| chunk = LEAPRAID_REP_DESC_CHUNK_SIZE; |
| for (i = 0; i < adapter->adapter_attr.rq_cnt; i++) |
| adapter->mem_desc.rep_desc_q_arr[i].rep_desc_base_addr = |
| cpu_to_le64 ((u64)adapter->mem_desc |
| .rep_desc_seg_maint[i / chunk] |
| .rep_desc_maint[i % chunk] |
| .rep_desc_dma); |
| |
| init_req->msg_flg = |
| LEAPRAID_ADAPTER_INIT_MSGFLG_RDPQ_ARRAY_MODE; |
| init_req->rep_desc_q_arr_addr = |
| cpu_to_le64((u64)adapter->mem_desc.rep_desc_q_arr_dma); |
| } else { |
| init_req->rep_desc_q_arr_addr = |
| cpu_to_le64((u64)adapter->mem_desc |
| .rep_desc_seg_maint[0] |
| .rep_desc_maint[0] |
| .rep_desc_dma); |
| } |
| cur_time = ktime_get_real(); |
| init_req->time_stamp = cpu_to_le64(ktime_to_ms(cur_time)); |
| } |
| |
| static int leapraid_send_adapter_init(struct leapraid_adapter *adapter) |
| { |
| struct leapraid_adapter_init_req init_req; |
| struct leapraid_adapter_init_rep init_rep; |
| u16 adapter_status; |
| int rc; |
| |
| leapraid_prepare_adapter_init_req(adapter, &init_req); |
| |
| rc = leapraid_handshake_func(adapter, |
| sizeof(struct leapraid_adapter_init_req), |
| (u32 *)&init_req, |
| sizeof(struct leapraid_adapter_init_rep), |
| (u16 *)&init_rep); |
| if (rc != 0) { |
| dev_err(&adapter->pdev->dev, "%s: Handshake failed, rc=%d\n", |
| __func__, rc); |
| return rc; |
| } |
| |
| adapter_status = |
| le16_to_cpu(init_rep.adapter_status) & |
| LEAPRAID_ADAPTER_STATUS_MASK; |
| if (adapter_status != LEAPRAID_ADAPTER_STATUS_SUCCESS) { |
| dev_err(&adapter->pdev->dev, "%s: failed\n", __func__); |
| rc = -EIO; |
| } |
| |
| return rc; |
| } |
| |
| static int leapraid_cfg_pages(struct leapraid_adapter *adapter) |
| { |
| union cfg_param_1 cfgp1 = {0}; |
| union cfg_param_2 cfgp2 = {0}; |
| union { |
| struct leapraid_manufacturing_p0 manufacturing_page0; |
| struct leapraid_bios_page3 bios_page3; |
| struct leapraid_bios_page2 bios_page2; |
| } cfg_page; |
| int rc; |
| |
| rc = leapraid_op_config_page(adapter, &cfg_page.bios_page3, cfgp1, |
| cfgp2, GET_BIOS_PG3); |
| if (rc) |
| return rc; |
| |
| adapter->adapter_attr.bios_version = |
| le32_to_cpu(cfg_page.bios_page3.bios_version); |
| |
| rc = leapraid_op_config_page(adapter, &cfg_page.bios_page2, cfgp1, |
| cfgp2, GET_BIOS_PG2); |
| if (rc) |
| return rc; |
| |
| adapter->boot_devs.requested_boot_dev.form = |
| cfg_page.bios_page2.requested_boot_dev_form; |
| memcpy(adapter->boot_devs.requested_boot_dev.pg_dev, |
| &cfg_page.bios_page2.requested_boot_dev, |
| LEAPRAID_BOOT_DEV_SIZE); |
| adapter->boot_devs.requested_alt_boot_dev.form = |
| cfg_page.bios_page2.requested_alt_boot_dev_form; |
| memcpy(adapter->boot_devs.requested_alt_boot_dev.pg_dev, |
| &cfg_page.bios_page2.requested_alt_boot_dev, |
| LEAPRAID_BOOT_DEV_SIZE); |
| adapter->boot_devs.current_boot_dev.form = |
| cfg_page.bios_page2.current_boot_dev_form; |
| memcpy(adapter->boot_devs.current_boot_dev.pg_dev, |
| &cfg_page.bios_page2.current_boot_dev, |
| LEAPRAID_BOOT_DEV_SIZE); |
| |
| rc = leapraid_op_config_page(adapter, &cfg_page.manufacturing_page0, |
| cfgp1, cfgp2, GET_MANUFACTURING_PG0); |
| if (rc) |
| return rc; |
| |
| snprintf(adapter->adapter_attr.board_name, |
| sizeof(adapter->adapter_attr.board_name), |
| "%.*s", |
| (int)sizeof(cfg_page.manufacturing_page0.board_name), |
| cfg_page.manufacturing_page0.board_name); |
| return rc; |
| } |
| |
| static int leapraid_evt_notify(struct leapraid_adapter *adapter) |
| { |
| struct leapraid_evt_notify_req *evt_notify_req; |
| struct leapraid_evt_notify_rep *evt_notify_rep; |
| u16 adapter_status; |
| int rc = 0; |
| int i; |
| |
| mutex_lock(&adapter->driver_cmds.notify_event_cmd.mutex); |
| adapter->driver_cmds.notify_event_cmd.status = LEAPRAID_CMD_PENDING; |
| evt_notify_req = |
| leapraid_get_task_desc( |
| adapter, |
| adapter->driver_cmds.notify_event_cmd.inter_taskid); |
| memset(evt_notify_req, 0, sizeof(struct leapraid_evt_notify_req)); |
| evt_notify_req->func = LEAPRAID_FUNC_EVENT_NOTIFY; |
| for (i = 0; i < LEAPRAID_EVT_MASK_COUNT; i++) |
| evt_notify_req->evt_masks[i] = |
| cpu_to_le32(adapter->fw_evt_s.leapraid_evt_masks[i]); |
| init_completion(&adapter->driver_cmds.notify_event_cmd.done); |
| leapraid_fire_task(adapter, |
| adapter->driver_cmds.notify_event_cmd.inter_taskid); |
| wait_for_completion_timeout( |
| &adapter->driver_cmds.notify_event_cmd.done, |
| LEAPRAID_NOTIFY_EVENT_CMD_TIMEOUT * HZ); |
| |
| if (!(adapter->driver_cmds.notify_event_cmd.status & |
| LEAPRAID_CMD_DONE)) { |
| rc = -EFAULT; |
| goto out_cleanup; |
| } |
| |
| if (!(adapter->driver_cmds.notify_event_cmd.status & |
| LEAPRAID_CMD_REPLY_VALID)) { |
| rc = -EFAULT; |
| goto out_cleanup; |
| } |
| |
| evt_notify_rep = (void *)&adapter->driver_cmds.notify_event_cmd.reply; |
| adapter_status = le16_to_cpu(evt_notify_rep->adapter_status) & |
| LEAPRAID_ADAPTER_STATUS_MASK; |
| if (adapter_status != LEAPRAID_ADAPTER_STATUS_SUCCESS) |
| rc = -EFAULT; |
| |
| out_cleanup: |
| adapter->driver_cmds.notify_event_cmd.status = LEAPRAID_CMD_NOT_USED; |
| mutex_unlock(&adapter->driver_cmds.notify_event_cmd.mutex); |
| |
| return rc; |
| } |
| |
| int leapraid_scan_dev(struct leapraid_adapter *adapter, bool async_scan_dev) |
| { |
| struct leapraid_scan_dev_req *scan_dev_req; |
| struct leapraid_scan_dev_rep *scan_dev_rep; |
| u16 adapter_status; |
| int rc = 0; |
| |
| dev_info(&adapter->pdev->dev, |
| "Send device scan, async_scan_dev=%d!\n", async_scan_dev); |
| |
| adapter->driver_cmds.scan_dev_cmd.status = LEAPRAID_CMD_PENDING; |
| adapter->driver_cmds.scan_dev_cmd.async_scan_dev = async_scan_dev; |
| scan_dev_req = |
| leapraid_get_task_desc( |
| adapter, |
| adapter->driver_cmds.scan_dev_cmd.inter_taskid); |
| memset(scan_dev_req, 0, sizeof(struct leapraid_scan_dev_req)); |
| scan_dev_req->func = LEAPRAID_FUNC_SCAN_DEV; |
| |
| if (async_scan_dev) { |
| adapter->scan_dev_desc.first_scan_dev_fired = 1; |
| leapraid_fire_task( |
| adapter, |
| adapter->driver_cmds.scan_dev_cmd.inter_taskid); |
| return 0; |
| } |
| |
| init_completion(&adapter->driver_cmds.scan_dev_cmd.done); |
| leapraid_fire_task(adapter, |
| adapter->driver_cmds.scan_dev_cmd.inter_taskid); |
| wait_for_completion_timeout(&adapter->driver_cmds.scan_dev_cmd.done, |
| LEAPRAID_SCAN_DEV_CMD_TIMEOUT * HZ); |
| if (!(adapter->driver_cmds.scan_dev_cmd.status & LEAPRAID_CMD_DONE)) { |
| dev_err(&adapter->pdev->dev, "Device scan timeout!\n"); |
| if (adapter->driver_cmds.scan_dev_cmd.status & |
| LEAPRAID_CMD_RESET) |
| rc = -EFAULT; |
| else |
| rc = -ETIME; |
| goto out_cleanup; |
| } |
| |
| scan_dev_rep = (void *)&adapter->driver_cmds.scan_dev_cmd.reply; |
| adapter_status = |
| le16_to_cpu(scan_dev_rep->adapter_status) & |
| LEAPRAID_ADAPTER_STATUS_MASK; |
| if (adapter_status != LEAPRAID_ADAPTER_STATUS_SUCCESS) { |
| dev_err(&adapter->pdev->dev, "Device scan failure!\n"); |
| rc = -EFAULT; |
| goto out_cleanup; |
| } |
| |
| out_cleanup: |
| adapter->driver_cmds.scan_dev_cmd.status = LEAPRAID_CMD_NOT_USED; |
| return rc; |
| } |
| |
| static void leapraid_init_task_tracker(struct leapraid_adapter *adapter) |
| { |
| unsigned long flags; |
| |
| spin_lock_irqsave(&adapter->dynamic_task_desc.task_lock, flags); |
| |
| spin_unlock_irqrestore(&adapter->dynamic_task_desc.task_lock, flags); |
| } |
| |
| static void leapraid_init_rep_msg_addr(struct leapraid_adapter *adapter) |
| { |
| u32 reply_address; |
| unsigned int i; |
| |
| for (i = 0, reply_address = (u32)adapter->mem_desc.rep_msg_dma; |
| i < adapter->adapter_attr.rep_msg_qd; |
| i++, reply_address += LEAPRAID_REPLY_SIZE) |
| adapter->mem_desc.rep_msg_addr[i] = cpu_to_le32(reply_address); |
| } |
| |
| static void init_rep_desc( |
| struct leapraid_rq *rq, |
| int index, |
| union leapraid_rep_desc_union *reply_post_free_contig) |
| { |
| struct leapraid_adapter *adapter = rq->adapter; |
| unsigned int i; |
| |
| if (!reset_devices) |
| rq->rep_desc = |
| adapter->mem_desc |
| .rep_desc_seg_maint[index / |
| LEAPRAID_REP_DESC_CHUNK_SIZE] |
| .rep_desc_maint[index % |
| LEAPRAID_REP_DESC_CHUNK_SIZE] |
| .rep_desc; |
| else |
| rq->rep_desc = reply_post_free_contig; |
| |
| rq->rep_post_host_idx = 0; |
| for (i = 0; i < adapter->adapter_attr.rep_desc_qd; i++) |
| rq->rep_desc[i].words = cpu_to_le64(ULLONG_MAX); |
| } |
| |
| static void leapraid_init_rep_desc(struct leapraid_adapter *adapter) |
| { |
| union leapraid_rep_desc_union *reply_post_free_contig; |
| struct leapraid_int_rq *int_rq; |
| struct leapraid_blk_mq_poll_rq *blk_mq_poll_rq; |
| unsigned int i; |
| int index; |
| |
| index = 0; |
| reply_post_free_contig = adapter->mem_desc |
| .rep_desc_seg_maint[0] |
| .rep_desc_maint[0] |
| .rep_desc; |
| |
| for (i = 0; i < adapter->notification_desc.iopoll_qdex; i++) { |
| int_rq = &adapter->notification_desc.int_rqs[i]; |
| init_rep_desc(&int_rq->rq, index, reply_post_free_contig); |
| if (!reset_devices) |
| index++; |
| else |
| reply_post_free_contig += |
| adapter->adapter_attr.rep_desc_qd; |
| } |
| |
| for (i = 0; i < adapter->notification_desc.iopoll_qcnt; i++) { |
| blk_mq_poll_rq = &adapter->notification_desc.blk_mq_poll_rqs[i]; |
| init_rep_desc(&blk_mq_poll_rq->rq, |
| index, reply_post_free_contig); |
| if (!reset_devices) |
| index++; |
| else |
| reply_post_free_contig += |
| adapter->adapter_attr.rep_desc_qd; |
| } |
| } |
| |
| static void leapraid_init_bar_idx_regs(struct leapraid_adapter *adapter) |
| { |
| struct leapraid_int_rq *int_rq; |
| struct leapraid_blk_mq_poll_rq *blk_mq_poll_rq; |
| unsigned int i, j; |
| |
| adapter->rep_msg_host_idx = adapter->adapter_attr.rep_msg_qd - 1; |
| writel(adapter->rep_msg_host_idx, |
| &adapter->iomem_base->rep_msg_host_idx); |
| |
| for (i = 0; i < adapter->notification_desc.iopoll_qdex; i++) { |
| int_rq = &adapter->notification_desc.int_rqs[i]; |
| for (j = 0; j < REP_POST_HOST_IDX_REG_CNT; j++) |
| writel((int_rq->rq.msix_idx & 7) << |
| LEAPRAID_RPHI_MSIX_IDX_SHIFT, |
| &adapter->iomem_base->rep_post_reg_idx[j].idx); |
| } |
| |
| for (i = 0; i < adapter->notification_desc.iopoll_qcnt; i++) { |
| blk_mq_poll_rq = |
| &adapter->notification_desc.blk_mq_poll_rqs[i]; |
| for (j = 0; j < REP_POST_HOST_IDX_REG_CNT; j++) |
| writel((blk_mq_poll_rq->rq.msix_idx & 7) << |
| LEAPRAID_RPHI_MSIX_IDX_SHIFT, |
| &adapter->iomem_base->rep_post_reg_idx[j].idx); |
| } |
| } |
| |
| static int leapraid_make_adapter_available(struct leapraid_adapter *adapter) |
| { |
| int rc; |
| |
| leapraid_init_task_tracker(adapter); |
| leapraid_init_rep_msg_addr(adapter); |
| |
| if (adapter->scan_dev_desc.driver_loading) |
| leapraid_configure_reply_queue_affinity(adapter); |
| |
| leapraid_init_rep_desc(adapter); |
| rc = leapraid_send_adapter_init(adapter); |
| if (rc) |
| return rc; |
| |
| leapraid_init_bar_idx_regs(adapter); |
| leapraid_unmask_int(adapter); |
| rc = leapraid_cfg_pages(adapter); |
| if (rc) |
| return rc; |
| |
| rc = leapraid_evt_notify(adapter); |
| if (rc) |
| return rc; |
| |
| if (!adapter->access_ctrl.shost_recovering) { |
| adapter->scan_dev_desc.wait_scan_dev_done = 1; |
| return 0; |
| } |
| |
| return leapraid_scan_dev(adapter, false); |
| } |
| |
| int leapraid_ctrl_init(struct leapraid_adapter *adapter) |
| { |
| u32 cap; |
| int rc; |
| |
| rc = leapraid_init_driver_cmds(adapter); |
| if (rc) { |
| dev_err(&adapter->pdev->dev, "Init driver cmds failure\n"); |
| goto out_free; |
| } |
| |
| rc = leapraid_set_pcie_and_notification(adapter); |
| if (rc) |
| goto out_free; |
| |
| pci_set_drvdata(adapter->pdev, adapter->shost); |
| |
| pcie_capability_read_dword(adapter->pdev, PCI_EXP_DEVCAP, &cap); |
| |
| if (cap & PCI_EXP_DEVCAP_EXT_TAG) |
| pcie_capability_set_word(adapter->pdev, PCI_EXP_DEVCTL, |
| PCI_EXP_DEVCTL_EXT_TAG); |
| |
| rc = leapraid_fw_log_init(adapter); |
| if (rc) { |
| dev_err(&adapter->pdev->dev, "FW log init failure\n"); |
| goto out_free; |
| } |
| |
| rc = leapraid_request_host_memory(adapter); |
| if (rc) { |
| dev_err(&adapter->pdev->dev, "Request host memory failure\n"); |
| goto out_free; |
| } |
| |
| init_waitqueue_head(&adapter->reset_desc.reset_wait_queue); |
| init_waitqueue_head(&adapter->access_ctrl.shost_recover_wq); |
| |
| rc = leapraid_alloc_dev_topo_bitmaps(adapter); |
| if (rc) { |
| dev_err(&adapter->pdev->dev, "Alloc topo bitmaps failure\n"); |
| goto out_free; |
| } |
| |
| leapraid_init_event_mask(adapter); |
| |
| rc = leapraid_make_adapter_available(adapter); |
| if (rc) { |
| dev_err(&adapter->pdev->dev, |
| "Make adapter available failure\n"); |
| goto out_free; |
| } |
| |
| leapraid_overheat_init(adapter); |
| if (!adapter->overheat_desc.fault_overheat_wq) { |
| rc = -ENOMEM; |
| goto out_free; |
| } |
| |
| return 0; |
| |
| out_free: |
| adapter->access_ctrl.host_removing = 1; |
| leapraid_fw_log_exit(adapter); |
| leapraid_disable_controller(adapter); |
| leapraid_free_host_memory(adapter); |
| leapraid_free_dev_topo_bitmaps(adapter); |
| pci_set_drvdata(adapter->pdev, NULL); |
| return rc; |
| } |
| |
| void leapraid_remove_ctrl(struct leapraid_adapter *adapter) |
| { |
| leapraid_overheat_cleanup(adapter); |
| leapraid_check_scheduled_fault_stop(adapter); |
| leapraid_fw_log_stop(adapter); |
| leapraid_fw_log_exit(adapter); |
| leapraid_disable_controller(adapter); |
| leapraid_free_host_memory(adapter); |
| leapraid_free_dev_topo_bitmaps(adapter); |
| leapraid_free_enc_list(adapter); |
| pci_set_drvdata(adapter->pdev, NULL); |
| } |