blob: 089d0810bd13a747ae2c92bb7305b964264784d6 [file] [edit]
// 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);
}