mirror of
https://github.com/torvalds/linux.git
synced 2026-10-16 12:50:02 +02:00
When extra warnings are enabled, the alloc_ordered_workqueue() function
cannot be called with a variable name for the format string:
drivers/scsi/leapraid/leapraid_os.c: In function 'leapraid_probe':
drivers/scsi/leapraid/leapraid_os.c:2062:58: error: format not a string literal and no format arguments [-Werror=format-security]
2062 | alloc_ordered_workqueue(adapter->fw_evt_s.fw_evt_name, 0);
| ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~
As the string is only assembled before the call and not used anywhere else,
just fold the earlier snprintf() into the alloc_ordered_workqueue() call.
Fixes: 5597088c9e ("scsi: leapraid: Add new SCSI driver")
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Link: https://patch.msgid.link/20260915202134.3534708-1-arnd@kernel.org
Signed-off-by: Martin K. Petersen (Oracle) <mkp@kernel.org>
2492 lines
68 KiB
C
2492 lines
68 KiB
C
// 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"
|
|
#include "leapraid.h"
|
|
|
|
LIST_HEAD(leapraid_adapter_list);
|
|
DEFINE_SPINLOCK(leapraid_adapter_lock);
|
|
|
|
MODULE_AUTHOR(LEAPRAID_AUTHOR);
|
|
MODULE_DESCRIPTION(LEAPRAID_DESCRIPTION);
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_VERSION(LEAPRAID_DRIVER_VERSION);
|
|
|
|
static atomic_t leapraid_ids = ATOMIC_INIT(0);
|
|
|
|
static int open_pcie_trace = 1;
|
|
module_param(open_pcie_trace, int, 0644);
|
|
MODULE_PARM_DESC(open_pcie_trace, "Default=1(open)/0(close)");
|
|
|
|
static int enable_mp = 1;
|
|
module_param(enable_mp, int, 0444);
|
|
MODULE_PARM_DESC(enable_mp,
|
|
"Enable multipath on target device. default=1(enable)");
|
|
|
|
static inline void leapraid_get_sense_data(char *sense,
|
|
struct sense_info *data)
|
|
{
|
|
bool desc_format = (sense[0] & SCSI_SENSE_RESPONSE_CODE_MASK) >=
|
|
DESC_FORMAT_THRESHOLD;
|
|
|
|
if (desc_format) {
|
|
data->sense_key = sense[1] & SENSE_KEY_MASK;
|
|
data->asc = sense[2];
|
|
data->ascq = sense[3];
|
|
} else {
|
|
data->sense_key = sense[2] & SENSE_KEY_MASK;
|
|
data->asc = sense[12];
|
|
data->ascq = sense[13];
|
|
}
|
|
}
|
|
|
|
static struct leapraid_adapter *pdev_to_adapter(struct pci_dev *pdev)
|
|
{
|
|
struct Scsi_Host *shost = pci_get_drvdata(pdev);
|
|
|
|
if (!shost)
|
|
return NULL;
|
|
|
|
return shost_priv(shost);
|
|
}
|
|
|
|
void leapraid_set_tm_flg(struct leapraid_adapter *adapter, u16 hdl)
|
|
{
|
|
struct leapraid_sdev_priv *sdev_priv;
|
|
struct scsi_device *sdev;
|
|
bool skip = false;
|
|
|
|
/* Iterate over all devices. */
|
|
shost_for_each_device(sdev, adapter->shost) {
|
|
if (skip)
|
|
continue;
|
|
|
|
sdev_priv = sdev->hostdata;
|
|
if (!sdev_priv)
|
|
continue;
|
|
|
|
if (sdev_priv->starget_priv->hdl == hdl) {
|
|
sdev_priv->starget_priv->tm_busy = 1;
|
|
skip = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void leapraid_clear_tm_flg(struct leapraid_adapter *adapter, u16 hdl)
|
|
{
|
|
struct leapraid_sdev_priv *sdev_priv;
|
|
struct scsi_device *sdev;
|
|
bool skip = false;
|
|
|
|
/* Iterate over all devices. */
|
|
shost_for_each_device(sdev, adapter->shost) {
|
|
if (skip)
|
|
continue;
|
|
|
|
sdev_priv = sdev->hostdata;
|
|
if (!sdev_priv)
|
|
continue;
|
|
|
|
if (sdev_priv->starget_priv->hdl == hdl) {
|
|
sdev_priv->starget_priv->tm_busy = 0;
|
|
skip = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int leapraid_tm_cmd_map_status(struct leapraid_adapter *adapter,
|
|
uint channel,
|
|
uint id,
|
|
uint lun,
|
|
u8 type,
|
|
u16 taskid_task)
|
|
{
|
|
int rc = FAILED;
|
|
|
|
if (taskid_task <= adapter->shost->can_queue) {
|
|
switch (type) {
|
|
case LEAPRAID_TM_TASKTYPE_ABRT_TASK_SET:
|
|
case LEAPRAID_TM_TASKTYPE_LOGICAL_UNIT_RESET:
|
|
if (!leapraid_scmd_find_by_lun(adapter, id, lun,
|
|
channel))
|
|
rc = SUCCESS;
|
|
break;
|
|
case LEAPRAID_TM_TASKTYPE_TARGET_RESET:
|
|
if (!leapraid_scmd_find_by_tgt(adapter, id, channel))
|
|
rc = SUCCESS;
|
|
break;
|
|
default:
|
|
rc = SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (taskid_task == adapter->driver_cmds.ctl_cmd.hp_taskid &&
|
|
(adapter->driver_cmds.ctl_cmd.status &
|
|
LEAPRAID_CMD_DONE ||
|
|
adapter->driver_cmds.ctl_cmd.status &
|
|
LEAPRAID_CMD_NOT_USED))
|
|
rc = SUCCESS;
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int leapraid_tm_post_processing(struct leapraid_adapter *adapter,
|
|
u16 hdl, uint channel, uint id,
|
|
uint lun, u8 type, u16 taskid_task)
|
|
{
|
|
int rc;
|
|
|
|
rc = leapraid_tm_cmd_map_status(adapter, channel, id, lun,
|
|
type, taskid_task);
|
|
if (rc == SUCCESS)
|
|
return rc;
|
|
|
|
leapraid_mask_int(adapter);
|
|
leapraid_sync_irqs(adapter, true);
|
|
leapraid_unmask_int(adapter);
|
|
|
|
return leapraid_tm_cmd_map_status(adapter, channel, id, lun, type,
|
|
taskid_task);
|
|
}
|
|
|
|
static void leapraid_build_tm_req(struct leapraid_scsi_tm_req *scsi_tm_req,
|
|
u16 hdl, uint lun, u8 type, u8 tr_method,
|
|
u16 target_taskid)
|
|
{
|
|
memset(scsi_tm_req, 0, sizeof(*scsi_tm_req));
|
|
scsi_tm_req->func = LEAPRAID_FUNC_SCSI_TMF;
|
|
scsi_tm_req->dev_hdl = cpu_to_le16(hdl);
|
|
scsi_tm_req->task_type = type;
|
|
scsi_tm_req->msg_flg = tr_method;
|
|
if (type == LEAPRAID_TM_TASKTYPE_ABORT_TASK ||
|
|
type == LEAPRAID_TM_TASKTYPE_QUERY_TASK)
|
|
scsi_tm_req->task_mid = cpu_to_le16(target_taskid);
|
|
int_to_scsilun(lun, (struct scsi_lun *)scsi_tm_req->lun);
|
|
}
|
|
|
|
int leapraid_issue_tm(struct leapraid_adapter *adapter, u16 hdl, uint channel,
|
|
uint id, uint lun, u8 type,
|
|
u16 target_taskid, u8 tr_method)
|
|
{
|
|
struct leapraid_scsi_tm_req *scsi_tm_req;
|
|
struct leapraid_scsiio_req *scsiio_req;
|
|
struct leapraid_io_req_tracker *io_req_tracker = NULL;
|
|
u16 msix_task;
|
|
u16 taskid;
|
|
bool issue_reset = false;
|
|
u32 db;
|
|
int rc;
|
|
|
|
lockdep_assert_held(&adapter->driver_cmds.tm_cmd.mutex);
|
|
|
|
if (adapter->access_ctrl.shost_recovering ||
|
|
adapter->access_ctrl.host_removing ||
|
|
adapter->access_ctrl.pcie_recovering) {
|
|
dev_info(&adapter->pdev->dev,
|
|
"%s %s: Host is recovering, skip tm command!\n",
|
|
__func__, adapter->adapter_attr.name);
|
|
return FAILED;
|
|
}
|
|
|
|
db = leapraid_readl(&adapter->iomem_base->db);
|
|
if (db & LEAPRAID_DB_USED) {
|
|
dev_info(&adapter->pdev->dev,
|
|
"%s Unexpected db status, issuing hard reset!\n",
|
|
adapter->adapter_attr.name);
|
|
dev_info(&adapter->pdev->dev, "%s:%d: call hard_reset\n",
|
|
__func__, __LINE__);
|
|
rc = leapraid_hard_reset_handler(adapter, FULL_RESET);
|
|
return !rc ? SUCCESS : FAILED;
|
|
}
|
|
|
|
if ((db & LEAPRAID_DB_MASK) == LEAPRAID_DB_FAULT) {
|
|
dev_info(&adapter->pdev->dev, "%s:%d: call hard_reset\n",
|
|
__func__, __LINE__);
|
|
rc = leapraid_hard_reset_handler(adapter, FULL_RESET);
|
|
return !rc ? SUCCESS : FAILED;
|
|
}
|
|
|
|
if (type == LEAPRAID_TM_TASKTYPE_ABORT_TASK)
|
|
io_req_tracker =
|
|
leapraid_get_io_tracker_from_taskid(adapter,
|
|
target_taskid);
|
|
|
|
adapter->driver_cmds.tm_cmd.status = LEAPRAID_CMD_PENDING;
|
|
scsi_tm_req =
|
|
leapraid_get_task_desc(adapter,
|
|
adapter->driver_cmds.tm_cmd.hp_taskid);
|
|
leapraid_build_tm_req(scsi_tm_req, hdl, lun, type, tr_method,
|
|
target_taskid);
|
|
memset(&adapter->driver_cmds.tm_cmd.reply, 0,
|
|
sizeof(struct leapraid_scsi_tm_rep));
|
|
leapraid_set_tm_flg(adapter, hdl);
|
|
init_completion(&adapter->driver_cmds.tm_cmd.done);
|
|
if (type == LEAPRAID_TM_TASKTYPE_ABORT_TASK &&
|
|
io_req_tracker &&
|
|
io_req_tracker->msix_io < adapter->adapter_attr.rq_cnt)
|
|
msix_task = io_req_tracker->msix_io;
|
|
else
|
|
msix_task = 0;
|
|
taskid = adapter->driver_cmds.tm_cmd.hp_taskid;
|
|
leapraid_fire_hpr_task(adapter, taskid, msix_task);
|
|
wait_for_completion_timeout(&adapter->driver_cmds.tm_cmd.done,
|
|
LEAPRAID_TM_CMD_TIMEOUT * HZ);
|
|
if (!(adapter->driver_cmds.tm_cmd.status & LEAPRAID_CMD_DONE)) {
|
|
dev_err(&adapter->pdev->dev,
|
|
"%s: TM cmd timeout, status=0x%x\n",
|
|
__func__, adapter->driver_cmds.tm_cmd.status);
|
|
leapraid_log_req_context(adapter, taskid, scsi_tm_req);
|
|
issue_reset =
|
|
leapraid_check_reset(
|
|
adapter->driver_cmds.tm_cmd.status);
|
|
if (issue_reset) {
|
|
dev_info(&adapter->pdev->dev,
|
|
"%s:%d: call hard_reset\n",
|
|
__func__, __LINE__);
|
|
rc = leapraid_hard_reset_handler(adapter, FULL_RESET);
|
|
rc = !rc ? SUCCESS : FAILED;
|
|
goto out_cleanup;
|
|
}
|
|
}
|
|
|
|
leapraid_sync_irqs(adapter, false);
|
|
|
|
switch (type) {
|
|
case LEAPRAID_TM_TASKTYPE_TARGET_RESET:
|
|
case LEAPRAID_TM_TASKTYPE_ABRT_TASK_SET:
|
|
case LEAPRAID_TM_TASKTYPE_LOGICAL_UNIT_RESET:
|
|
rc = leapraid_tm_post_processing(adapter, hdl, channel, id,
|
|
lun, type, target_taskid);
|
|
break;
|
|
case LEAPRAID_TM_TASKTYPE_ABORT_TASK:
|
|
rc = SUCCESS;
|
|
scsiio_req = leapraid_get_task_desc(adapter, target_taskid);
|
|
if (le16_to_cpu(scsiio_req->dev_hdl) != hdl)
|
|
break;
|
|
dev_err(&adapter->pdev->dev, "%s: Abort failed, hdl=0x%04x\n",
|
|
adapter->adapter_attr.name, hdl);
|
|
rc = FAILED;
|
|
break;
|
|
case LEAPRAID_TM_TASKTYPE_QUERY_TASK:
|
|
rc = SUCCESS;
|
|
break;
|
|
default:
|
|
rc = FAILED;
|
|
break;
|
|
}
|
|
|
|
out_cleanup:
|
|
leapraid_clear_tm_flg(adapter, hdl);
|
|
adapter->driver_cmds.tm_cmd.status = LEAPRAID_CMD_NOT_USED;
|
|
return rc;
|
|
}
|
|
|
|
int leapraid_issue_locked_tm(struct leapraid_adapter *adapter, u16 hdl,
|
|
uint channel, uint id, uint lun, u8 type,
|
|
u16 target_taskid, u8 tr_method)
|
|
{
|
|
int rc;
|
|
|
|
mutex_lock(&adapter->driver_cmds.tm_cmd.mutex);
|
|
rc = leapraid_issue_tm(adapter, hdl, channel, id, lun, type,
|
|
target_taskid, tr_method);
|
|
mutex_unlock(&adapter->driver_cmds.tm_cmd.mutex);
|
|
|
|
return rc;
|
|
}
|
|
|
|
void leapraid_smart_fault_detect(struct leapraid_adapter *adapter, u16 hdl)
|
|
{
|
|
struct leapraid_starget_priv *starget_priv;
|
|
struct leapraid_sas_dev *sas_dev;
|
|
struct scsi_target *starget;
|
|
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);
|
|
if (!sas_dev) {
|
|
spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
|
|
return;
|
|
}
|
|
|
|
starget = sas_dev->starget;
|
|
starget_priv = starget ? starget->hostdata : NULL;
|
|
if (!starget_priv) {
|
|
spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
|
|
goto release_sdev;
|
|
}
|
|
|
|
if (starget_priv->flg & LEAPRAID_TGT_FLG_RAID_MEMBER ||
|
|
starget_priv->flg & LEAPRAID_TGT_FLG_VOLUME) {
|
|
spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
|
|
goto release_sdev;
|
|
}
|
|
|
|
spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
|
|
leapraid_async_turn_on_led(adapter, hdl);
|
|
release_sdev:
|
|
if (sas_dev)
|
|
leapraid_sdev_put(sas_dev);
|
|
}
|
|
|
|
static void leapraid_process_sense_data(struct leapraid_adapter *adapter,
|
|
struct leapraid_scsiio_rep *scsiio_rep,
|
|
struct scsi_cmnd *scmd, u16 taskid)
|
|
{
|
|
struct sense_info data;
|
|
const void *sense_data;
|
|
u32 sz;
|
|
|
|
if (!(scsiio_rep->scsi_state & LEAPRAID_SCSI_STATE_AUTOSENSE_VALID))
|
|
return;
|
|
|
|
sense_data = leapraid_get_sense_buffer(adapter, taskid);
|
|
sz = min_t(u32, SCSI_SENSE_BUFFERSIZE,
|
|
le32_to_cpu(scsiio_rep->sense_count));
|
|
|
|
memcpy(scmd->sense_buffer, sense_data, sz);
|
|
leapraid_get_sense_data(scmd->sense_buffer, &data);
|
|
if (data.asc == ASC_FAILURE_PREDICTION_THRESHOLD_EXCEEDED)
|
|
leapraid_smart_fault_detect(adapter,
|
|
le16_to_cpu(scsiio_rep->dev_hdl));
|
|
}
|
|
|
|
static void leapraid_handle_data_underrun(
|
|
struct leapraid_scsiio_rep *scsiio_rep,
|
|
struct scsi_cmnd *scmd, u32 xfer_cnt)
|
|
{
|
|
u8 scsi_status = scsiio_rep->scsi_status;
|
|
u8 scsi_state = scsiio_rep->scsi_state;
|
|
|
|
scmd->result = (DID_OK << LEAPRAID_SCSI_HOST_SHIFT) | scsi_status;
|
|
|
|
if (scsi_state & LEAPRAID_SCSI_STATE_AUTOSENSE_VALID)
|
|
return;
|
|
|
|
if (xfer_cnt < scmd->underflow) {
|
|
if (scsi_status == SAM_STAT_BUSY)
|
|
scmd->result = SAM_STAT_BUSY;
|
|
else
|
|
scmd->result = DID_SOFT_ERROR <<
|
|
LEAPRAID_SCSI_HOST_SHIFT;
|
|
} else if (scsi_state & (LEAPRAID_SCSI_STATE_AUTOSENSE_FAILED |
|
|
LEAPRAID_SCSI_STATE_NO_SCSI_STATUS)) {
|
|
scmd->result = DID_SOFT_ERROR << LEAPRAID_SCSI_HOST_SHIFT;
|
|
} else if (scsi_state & LEAPRAID_SCSI_STATE_TERMINATED) {
|
|
scmd->result = DID_RESET << LEAPRAID_SCSI_HOST_SHIFT;
|
|
} else if (!xfer_cnt && scmd->cmnd[0] == REPORT_LUNS) {
|
|
scsiio_rep->scsi_state = LEAPRAID_SCSI_STATE_AUTOSENSE_VALID;
|
|
scsiio_rep->scsi_status = SAM_STAT_CHECK_CONDITION;
|
|
scsi_build_sense(scmd, 0, ILLEGAL_REQUEST,
|
|
LEAPRAID_SCSI_ASC_INVALID_CMD_CODE,
|
|
LEAPRAID_SCSI_ASCQ_DEFAULT);
|
|
}
|
|
}
|
|
|
|
static void leapraid_handle_success_status(
|
|
struct leapraid_scsiio_rep *scsiio_rep,
|
|
struct scsi_cmnd *scmd,
|
|
u32 response_code)
|
|
{
|
|
u8 scsi_status = scsiio_rep->scsi_status;
|
|
u8 scsi_state = scsiio_rep->scsi_state;
|
|
|
|
scmd->result = (DID_OK << LEAPRAID_SCSI_HOST_SHIFT) | scsi_status;
|
|
|
|
if (response_code == LEAPRAID_TM_RSP_INVALID_FRAME ||
|
|
(scsi_state & (LEAPRAID_SCSI_STATE_AUTOSENSE_FAILED |
|
|
LEAPRAID_SCSI_STATE_NO_SCSI_STATUS)))
|
|
scmd->result = DID_SOFT_ERROR << LEAPRAID_SCSI_HOST_SHIFT;
|
|
else if (scsi_state & LEAPRAID_SCSI_STATE_TERMINATED)
|
|
scmd->result = DID_RESET << LEAPRAID_SCSI_HOST_SHIFT;
|
|
}
|
|
|
|
static void leapraid_scsiio_done_dispatch(
|
|
struct leapraid_adapter *adapter,
|
|
struct leapraid_scsiio_rep *scsiio_rep,
|
|
struct leapraid_sdev_priv *sdev_priv,
|
|
struct scsi_cmnd *scmd,
|
|
u16 taskid, u32 response_code)
|
|
{
|
|
u8 scsi_status = scsiio_rep->scsi_status;
|
|
u8 scsi_state = scsiio_rep->scsi_state;
|
|
u16 adapter_status;
|
|
u32 xfer_cnt;
|
|
u32 sz;
|
|
|
|
adapter_status = le16_to_cpu(scsiio_rep->adapter_status) &
|
|
LEAPRAID_ADAPTER_STATUS_MASK;
|
|
|
|
xfer_cnt = le32_to_cpu(scsiio_rep->transfer_count);
|
|
scsi_set_resid(scmd, scsi_bufflen(scmd) - xfer_cnt);
|
|
|
|
if (adapter_status == LEAPRAID_ADAPTER_STATUS_SCSI_DATA_UNDERRUN &&
|
|
xfer_cnt == 0 &&
|
|
(scsi_status == LEAPRAID_SCSI_STATUS_BUSY ||
|
|
scsi_status == LEAPRAID_SCSI_STATUS_RESERVATION_CONFLICT ||
|
|
scsi_status == LEAPRAID_SCSI_STATUS_TASK_SET_FULL))
|
|
adapter_status = LEAPRAID_ADAPTER_STATUS_SUCCESS;
|
|
|
|
switch (adapter_status) {
|
|
case LEAPRAID_ADAPTER_STATUS_SCSI_DEVICE_NOT_THERE:
|
|
scmd->result = DID_NO_CONNECT << LEAPRAID_SCSI_HOST_SHIFT;
|
|
break;
|
|
|
|
case LEAPRAID_ADAPTER_STATUS_BUSY:
|
|
case LEAPRAID_ADAPTER_STATUS_INSUFFICIENT_RESOURCES:
|
|
scmd->result = SAM_STAT_BUSY;
|
|
break;
|
|
|
|
case LEAPRAID_ADAPTER_STATUS_SCSI_RESIDUAL_MISMATCH:
|
|
if (xfer_cnt == 0 || scmd->underflow > xfer_cnt)
|
|
scmd->result = DID_SOFT_ERROR <<
|
|
LEAPRAID_SCSI_HOST_SHIFT;
|
|
else
|
|
scmd->result = (DID_OK << LEAPRAID_SCSI_HOST_SHIFT) |
|
|
scsi_status;
|
|
break;
|
|
|
|
case LEAPRAID_ADAPTER_STATUS_SCSI_ADAPTER_TERMINATED:
|
|
if (sdev_priv->block) {
|
|
scmd->result = DID_TRANSPORT_DISRUPTED <<
|
|
LEAPRAID_SCSI_HOST_SHIFT;
|
|
return;
|
|
}
|
|
|
|
if (scmd->device->channel == RAID_CHANNEL &&
|
|
scsi_state == (LEAPRAID_SCSI_STATE_TERMINATED |
|
|
LEAPRAID_SCSI_STATE_NO_SCSI_STATUS)) {
|
|
scmd->result = DID_RESET << LEAPRAID_SCSI_HOST_SHIFT;
|
|
break;
|
|
}
|
|
|
|
scmd->result = DID_SOFT_ERROR << LEAPRAID_SCSI_HOST_SHIFT;
|
|
break;
|
|
|
|
case LEAPRAID_ADAPTER_STATUS_SCSI_TASK_TERMINATED:
|
|
case LEAPRAID_ADAPTER_STATUS_SCSI_EXT_TERMINATED:
|
|
scmd->result = DID_RESET << LEAPRAID_SCSI_HOST_SHIFT;
|
|
break;
|
|
|
|
case LEAPRAID_ADAPTER_STATUS_SCSI_DATA_UNDERRUN:
|
|
leapraid_handle_data_underrun(scsiio_rep, scmd, xfer_cnt);
|
|
break;
|
|
|
|
case LEAPRAID_ADAPTER_STATUS_SCSI_DATA_OVERRUN:
|
|
scsi_set_resid(scmd, 0);
|
|
leapraid_handle_success_status(scsiio_rep, scmd,
|
|
response_code);
|
|
break;
|
|
case LEAPRAID_ADAPTER_STATUS_SCSI_RECOVERED_ERROR:
|
|
case LEAPRAID_ADAPTER_STATUS_SUCCESS:
|
|
leapraid_handle_success_status(scsiio_rep, scmd,
|
|
response_code);
|
|
break;
|
|
|
|
case LEAPRAID_ADAPTER_STATUS_SCSI_PROTOCOL_ERROR:
|
|
case LEAPRAID_ADAPTER_STATUS_INTERNAL_ERROR:
|
|
case LEAPRAID_ADAPTER_STATUS_SCSI_IO_DATA_ERROR:
|
|
case LEAPRAID_ADAPTER_STATUS_SCSI_TASK_MGMT_FAILED:
|
|
default:
|
|
scmd->result = DID_SOFT_ERROR << LEAPRAID_SCSI_HOST_SHIFT;
|
|
break;
|
|
}
|
|
|
|
if (!scmd->result)
|
|
return;
|
|
|
|
scsi_print_command(scmd);
|
|
dev_warn(&adapter->pdev->dev,
|
|
"SCSI I/O: hdl=0x%x, status: 0x%x, 0x%x, 0x%x\n",
|
|
le16_to_cpu(scsiio_rep->dev_hdl), adapter_status,
|
|
scsi_status, scsi_state);
|
|
|
|
if (scsi_state & LEAPRAID_SCSI_STATE_AUTOSENSE_VALID) {
|
|
struct scsi_sense_hdr sshdr;
|
|
|
|
sz = min_t(u32, SCSI_SENSE_BUFFERSIZE,
|
|
le32_to_cpu(scsiio_rep->sense_count));
|
|
if (scsi_normalize_sense(scmd->sense_buffer, sz,
|
|
&sshdr))
|
|
dev_warn(&adapter->pdev->dev,
|
|
"Sense: key=0x%x asc=0x%x ascq=0x%x\n",
|
|
sshdr.sense_key, sshdr.asc,
|
|
sshdr.ascq);
|
|
else
|
|
dev_warn(&adapter->pdev->dev,
|
|
"Sense: Invalid sense data\n");
|
|
}
|
|
}
|
|
|
|
bool leapraid_scsiio_done(struct leapraid_adapter *adapter, u16 taskid,
|
|
u8 msix_index, u32 rep)
|
|
{
|
|
struct leapraid_scsiio_rep *scsiio_rep;
|
|
struct leapraid_sdev_priv *sdev_priv;
|
|
struct scsi_cmnd *scmd;
|
|
u32 response_code = 0;
|
|
|
|
scmd = leapraid_get_scmd_from_taskid(adapter, taskid);
|
|
if (!scmd)
|
|
return true;
|
|
|
|
scsiio_rep = leapraid_get_reply_vaddr(adapter, rep);
|
|
if (!scsiio_rep) {
|
|
scmd->result = DID_OK << LEAPRAID_SCSI_HOST_SHIFT;
|
|
goto out_scsiio_done;
|
|
}
|
|
|
|
sdev_priv = scmd->device->hostdata;
|
|
if (!sdev_priv ||
|
|
!sdev_priv->starget_priv ||
|
|
sdev_priv->starget_priv->deleted) {
|
|
scmd->result = DID_NO_CONNECT << LEAPRAID_SCSI_HOST_SHIFT;
|
|
goto out_scsiio_done;
|
|
}
|
|
|
|
if (scsiio_rep->scsi_state & LEAPRAID_SCSI_STATE_RESPONSE_INFO_VALID)
|
|
response_code = le32_to_cpu(scsiio_rep->resp_info) & 0xFF;
|
|
|
|
leapraid_process_sense_data(adapter, scsiio_rep, scmd, taskid);
|
|
leapraid_scsiio_done_dispatch(adapter, scsiio_rep, sdev_priv, scmd,
|
|
taskid, response_code);
|
|
|
|
out_scsiio_done:
|
|
scsi_dma_unmap(scmd);
|
|
leapraid_free_taskid(adapter, taskid);
|
|
scsi_done(scmd);
|
|
return false;
|
|
}
|
|
|
|
static void leapraid_probe_raid(struct leapraid_adapter *adapter)
|
|
{
|
|
struct leapraid_raid_volume *raid_volume, *next_raid_volume;
|
|
unsigned long flags;
|
|
LIST_HEAD(head);
|
|
int rc;
|
|
|
|
spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags);
|
|
list_splice_init(&adapter->dev_topo.raid_volume_list, &head);
|
|
spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags);
|
|
|
|
list_for_each_entry_safe(raid_volume, next_raid_volume, &head, list) {
|
|
spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags);
|
|
list_move_tail(&raid_volume->list,
|
|
&adapter->dev_topo.raid_volume_list);
|
|
if (raid_volume->starget) {
|
|
spin_unlock_irqrestore(
|
|
&adapter->dev_topo.raid_volume_lock, flags);
|
|
continue;
|
|
}
|
|
|
|
leapraid_raid_volume_get(raid_volume);
|
|
spin_unlock_irqrestore(
|
|
&adapter->dev_topo.raid_volume_lock, flags);
|
|
|
|
rc = scsi_add_device(adapter->shost, RAID_CHANNEL,
|
|
raid_volume->id, 0);
|
|
if (rc)
|
|
leapraid_raid_volume_remove(adapter, raid_volume);
|
|
|
|
leapraid_raid_volume_put(raid_volume);
|
|
}
|
|
}
|
|
|
|
static void leapraid_sas_dev_make_active(struct leapraid_adapter *adapter,
|
|
struct leapraid_sas_dev *sas_dev)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
|
|
if (!list_empty(&sas_dev->list)) {
|
|
list_del_init(&sas_dev->list);
|
|
leapraid_sdev_put(sas_dev);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
static void leapraid_probe_sas(struct leapraid_adapter *adapter)
|
|
{
|
|
struct leapraid_sas_dev *sas_dev;
|
|
bool added;
|
|
|
|
for (;;) {
|
|
sas_dev = leapraid_get_next_sas_dev_from_init_list(adapter);
|
|
if (!sas_dev)
|
|
break;
|
|
added = leapraid_transport_port_add(adapter,
|
|
sas_dev->hdl,
|
|
sas_dev->parent_sas_addr,
|
|
sas_dev->card_port);
|
|
if (!added)
|
|
goto remove_dev;
|
|
|
|
if (!sas_dev->starget &&
|
|
!adapter->scan_dev_desc.driver_loading) {
|
|
leapraid_transport_port_remove(adapter,
|
|
sas_dev->sas_addr,
|
|
sas_dev->parent_sas_addr,
|
|
sas_dev->card_port);
|
|
goto remove_dev;
|
|
}
|
|
|
|
leapraid_sas_dev_make_active(adapter, sas_dev);
|
|
leapraid_sdev_put(sas_dev);
|
|
continue;
|
|
|
|
remove_dev:
|
|
leapraid_sas_dev_remove(adapter, sas_dev);
|
|
leapraid_sdev_put(sas_dev);
|
|
}
|
|
}
|
|
|
|
static bool leapraid_get_boot_dev(struct leapraid_adapter *adapter,
|
|
struct leapraid_boot_dev *boot_dev,
|
|
void **pdev, u32 *pchnl)
|
|
{
|
|
unsigned long flags;
|
|
void *dev;
|
|
u32 chnl;
|
|
|
|
spin_lock_irqsave(&adapter->boot_devs.lock, flags);
|
|
if (!boot_dev->dev) {
|
|
spin_unlock_irqrestore(&adapter->boot_devs.lock, flags);
|
|
return false;
|
|
}
|
|
|
|
dev = boot_dev->dev;
|
|
chnl = boot_dev->chnl;
|
|
leapraid_boot_dev_get(dev, chnl);
|
|
spin_unlock_irqrestore(&adapter->boot_devs.lock, flags);
|
|
|
|
*pdev = dev;
|
|
*pchnl = chnl;
|
|
return true;
|
|
}
|
|
|
|
static void leapraid_probe_boot_dev(struct leapraid_adapter *adapter)
|
|
{
|
|
struct leapraid_raid_volume *raid_volume;
|
|
struct leapraid_sas_dev *sas_dev;
|
|
struct leapraid_sas_port *sport;
|
|
unsigned long flags;
|
|
void *dev = NULL;
|
|
u32 chnl;
|
|
|
|
if (leapraid_get_boot_dev(adapter,
|
|
&adapter->boot_devs.requested_boot_dev,
|
|
&dev, &chnl))
|
|
goto boot_dev_found;
|
|
|
|
if (leapraid_get_boot_dev(adapter,
|
|
&adapter->boot_devs.requested_alt_boot_dev,
|
|
&dev, &chnl))
|
|
goto boot_dev_found;
|
|
|
|
if (leapraid_get_boot_dev(adapter,
|
|
&adapter->boot_devs.current_boot_dev,
|
|
&dev, &chnl))
|
|
goto boot_dev_found;
|
|
|
|
return;
|
|
|
|
boot_dev_found:
|
|
switch (chnl) {
|
|
case RAID_CHANNEL:
|
|
raid_volume = dev;
|
|
|
|
if (raid_volume->starget)
|
|
break;
|
|
|
|
/* TODO eedp */
|
|
|
|
if (scsi_add_device(adapter->shost, RAID_CHANNEL,
|
|
raid_volume->id, 0))
|
|
leapraid_raid_volume_remove(adapter, raid_volume);
|
|
break;
|
|
default:
|
|
sas_dev = dev;
|
|
|
|
if (sas_dev->starget)
|
|
break;
|
|
|
|
spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
|
|
list_move_tail(&sas_dev->list,
|
|
&adapter->dev_topo.sas_dev_list);
|
|
spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
|
|
|
|
if (!sas_dev->card_port)
|
|
break;
|
|
|
|
sport = leapraid_transport_port_add(adapter, sas_dev->hdl,
|
|
sas_dev->parent_sas_addr,
|
|
sas_dev->card_port);
|
|
if (!sport)
|
|
leapraid_sas_dev_remove(adapter, sas_dev);
|
|
break;
|
|
}
|
|
|
|
leapraid_boot_dev_put(dev, chnl);
|
|
}
|
|
|
|
static void leapraid_probe_devices(struct leapraid_adapter *adapter)
|
|
{
|
|
leapraid_probe_boot_dev(adapter);
|
|
|
|
if (adapter->adapter_attr.raid_support) {
|
|
leapraid_probe_raid(adapter);
|
|
leapraid_probe_sas(adapter);
|
|
} else {
|
|
leapraid_probe_sas(adapter);
|
|
}
|
|
}
|
|
|
|
void leapraid_scan_dev_done(struct leapraid_adapter *adapter)
|
|
{
|
|
if (adapter->scan_dev_desc.wait_scan_dev_done) {
|
|
adapter->scan_dev_desc.wait_scan_dev_done = 0;
|
|
leapraid_probe_devices(adapter);
|
|
}
|
|
|
|
adapter->scan_dev_desc.scan_start = 0;
|
|
|
|
leapraid_check_scheduled_fault_start(adapter);
|
|
leapraid_fw_log_start(adapter);
|
|
adapter->scan_dev_desc.driver_loading = 0;
|
|
wake_up(&adapter->scan_dev_desc.wait_driver_loading);
|
|
}
|
|
|
|
static const struct pci_device_id leapraid_pci_table[] = {
|
|
{ PCI_DEVICE_SUB(LEAPRAID_VENDOR_ID, LEAPRAID_DEVID_HBA,
|
|
LEAPRAID_SUBVENDOR_ID,
|
|
LEAPRAID_SUBDEVID_HBA) },
|
|
{ 0, }
|
|
};
|
|
|
|
static inline bool leapraid_is_scmd_permitted(struct leapraid_adapter *adapter,
|
|
struct scsi_cmnd *scmd)
|
|
{
|
|
u8 opcode;
|
|
|
|
if (adapter->access_ctrl.pcie_recovering ||
|
|
atomic_read(&adapter->overheat_desc.thermal_alert))
|
|
return false;
|
|
|
|
if (adapter->access_ctrl.host_removing) {
|
|
if (leapraid_pci_removed(adapter))
|
|
return false;
|
|
|
|
opcode = scmd->cmnd[0];
|
|
return opcode == SYNCHRONIZE_CACHE || opcode == START_STOP;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool leapraid_should_queuecommand(struct leapraid_adapter *adapter,
|
|
struct leapraid_sdev_priv *sdev_priv,
|
|
struct scsi_cmnd *scmd,
|
|
enum scsi_qc_status *rc)
|
|
{
|
|
struct leapraid_starget_priv *starget_priv;
|
|
|
|
if (!sdev_priv || !sdev_priv->starget_priv)
|
|
goto no_connect;
|
|
|
|
if (!leapraid_is_scmd_permitted(adapter, scmd))
|
|
goto no_connect;
|
|
|
|
starget_priv = sdev_priv->starget_priv;
|
|
if (starget_priv->hdl == LEAPRAID_INVALID_DEV_HANDLE)
|
|
goto no_connect;
|
|
|
|
if (sdev_priv->block &&
|
|
scsi_get_host_state(scmd->device->host) == SHOST_RECOVERY &&
|
|
scmd->cmnd[0] == TEST_UNIT_READY) {
|
|
scsi_build_sense(scmd, 0, UNIT_ATTENTION,
|
|
LEAPRAID_SCSI_ASC_POWER_ON_RESET,
|
|
LEAPRAID_SCSI_ASCQ_POWER_ON_RESET);
|
|
goto scsiio_done;
|
|
}
|
|
|
|
if (adapter->access_ctrl.shost_recovering ||
|
|
adapter->reset_desc.adapter_link_resetting) {
|
|
*rc = SCSI_MLQUEUE_HOST_BUSY;
|
|
return false;
|
|
}
|
|
|
|
if (starget_priv->deleted || sdev_priv->deleted)
|
|
goto no_connect;
|
|
|
|
if (starget_priv->tm_busy || sdev_priv->block) {
|
|
*rc = SCSI_MLQUEUE_DEVICE_BUSY;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
|
|
no_connect:
|
|
scmd->result = DID_NO_CONNECT << LEAPRAID_SCSI_HOST_SHIFT;
|
|
scsiio_done:
|
|
scsi_done(scmd);
|
|
|
|
return false;
|
|
}
|
|
|
|
static u32 build_scsiio_req_control(struct scsi_cmnd *scmd,
|
|
struct leapraid_sdev_priv *sdev_priv)
|
|
{
|
|
u32 control;
|
|
|
|
switch (scmd->sc_data_direction) {
|
|
case DMA_FROM_DEVICE:
|
|
control = LEAPRAID_SCSIIO_CTRL_READ;
|
|
break;
|
|
case DMA_TO_DEVICE:
|
|
control = LEAPRAID_SCSIIO_CTRL_WRITE;
|
|
break;
|
|
default:
|
|
control = LEAPRAID_SCSIIO_CTRL_NODATATRANSFER;
|
|
break;
|
|
}
|
|
|
|
control |= LEAPRAID_SCSIIO_CTRL_SIMPLEQ;
|
|
|
|
if (sdev_priv->ncq_prio_enable &&
|
|
(IOPRIO_PRIO_CLASS(req_get_ioprio(scsi_cmd_to_rq(scmd))) ==
|
|
IOPRIO_CLASS_RT))
|
|
control |= LEAPRAID_SCSIIO_CTRL_CMDPRI;
|
|
if (scmd->cmd_len == 32)
|
|
control |= LEAPRAID_SCSIIO_CTRL_CDB_32BYTE <<
|
|
LEAPRAID_SCSIIO_CTRL_CDB_LEN_SHIFT;
|
|
|
|
return control;
|
|
}
|
|
|
|
enum scsi_qc_status leapraid_queuecommand(struct Scsi_Host *shost,
|
|
struct scsi_cmnd *scmd)
|
|
{
|
|
struct leapraid_adapter *adapter = shost_priv(scmd->device->host);
|
|
struct leapraid_sdev_priv *sdev_priv = scmd->device->hostdata;
|
|
struct leapraid_starget_priv *starget_priv;
|
|
struct leapraid_scsiio_req *scsiio_req;
|
|
u32 control;
|
|
u16 taskid;
|
|
u16 hdl;
|
|
enum scsi_qc_status rc = 0;
|
|
|
|
if (!leapraid_should_queuecommand(adapter, sdev_priv, scmd, &rc))
|
|
return rc;
|
|
|
|
starget_priv = sdev_priv->starget_priv;
|
|
hdl = starget_priv->hdl;
|
|
control = build_scsiio_req_control(scmd, sdev_priv);
|
|
|
|
taskid = leapraid_alloc_scsiio_taskid(adapter, scmd);
|
|
scsiio_req = leapraid_get_task_desc(adapter, taskid);
|
|
|
|
scsiio_req->func = LEAPRAID_FUNC_SCSIIO;
|
|
if (sdev_priv->starget_priv->flg & LEAPRAID_TGT_FLG_RAID_MEMBER)
|
|
scsiio_req->func = LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH;
|
|
else
|
|
scsiio_req->func = LEAPRAID_FUNC_SCSIIO;
|
|
|
|
scsiio_req->dev_hdl = cpu_to_le16(hdl);
|
|
scsiio_req->data_len = cpu_to_le32(scsi_bufflen(scmd));
|
|
scsiio_req->ctrl = cpu_to_le32(control);
|
|
scsiio_req->io_flg = cpu_to_le16(scmd->cmd_len);
|
|
scsiio_req->msg_flg = 0;
|
|
scsiio_req->sense_buffer_len = SCSI_SENSE_BUFFERSIZE;
|
|
scsiio_req->sense_buffer_low_add =
|
|
leapraid_get_sense_buffer_dma(adapter, taskid);
|
|
scsiio_req->sgl_offset0 =
|
|
offsetof(struct leapraid_scsiio_req, sgl) /
|
|
LEAPRAID_DWORDS_BYTE_SIZE;
|
|
int_to_scsilun(sdev_priv->lun, (struct scsi_lun *)scsiio_req->lun);
|
|
memcpy(scsiio_req->cdb.cdb32, scmd->cmnd, scmd->cmd_len);
|
|
if (scsiio_req->data_len) {
|
|
if (leapraid_build_scmd_ieee_sg(adapter, scmd, taskid)) {
|
|
leapraid_free_taskid(adapter, taskid);
|
|
return SCSI_MLQUEUE_HOST_BUSY;
|
|
}
|
|
} else {
|
|
leapraid_build_ieee_nodata_sg(adapter, &scsiio_req->sgl);
|
|
}
|
|
|
|
if (likely(scsiio_req->func == LEAPRAID_FUNC_SCSIIO))
|
|
leapraid_fire_scsi_io(adapter, taskid,
|
|
le16_to_cpu(scsiio_req->dev_hdl));
|
|
else
|
|
leapraid_fire_task(adapter, taskid);
|
|
|
|
dev_dbg(&adapter->pdev->dev,
|
|
"LEAPRAID_SCSIIO: Send Descriptor taskid %d, req type 0x%x\n",
|
|
taskid, scsiio_req->func);
|
|
return rc;
|
|
}
|
|
|
|
static int leapraid_init_cmd_priv(struct Scsi_Host *shost,
|
|
struct scsi_cmnd *scmd)
|
|
{
|
|
struct leapraid_adapter *adapter = shost_priv(shost);
|
|
struct leapraid_io_req_tracker *io_tracker;
|
|
|
|
io_tracker = scsi_cmd_priv(scmd);
|
|
leapraid_internal_init_cmd_priv(adapter, io_tracker);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int leapraid_exit_cmd_priv(struct Scsi_Host *shost,
|
|
struct scsi_cmnd *scmd)
|
|
{
|
|
struct leapraid_adapter *adapter = shost_priv(shost);
|
|
struct leapraid_io_req_tracker *io_tracker;
|
|
|
|
io_tracker = scsi_cmd_priv(scmd);
|
|
leapraid_internal_exit_cmd_priv(adapter, io_tracker);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int leapraid_error_handler(struct scsi_cmnd *scmd,
|
|
const char *str, u8 type)
|
|
{
|
|
struct leapraid_adapter *adapter = shost_priv(scmd->device->host);
|
|
struct scsi_target *starget = scmd->device->sdev_target;
|
|
struct leapraid_starget_priv *starget_priv = starget->hostdata;
|
|
struct leapraid_io_req_tracker *io_req_tracker = NULL;
|
|
struct leapraid_sdev_priv *sdev_priv;
|
|
struct leapraid_sas_dev *sas_dev = NULL;
|
|
u16 hdl;
|
|
int rc;
|
|
|
|
dev_info(&adapter->pdev->dev,
|
|
"EH enter: type=%s, scmd=0x%p, req tag=%d\n", str, scmd,
|
|
scsi_cmd_to_rq(scmd)->tag);
|
|
scsi_print_command(scmd);
|
|
|
|
if (type == LEAPRAID_TM_TASKTYPE_ABORT_TASK) {
|
|
io_req_tracker = scsi_cmd_priv(scmd);
|
|
dev_info(&adapter->pdev->dev,
|
|
"EH ABORT: scmd=0x%p, pend=%ums, tout=%ums, tag=%d\n",
|
|
scmd,
|
|
jiffies_to_msecs(jiffies - scmd->jiffies_at_alloc),
|
|
(scsi_cmd_to_rq(scmd)->timeout / HZ) * 1000,
|
|
scsi_cmd_to_rq(scmd)->tag);
|
|
}
|
|
|
|
if (leapraid_pci_removed(adapter) ||
|
|
adapter->access_ctrl.host_removing) {
|
|
dev_err(&adapter->pdev->dev,
|
|
"EH %s failed: %s scmd=0x%p\n", str,
|
|
(adapter->access_ctrl.host_removing ?
|
|
"shost removing!" : "pci_dev removed!"), scmd);
|
|
if (type == LEAPRAID_TM_TASKTYPE_ABORT_TASK &&
|
|
io_req_tracker && io_req_tracker->taskid)
|
|
leapraid_free_taskid(adapter, io_req_tracker->taskid);
|
|
scmd->result = DID_NO_CONNECT << LEAPRAID_SCSI_HOST_SHIFT;
|
|
#ifdef FAST_IO_FAIL
|
|
rc = FAST_IO_FAIL;
|
|
#else
|
|
rc = FAILED;
|
|
#endif
|
|
goto out_eh_done;
|
|
}
|
|
|
|
sdev_priv = scmd->device->hostdata;
|
|
if (!sdev_priv || !sdev_priv->starget_priv) {
|
|
dev_warn(&adapter->pdev->dev,
|
|
"EH %s: SAS dev or starget gone, scmd=0x%p\n",
|
|
str, scmd);
|
|
scmd->result = DID_NO_CONNECT << LEAPRAID_SCSI_HOST_SHIFT;
|
|
scsi_done(scmd);
|
|
rc = SUCCESS;
|
|
goto out_eh_done;
|
|
}
|
|
|
|
if (type == LEAPRAID_TM_TASKTYPE_ABORT_TASK) {
|
|
if (!io_req_tracker) {
|
|
dev_warn(&adapter->pdev->dev,
|
|
"EH ABORT: No I/O tracker, scmd 0x%p\n", scmd);
|
|
scmd->result = DID_RESET << LEAPRAID_SCSI_HOST_SHIFT;
|
|
rc = SUCCESS;
|
|
goto out_eh_done;
|
|
}
|
|
|
|
if (sdev_priv->starget_priv->flg &
|
|
LEAPRAID_TGT_FLG_RAID_MEMBER ||
|
|
sdev_priv->starget_priv->flg & LEAPRAID_TGT_FLG_VOLUME) {
|
|
dev_err(&adapter->pdev->dev,
|
|
"EH ABORT: Skip RAID/VOLUME, scmd=0x%p\n",
|
|
scmd);
|
|
scmd->result = DID_RESET << LEAPRAID_SCSI_HOST_SHIFT;
|
|
rc = FAILED;
|
|
goto out_eh_done;
|
|
}
|
|
|
|
hdl = sdev_priv->starget_priv->hdl;
|
|
} else {
|
|
hdl = 0;
|
|
if (sdev_priv->starget_priv->flg &
|
|
LEAPRAID_TGT_FLG_RAID_MEMBER) {
|
|
sas_dev = leapraid_get_sas_dev_from_tgt(adapter,
|
|
starget_priv);
|
|
if (sas_dev)
|
|
hdl = sas_dev->volume_hdl;
|
|
} else {
|
|
hdl = sdev_priv->starget_priv->hdl;
|
|
}
|
|
|
|
if (!hdl) {
|
|
dev_err(&adapter->pdev->dev,
|
|
"EH %s failed: Target handle 0, scmd=0x%p\n",
|
|
str, scmd);
|
|
scmd->result = DID_RESET << LEAPRAID_SCSI_HOST_SHIFT;
|
|
rc = FAILED;
|
|
goto out_eh_done;
|
|
}
|
|
}
|
|
|
|
dev_info(&adapter->pdev->dev,
|
|
"EH issue TM: type=%s, scmd=0x%p, hdl=0x%x\n",
|
|
str, scmd, hdl);
|
|
|
|
rc = leapraid_issue_locked_tm(
|
|
adapter,
|
|
hdl,
|
|
scmd->device->channel,
|
|
scmd->device->id,
|
|
(type == LEAPRAID_TM_TASKTYPE_TARGET_RESET ?
|
|
0 : scmd->device->lun),
|
|
type,
|
|
(type == LEAPRAID_TM_TASKTYPE_ABORT_TASK ?
|
|
io_req_tracker->taskid : 0),
|
|
LEAPRAID_TM_MSGFLAGS_LINK_RESET);
|
|
|
|
out_eh_done:
|
|
if (type == LEAPRAID_TM_TASKTYPE_ABORT_TASK) {
|
|
if (rc != SUCCESS)
|
|
dev_err(&adapter->pdev->dev,
|
|
"EH ABORT result: failed, scmd=0x%p\n",
|
|
scmd);
|
|
} else {
|
|
if (rc != SUCCESS)
|
|
dev_err(&adapter->pdev->dev,
|
|
"EH %s result: failed, scmd=0x%p\n",
|
|
str, scmd);
|
|
if (sas_dev)
|
|
leapraid_sdev_put(sas_dev);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static int leapraid_eh_abort_handler(struct scsi_cmnd *scmd)
|
|
{
|
|
return leapraid_error_handler(scmd, "ABORT TASK",
|
|
LEAPRAID_TM_TASKTYPE_ABORT_TASK);
|
|
}
|
|
|
|
static int leapraid_eh_device_reset_handler(struct scsi_cmnd *scmd)
|
|
{
|
|
return leapraid_error_handler(scmd, "UNIT RESET",
|
|
LEAPRAID_TM_TASKTYPE_LOGICAL_UNIT_RESET);
|
|
}
|
|
|
|
static int leapraid_eh_target_reset_handler(struct scsi_cmnd *scmd)
|
|
{
|
|
return leapraid_error_handler(scmd, "TARGET RESET",
|
|
LEAPRAID_TM_TASKTYPE_TARGET_RESET);
|
|
}
|
|
|
|
static int leapraid_eh_host_reset_handler(struct scsi_cmnd *scmd)
|
|
{
|
|
struct leapraid_adapter *adapter = shost_priv(scmd->device->host);
|
|
int rc;
|
|
|
|
dev_info(&adapter->pdev->dev,
|
|
"EH HOST RESET enter: scmd=%p, req tag=%d\n",
|
|
scmd,
|
|
scsi_cmd_to_rq(scmd)->tag);
|
|
scsi_print_command(scmd);
|
|
|
|
if (adapter->scan_dev_desc.driver_loading ||
|
|
adapter->access_ctrl.host_removing) {
|
|
dev_err(&adapter->pdev->dev,
|
|
"EH HOST RESET failed: %s scmd=0x%p\n",
|
|
(adapter->access_ctrl.host_removing ?
|
|
"shost removing!" : "driver loading!"), scmd);
|
|
rc = FAILED;
|
|
goto out_host_reset_done;
|
|
}
|
|
|
|
dev_info(&adapter->pdev->dev, "%s:%d: Issuing hard reset\n",
|
|
__func__, __LINE__);
|
|
if (leapraid_hard_reset_handler(adapter, FULL_RESET) < 0)
|
|
rc = FAILED;
|
|
else
|
|
rc = SUCCESS;
|
|
|
|
out_host_reset_done:
|
|
if (rc != SUCCESS)
|
|
dev_err(&adapter->pdev->dev,
|
|
"EH HOST RESET result: failed, scmd=0x%p\n",
|
|
scmd);
|
|
return rc;
|
|
}
|
|
|
|
static int leapraid_sdev_init(struct scsi_device *sdev)
|
|
{
|
|
struct leapraid_raid_volume *raid_volume;
|
|
struct leapraid_starget_priv *stgt_priv;
|
|
struct leapraid_sdev_priv *sdev_priv;
|
|
struct leapraid_adapter *adapter;
|
|
struct leapraid_sas_dev *sas_dev;
|
|
struct scsi_target *tgt;
|
|
struct Scsi_Host *shost;
|
|
unsigned long flags;
|
|
|
|
sdev_priv = kzalloc_obj(*sdev_priv);
|
|
if (!sdev_priv)
|
|
return -ENOMEM;
|
|
|
|
sdev_priv->lun = sdev->lun;
|
|
sdev_priv->flg = LEAPRAID_DEVICE_FLG_INIT;
|
|
tgt = scsi_target(sdev);
|
|
stgt_priv = tgt->hostdata;
|
|
stgt_priv->num_luns++;
|
|
sdev_priv->starget_priv = stgt_priv;
|
|
sdev->hostdata = sdev_priv;
|
|
if (stgt_priv->flg & LEAPRAID_TGT_FLG_RAID_MEMBER)
|
|
sdev->no_uld_attach = LEAPRAID_NO_ULD_ATTACH;
|
|
|
|
shost = dev_to_shost(&tgt->dev);
|
|
adapter = shost_priv(shost);
|
|
if (tgt->channel == RAID_CHANNEL) {
|
|
raid_volume = leapraid_raid_volume_find_by_id(adapter,
|
|
tgt->id,
|
|
tgt->channel);
|
|
if (raid_volume) {
|
|
spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock,
|
|
flags);
|
|
raid_volume->sdev = sdev;
|
|
spin_unlock_irqrestore(
|
|
&adapter->dev_topo.raid_volume_lock, flags);
|
|
leapraid_raid_volume_put(raid_volume);
|
|
}
|
|
}
|
|
|
|
if (!(stgt_priv->flg & LEAPRAID_TGT_FLG_VOLUME)) {
|
|
spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
|
|
sas_dev = leapraid_hold_lock_get_sas_dev_by_addr(
|
|
adapter,
|
|
stgt_priv->sas_address,
|
|
stgt_priv->card_port);
|
|
if (sas_dev && !sas_dev->starget) {
|
|
sdev_printk(KERN_INFO, sdev,
|
|
"%s: Assign starget to sas_dev\n",
|
|
__func__);
|
|
sas_dev->starget = tgt;
|
|
}
|
|
|
|
if (sas_dev)
|
|
leapraid_sdev_put(sas_dev);
|
|
spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static bool leapraid_slave_cfg_volume(struct scsi_device *sdev,
|
|
struct queue_limits *lim)
|
|
{
|
|
struct Scsi_Host *shost = sdev->host;
|
|
struct leapraid_adapter *adapter = shost_priv(shost);
|
|
struct leapraid_raid_volume *raid_volume;
|
|
struct leapraid_starget_priv *starget_priv;
|
|
struct leapraid_sdev_priv *sdev_priv;
|
|
int qd;
|
|
u16 hdl;
|
|
|
|
sdev_priv = sdev->hostdata;
|
|
starget_priv = sdev_priv->starget_priv;
|
|
hdl = starget_priv->hdl;
|
|
|
|
raid_volume = leapraid_raid_volume_find_by_hdl(adapter, hdl);
|
|
if (!raid_volume) {
|
|
sdev_printk(KERN_WARNING, sdev,
|
|
"%s: RAID volume not found, hdl=0x%x\n",
|
|
__func__, hdl);
|
|
return 1;
|
|
}
|
|
|
|
if (leapraid_get_volume_cap(adapter, raid_volume)) {
|
|
sdev_printk(KERN_ERR, sdev,
|
|
"%s: Failed to get volume cap, hdl=0x%x\n",
|
|
__func__, hdl);
|
|
leapraid_raid_volume_put(raid_volume);
|
|
return 1;
|
|
}
|
|
|
|
qd = (raid_volume->dev_info & LEAPRAID_DEVTYP_SSP_TGT) ?
|
|
adapter->adapter_attr.narrowport_max_queue_depth :
|
|
adapter->adapter_attr.sata_max_queue_depth;
|
|
if (raid_volume->vol_type != LEAPRAID_VOL_TYPE_RAID0)
|
|
qd = adapter->adapter_attr.raid_volume_max_queue_depth;
|
|
|
|
sdev_printk(KERN_INFO, sdev,
|
|
"RAID volume: hdl=0x%04x, wwid=0x%016llx\n",
|
|
raid_volume->hdl, (unsigned long long)raid_volume->wwid);
|
|
|
|
if (shost->max_sectors > LEAPRAID_MAX_SECTORS)
|
|
lim->max_hw_sectors = LEAPRAID_MAX_SECTORS;
|
|
|
|
leapraid_change_queue_depth(sdev, qd);
|
|
leapraid_raid_volume_put(raid_volume);
|
|
return 0;
|
|
}
|
|
|
|
static bool leapraid_slave_configure_extra(struct scsi_device *sdev,
|
|
struct leapraid_sas_dev **psas_dev,
|
|
u16 vol_hdl, u64 volume_wwid,
|
|
bool *is_target_ssp, int *qd)
|
|
{
|
|
struct leapraid_sas_dev *sas_dev;
|
|
struct leapraid_sdev_priv *sdev_priv;
|
|
struct Scsi_Host *shost = sdev->host;
|
|
struct leapraid_adapter *adapter = shost_priv(shost);
|
|
unsigned long flags;
|
|
|
|
sdev_priv = sdev->hostdata;
|
|
spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
|
|
*is_target_ssp = false;
|
|
sas_dev = leapraid_hold_lock_get_sas_dev_by_addr(
|
|
adapter,
|
|
sdev_priv->starget_priv->sas_address,
|
|
sdev_priv->starget_priv->card_port);
|
|
if (!sas_dev) {
|
|
spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
|
|
sdev_printk(KERN_WARNING, sdev,
|
|
"%s: SAS dev not found, sas=0x%llx\n",
|
|
__func__, sdev_priv->starget_priv->sas_address);
|
|
return 1;
|
|
}
|
|
|
|
*psas_dev = sas_dev;
|
|
sas_dev->volume_hdl = vol_hdl;
|
|
sas_dev->volume_wwid = volume_wwid;
|
|
if (sas_dev->dev_info & LEAPRAID_DEVTYP_SSP_TGT) {
|
|
*qd = (sas_dev->port_connection > 1) ?
|
|
adapter->adapter_attr.wideport_max_queue_depth :
|
|
adapter->adapter_attr.narrowport_max_queue_depth;
|
|
*is_target_ssp = true;
|
|
if (sas_dev->dev_info & LEAPRAID_DEVTYP_SEP)
|
|
sdev_priv->sep = 1;
|
|
} else {
|
|
*qd = adapter->adapter_attr.sata_max_queue_depth;
|
|
}
|
|
|
|
sdev_printk(KERN_INFO, sdev,
|
|
"device name=0x%016llx, SAS addr=0x%016llx\n",
|
|
(unsigned long long)sas_dev->dev_name,
|
|
(unsigned long long)sas_dev->sas_addr);
|
|
leapraid_sdev_put(sas_dev);
|
|
spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
|
|
return 0;
|
|
}
|
|
|
|
static int leapraid_sdev_configure(struct scsi_device *sdev,
|
|
struct queue_limits *lim)
|
|
{
|
|
struct leapraid_sas_dev *sas_dev;
|
|
struct leapraid_sdev_priv *sdev_priv;
|
|
struct Scsi_Host *shost = sdev->host;
|
|
struct leapraid_starget_priv *starget_priv;
|
|
struct leapraid_adapter *adapter;
|
|
u16 hdl, vol_hdl = 0;
|
|
bool is_target_ssp = false;
|
|
u64 volume_wwid = 0;
|
|
int qd = 1;
|
|
|
|
adapter = shost_priv(shost);
|
|
sdev_priv = sdev->hostdata;
|
|
sdev_priv->flg &= ~LEAPRAID_DEVICE_FLG_INIT;
|
|
starget_priv = sdev_priv->starget_priv;
|
|
hdl = starget_priv->hdl;
|
|
if (starget_priv->flg & LEAPRAID_TGT_FLG_VOLUME)
|
|
return leapraid_slave_cfg_volume(sdev, lim);
|
|
|
|
if (starget_priv->flg & LEAPRAID_TGT_FLG_RAID_MEMBER) {
|
|
if (leapraid_cfg_get_volume_hdl(adapter, hdl, &vol_hdl)) {
|
|
sdev_printk(KERN_WARNING, sdev,
|
|
"%s: Get volume hdl failed, hdl=0x%x\n",
|
|
__func__, hdl);
|
|
return 1;
|
|
}
|
|
|
|
if (vol_hdl && leapraid_cfg_get_volume_wwid(adapter, vol_hdl,
|
|
&volume_wwid)) {
|
|
sdev_printk(KERN_WARNING, sdev,
|
|
"%s: Get wwid failed, volume_hdl=0x%x\n",
|
|
__func__, vol_hdl);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (leapraid_slave_configure_extra(sdev, &sas_dev, vol_hdl,
|
|
volume_wwid, &is_target_ssp, &qd)) {
|
|
sdev_printk(KERN_WARNING, sdev,
|
|
"%s: slave_configure_extra failed\n", __func__);
|
|
return 1;
|
|
}
|
|
|
|
leapraid_change_queue_depth(sdev, qd);
|
|
if (is_target_ssp)
|
|
sas_read_port_mode_page(sdev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void leapraid_sdev_destroy(struct scsi_device *sdev)
|
|
{
|
|
struct leapraid_adapter *adapter;
|
|
struct Scsi_Host *shost;
|
|
struct leapraid_sas_dev *sas_dev;
|
|
struct leapraid_starget_priv *starget_priv;
|
|
struct scsi_target *stgt;
|
|
unsigned long flags;
|
|
|
|
if (!sdev->hostdata)
|
|
return;
|
|
|
|
stgt = scsi_target(sdev);
|
|
starget_priv = stgt->hostdata;
|
|
starget_priv->num_luns--;
|
|
shost = dev_to_shost(&stgt->dev);
|
|
adapter = shost_priv(shost);
|
|
if (!(starget_priv->flg & LEAPRAID_TGT_FLG_VOLUME)) {
|
|
spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
|
|
sas_dev = leapraid_hold_lock_get_sas_dev_from_tgt(adapter,
|
|
starget_priv);
|
|
if (sas_dev && !starget_priv->num_luns)
|
|
sas_dev->starget = NULL;
|
|
if (sas_dev)
|
|
leapraid_sdev_put(sas_dev);
|
|
spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
|
|
}
|
|
|
|
kfree(sdev->hostdata);
|
|
sdev->hostdata = NULL;
|
|
}
|
|
|
|
static int leapraid_target_alloc_raid(struct scsi_target *tgt)
|
|
{
|
|
struct leapraid_starget_priv *starget_priv;
|
|
struct leapraid_raid_volume *raid_volume;
|
|
struct Scsi_Host *shost = dev_to_shost(&tgt->dev);
|
|
struct leapraid_adapter *adapter = shost_priv(shost);
|
|
unsigned long flags;
|
|
|
|
starget_priv = tgt->hostdata;
|
|
raid_volume = leapraid_raid_volume_find_by_id(adapter, tgt->id,
|
|
tgt->channel);
|
|
if (raid_volume) {
|
|
spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags);
|
|
starget_priv->hdl = raid_volume->hdl;
|
|
starget_priv->sas_address = raid_volume->wwid;
|
|
starget_priv->flg |= LEAPRAID_TGT_FLG_VOLUME;
|
|
raid_volume->starget = tgt;
|
|
spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock,
|
|
flags);
|
|
leapraid_raid_volume_put(raid_volume);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int leapraid_target_alloc_sas(struct scsi_target *tgt)
|
|
{
|
|
struct sas_rphy *rphy;
|
|
struct Scsi_Host *shost;
|
|
struct leapraid_sas_dev *sas_dev;
|
|
struct leapraid_adapter *adapter;
|
|
struct leapraid_starget_priv *starget_priv;
|
|
unsigned long flags;
|
|
|
|
shost = dev_to_shost(&tgt->dev);
|
|
adapter = shost_priv(shost);
|
|
starget_priv = tgt->hostdata;
|
|
spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
|
|
rphy = dev_to_rphy(tgt->dev.parent);
|
|
sas_dev = leapraid_hold_lock_get_sas_dev_by_addr_and_rphy(
|
|
adapter,
|
|
rphy->identify.sas_address,
|
|
rphy);
|
|
if (sas_dev) {
|
|
starget_priv->sas_dev = sas_dev;
|
|
starget_priv->card_port = sas_dev->card_port;
|
|
starget_priv->sas_address = sas_dev->sas_addr;
|
|
starget_priv->hdl = sas_dev->hdl;
|
|
sas_dev->channel = tgt->channel;
|
|
sas_dev->id = tgt->id;
|
|
sas_dev->starget = tgt;
|
|
if (sas_dev->hdl &&
|
|
sas_dev->hdl <=
|
|
adapter->adapter_attr.features.max_dev_handle &&
|
|
test_bit(sas_dev->hdl, adapter->dev_topo.pd_hdls))
|
|
starget_priv->flg |= LEAPRAID_TGT_FLG_RAID_MEMBER;
|
|
}
|
|
spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int leapraid_target_alloc(struct scsi_target *tgt)
|
|
{
|
|
struct leapraid_starget_priv *starget_priv;
|
|
|
|
starget_priv = kzalloc_obj(*starget_priv);
|
|
if (!starget_priv)
|
|
return -ENOMEM;
|
|
|
|
tgt->hostdata = starget_priv;
|
|
starget_priv->starget = tgt;
|
|
starget_priv->hdl = LEAPRAID_INVALID_DEV_HANDLE;
|
|
if (tgt->channel == RAID_CHANNEL)
|
|
return leapraid_target_alloc_raid(tgt);
|
|
|
|
return leapraid_target_alloc_sas(tgt);
|
|
}
|
|
|
|
static void leapraid_target_destroy_raid(struct scsi_target *tgt)
|
|
{
|
|
struct leapraid_raid_volume *raid_volume;
|
|
struct Scsi_Host *shost = dev_to_shost(&tgt->dev);
|
|
struct leapraid_adapter *adapter = shost_priv(shost);
|
|
unsigned long flags;
|
|
|
|
raid_volume = leapraid_raid_volume_find_by_id(adapter, tgt->id,
|
|
tgt->channel);
|
|
if (raid_volume) {
|
|
spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags);
|
|
raid_volume->starget = NULL;
|
|
raid_volume->sdev = NULL;
|
|
spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock,
|
|
flags);
|
|
leapraid_raid_volume_put(raid_volume);
|
|
}
|
|
}
|
|
|
|
static void leapraid_target_destroy_sas(struct scsi_target *tgt)
|
|
{
|
|
struct leapraid_adapter *adapter;
|
|
struct leapraid_sas_dev *sas_dev;
|
|
struct leapraid_starget_priv *starget_priv;
|
|
struct Scsi_Host *shost;
|
|
unsigned long flags;
|
|
|
|
shost = dev_to_shost(&tgt->dev);
|
|
adapter = shost_priv(shost);
|
|
starget_priv = tgt->hostdata;
|
|
|
|
spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
|
|
sas_dev = leapraid_hold_lock_get_sas_dev_from_tgt(adapter,
|
|
starget_priv);
|
|
if (sas_dev &&
|
|
sas_dev->starget == tgt &&
|
|
sas_dev->id == tgt->id &&
|
|
sas_dev->channel == tgt->channel)
|
|
sas_dev->starget = NULL;
|
|
|
|
if (sas_dev) {
|
|
starget_priv->sas_dev = NULL;
|
|
leapraid_sdev_put(sas_dev);
|
|
leapraid_sdev_put(sas_dev);
|
|
}
|
|
spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
|
|
}
|
|
|
|
static void leapraid_target_destroy(struct scsi_target *tgt)
|
|
{
|
|
struct leapraid_starget_priv *starget_priv;
|
|
|
|
starget_priv = tgt->hostdata;
|
|
if (!starget_priv)
|
|
return;
|
|
|
|
if (tgt->channel == RAID_CHANNEL) {
|
|
leapraid_target_destroy_raid(tgt);
|
|
goto out_free;
|
|
}
|
|
|
|
leapraid_target_destroy_sas(tgt);
|
|
|
|
out_free:
|
|
kfree(starget_priv);
|
|
tgt->hostdata = NULL;
|
|
}
|
|
|
|
static bool leapraid_scan_check_status(struct leapraid_adapter *adapter,
|
|
bool *need_hard_reset)
|
|
{
|
|
u32 adapter_state;
|
|
|
|
if (adapter->scan_dev_desc.scan_start) {
|
|
adapter_state = leapraid_get_adapter_state(adapter);
|
|
if (adapter_state == LEAPRAID_DB_FAULT) {
|
|
*need_hard_reset = true;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (adapter->driver_cmds.scan_dev_cmd.status & LEAPRAID_CMD_RESET) {
|
|
dev_err(&adapter->pdev->dev,
|
|
"Device scan: Aborted due to reset\n");
|
|
adapter->driver_cmds.scan_dev_cmd.status =
|
|
LEAPRAID_CMD_NOT_USED;
|
|
adapter->scan_dev_desc.driver_loading = 0;
|
|
wake_up(&adapter->scan_dev_desc.wait_driver_loading);
|
|
return true;
|
|
}
|
|
|
|
if (adapter->scan_dev_desc.scan_start_failed) {
|
|
dev_err(&adapter->pdev->dev,
|
|
"Device scan: Failed with adapter_status=0x%08x\n",
|
|
adapter->scan_dev_desc.scan_start_failed);
|
|
adapter->scan_dev_desc.driver_loading = 0;
|
|
wake_up(&adapter->scan_dev_desc.wait_driver_loading);
|
|
adapter->scan_dev_desc.wait_scan_dev_done = 0;
|
|
adapter->access_ctrl.host_removing = 1;
|
|
return true;
|
|
}
|
|
|
|
adapter->driver_cmds.scan_dev_cmd.status = LEAPRAID_CMD_NOT_USED;
|
|
leapraid_scan_dev_done(adapter);
|
|
return true;
|
|
}
|
|
|
|
static int leapraid_scan_finished(struct Scsi_Host *shost, unsigned long time)
|
|
{
|
|
struct leapraid_adapter *adapter = shost_priv(shost);
|
|
bool need_hard_reset = false;
|
|
|
|
if (time >= (LEAPRAID_SCAN_DEV_CMD_TIMEOUT * HZ)) {
|
|
adapter->driver_cmds.scan_dev_cmd.status =
|
|
LEAPRAID_CMD_NOT_USED;
|
|
dev_err(&adapter->pdev->dev,
|
|
"Device scan: Failed with timeout 300s\n");
|
|
adapter->scan_dev_desc.driver_loading = 0;
|
|
wake_up(&adapter->scan_dev_desc.wait_driver_loading);
|
|
return 1;
|
|
}
|
|
|
|
if (!leapraid_scan_check_status(adapter, &need_hard_reset))
|
|
return 0;
|
|
|
|
if (need_hard_reset) {
|
|
adapter->driver_cmds.scan_dev_cmd.status =
|
|
LEAPRAID_CMD_NOT_USED;
|
|
dev_info(&adapter->pdev->dev, "%s:%d: call hard_reset\n",
|
|
__func__, __LINE__);
|
|
if (leapraid_hard_reset_handler(adapter, PART_RESET)) {
|
|
adapter->scan_dev_desc.driver_loading = 0;
|
|
wake_up(&adapter->scan_dev_desc.wait_driver_loading);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void leapraid_scan_start(struct Scsi_Host *shost)
|
|
{
|
|
struct leapraid_adapter *adapter = shost_priv(shost);
|
|
|
|
adapter->scan_dev_desc.scan_start = 1;
|
|
leapraid_scan_dev(adapter, true);
|
|
}
|
|
|
|
static u32 leapraid_get_raid_qd(struct leapraid_adapter *adapter,
|
|
const struct leapraid_raid_volume *raid_volume,
|
|
bool default_qd)
|
|
{
|
|
const struct leapraid_adapter_attr *attr = &adapter->adapter_attr;
|
|
|
|
if (raid_volume->vol_type != LEAPRAID_VOL_TYPE_RAID0)
|
|
return default_qd ? LEAPRAID_RAID_QUEUE_DEPTH :
|
|
attr->raid_volume_max_queue_depth;
|
|
|
|
if (raid_volume->dev_info & LEAPRAID_DEVTYP_SSP_TGT)
|
|
return default_qd ? LEAPRAID_SAS_QUEUE_DEPTH :
|
|
attr->narrowport_max_queue_depth;
|
|
|
|
return default_qd ? LEAPRAID_SATA_QUEUE_DEPTH :
|
|
attr->sata_max_queue_depth;
|
|
}
|
|
|
|
static int leapraid_calc_max_queue_depth(struct scsi_device *sdev, int qdepth)
|
|
{
|
|
struct Scsi_Host *shost;
|
|
struct leapraid_adapter *adapter;
|
|
struct leapraid_starget_priv *starget_priv;
|
|
struct leapraid_sdev_priv *sdev_priv;
|
|
struct leapraid_raid_volume *raid_volume;
|
|
struct leapraid_sas_dev *sas_dev;
|
|
int max_depth;
|
|
u32 default_qdepth = 0;
|
|
u32 fw_qdepth = 0;
|
|
|
|
shost = sdev->host;
|
|
adapter = shost_priv(shost);
|
|
max_depth = shost->can_queue;
|
|
|
|
sdev_priv = sdev->hostdata;
|
|
if (!sdev_priv)
|
|
goto out_tag_check;
|
|
|
|
starget_priv = sdev_priv->starget_priv;
|
|
if (!starget_priv)
|
|
goto out_tag_check;
|
|
|
|
if (starget_priv->flg & LEAPRAID_TGT_FLG_VOLUME) {
|
|
raid_volume = leapraid_raid_volume_find_by_hdl(
|
|
adapter, starget_priv->hdl);
|
|
if (raid_volume) {
|
|
default_qdepth = leapraid_get_raid_qd(
|
|
adapter, raid_volume, true);
|
|
fw_qdepth = leapraid_get_raid_qd(
|
|
adapter, raid_volume, false);
|
|
leapraid_raid_volume_put(raid_volume);
|
|
}
|
|
goto out_limit_check;
|
|
}
|
|
|
|
sas_dev = leapraid_get_sas_dev_from_tgt(adapter, starget_priv);
|
|
if (sas_dev) {
|
|
if (sas_dev->dev_info & LEAPRAID_DEVTYP_SSP_TGT) {
|
|
default_qdepth = LEAPRAID_SAS_QUEUE_DEPTH;
|
|
fw_qdepth = (sas_dev->port_connection > 1) ?
|
|
adapter->adapter_attr.wideport_max_queue_depth :
|
|
adapter->adapter_attr.narrowport_max_queue_depth;
|
|
}
|
|
if (sas_dev->dev_info & LEAPRAID_DEVTYP_SATA_DEV) {
|
|
default_qdepth = LEAPRAID_SATA_QUEUE_DEPTH;
|
|
fw_qdepth = adapter->adapter_attr.sata_max_queue_depth;
|
|
}
|
|
leapraid_sdev_put(sas_dev);
|
|
}
|
|
|
|
out_limit_check:
|
|
if (fw_qdepth > shost->can_queue && default_qdepth)
|
|
fw_qdepth = default_qdepth;
|
|
|
|
if (fw_qdepth)
|
|
max_depth = min_t(int, max_depth, fw_qdepth);
|
|
|
|
out_tag_check:
|
|
if (!sdev->tagged_supported)
|
|
max_depth = 1;
|
|
|
|
if (qdepth > max_depth)
|
|
qdepth = max_depth;
|
|
|
|
return qdepth;
|
|
}
|
|
|
|
int leapraid_change_queue_depth(struct scsi_device *sdev, int qdepth)
|
|
{
|
|
qdepth = leapraid_calc_max_queue_depth(sdev, qdepth);
|
|
scsi_change_queue_depth(sdev, qdepth);
|
|
return sdev->queue_depth;
|
|
}
|
|
|
|
static void leapraid_map_queues(struct Scsi_Host *shost)
|
|
{
|
|
struct leapraid_adapter *adapter;
|
|
struct blk_mq_queue_map *queue_map;
|
|
int msix_queue_count;
|
|
int poll_queue_count;
|
|
int queue_offset;
|
|
int map_index;
|
|
|
|
adapter = (struct leapraid_adapter *)shost->hostdata;
|
|
if (shost->nr_hw_queues == 1)
|
|
return;
|
|
|
|
msix_queue_count = adapter->notification_desc.iopoll_qdex;
|
|
poll_queue_count = adapter->adapter_attr.rq_cnt - msix_queue_count;
|
|
|
|
queue_offset = 0;
|
|
for (map_index = 0; map_index < shost->nr_maps; map_index++) {
|
|
queue_map = &shost->tag_set.map[map_index];
|
|
queue_map->nr_queues = 0;
|
|
|
|
switch (map_index) {
|
|
case HCTX_TYPE_DEFAULT:
|
|
queue_map->nr_queues = msix_queue_count;
|
|
queue_map->queue_offset = queue_offset;
|
|
WARN_ON_ONCE(!queue_map->nr_queues);
|
|
blk_mq_map_hw_queues(queue_map,
|
|
&adapter->pdev->dev, 0);
|
|
break;
|
|
case HCTX_TYPE_POLL:
|
|
queue_map->nr_queues = poll_queue_count;
|
|
queue_map->queue_offset = queue_offset;
|
|
blk_mq_map_queues(queue_map);
|
|
break;
|
|
default:
|
|
queue_map->queue_offset = queue_offset;
|
|
blk_mq_map_hw_queues(queue_map,
|
|
&adapter->pdev->dev, 0);
|
|
break;
|
|
}
|
|
queue_offset += queue_map->nr_queues;
|
|
}
|
|
}
|
|
|
|
int leapraid_blk_mq_poll(struct Scsi_Host *shost, unsigned int queue_num)
|
|
{
|
|
struct leapraid_adapter *adapter =
|
|
(struct leapraid_adapter *)shost->hostdata;
|
|
struct leapraid_blk_mq_poll_rq *blk_mq_poll_rq;
|
|
int num_entries;
|
|
int qid = queue_num - adapter->notification_desc.iopoll_qdex;
|
|
|
|
blk_mq_poll_rq = &adapter->notification_desc.blk_mq_poll_rqs[qid];
|
|
if (atomic_read(&blk_mq_poll_rq->pause) ||
|
|
!atomic_add_unless(&blk_mq_poll_rq->busy, 1, 1))
|
|
return 0;
|
|
|
|
num_entries = leapraid_rep_queue_handler(&blk_mq_poll_rq->rq);
|
|
atomic_dec(&blk_mq_poll_rq->busy);
|
|
return num_entries;
|
|
}
|
|
|
|
static int leapraid_bios_param(struct scsi_device *sdev,
|
|
struct gendisk *disk, sector_t capacity,
|
|
int geom[])
|
|
{
|
|
int heads;
|
|
int sectors;
|
|
sector_t cylinders;
|
|
|
|
if (scsi_partsize(disk, capacity, geom))
|
|
return 0;
|
|
|
|
if ((ulong)capacity >= LEAPRAID_LARGE_DISK_THRESHOLD) {
|
|
heads = LEAPRAID_LARGE_DISK_HEADS;
|
|
sectors = LEAPRAID_LARGE_DISK_SECTORS;
|
|
} else {
|
|
heads = LEAPRAID_SMALL_DISK_HEADS;
|
|
sectors = LEAPRAID_SMALL_DISK_SECTORS;
|
|
}
|
|
|
|
cylinders = capacity;
|
|
sector_div(cylinders, heads * sectors);
|
|
|
|
geom[0] = heads;
|
|
geom[1] = sectors;
|
|
geom[2] = cylinders;
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t fw_queue_depth_show(struct device *cdev,
|
|
struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct Scsi_Host *shost = class_to_shost(cdev);
|
|
struct leapraid_adapter *adapter = shost_priv(shost);
|
|
|
|
return sysfs_emit(buf, "%02d\n",
|
|
adapter->adapter_attr.features.req_slot);
|
|
}
|
|
|
|
static ssize_t host_sas_address_show(struct device *cdev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct Scsi_Host *shost = class_to_shost(cdev);
|
|
struct leapraid_adapter *adapter = shost_priv(shost);
|
|
|
|
return sysfs_emit(buf, "0x%016llx\n",
|
|
(unsigned long long)adapter->dev_topo.card.sas_address);
|
|
}
|
|
|
|
static ssize_t board_name_show(struct device *cdev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct Scsi_Host *shost = class_to_shost(cdev);
|
|
struct leapraid_adapter *adapter = shost_priv(shost);
|
|
|
|
return sysfs_emit(buf, "%s\n", adapter->adapter_attr.board_name);
|
|
}
|
|
|
|
static DEVICE_ATTR_RO(fw_queue_depth);
|
|
static DEVICE_ATTR_RO(host_sas_address);
|
|
static DEVICE_ATTR_RO(board_name);
|
|
|
|
static struct attribute *leapraid_shost_attrs[] = {
|
|
&dev_attr_fw_queue_depth.attr,
|
|
&dev_attr_host_sas_address.attr,
|
|
&dev_attr_board_name.attr,
|
|
NULL,
|
|
};
|
|
|
|
ATTRIBUTE_GROUPS(leapraid_shost);
|
|
|
|
static ssize_t sas_device_handle_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct scsi_device *sdev = to_scsi_device(dev);
|
|
struct leapraid_sdev_priv *sas_device_priv_data = sdev->hostdata;
|
|
|
|
if (!sas_device_priv_data || !sas_device_priv_data->starget_priv) {
|
|
dev_err(&sdev->sdev_gendev,
|
|
"%s: Invalid sdev_priv or starget_priv\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return sysfs_emit(buf, "0x%04x\n",
|
|
sas_device_priv_data->starget_priv->hdl);
|
|
}
|
|
|
|
static ssize_t sas_ncq_prio_supported_show(struct device *dev,
|
|
struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct scsi_device *sdev = to_scsi_device(dev);
|
|
|
|
return sysfs_emit(buf, "%d\n", sas_ata_ncq_prio_supported(sdev));
|
|
}
|
|
|
|
static ssize_t sas_ncq_prio_enable_show(struct device *dev,
|
|
struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct scsi_device *sdev = to_scsi_device(dev);
|
|
struct leapraid_sdev_priv *sas_device_priv_data = sdev->hostdata;
|
|
|
|
if (!sas_device_priv_data) {
|
|
dev_err(&sdev->sdev_gendev,
|
|
"%s: Invalid sdev_priv\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return sysfs_emit(buf, "%d\n", sas_device_priv_data->ncq_prio_enable);
|
|
}
|
|
|
|
static ssize_t sas_ncq_prio_enable_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct scsi_device *sdev = to_scsi_device(dev);
|
|
struct leapraid_sdev_priv *sas_device_priv_data = sdev->hostdata;
|
|
bool enable;
|
|
|
|
if (!sas_device_priv_data) {
|
|
dev_err(&sdev->sdev_gendev,
|
|
"%s: Invalid sdev_priv\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (kstrtobool(buf, &enable))
|
|
return -EINVAL;
|
|
|
|
if (!sas_ata_ncq_prio_supported(sdev))
|
|
return -EINVAL;
|
|
|
|
sas_device_priv_data->ncq_prio_enable = enable;
|
|
return count;
|
|
}
|
|
|
|
static DEVICE_ATTR_RO(sas_device_handle);
|
|
static DEVICE_ATTR_RO(sas_ncq_prio_supported);
|
|
static DEVICE_ATTR_RW(sas_ncq_prio_enable);
|
|
|
|
static bool leapraid_sdev_is_sata(struct scsi_device *sdev)
|
|
{
|
|
struct scsi_target *starget = sdev->sdev_target;
|
|
struct leapraid_starget_priv *starget_priv = starget->hostdata;
|
|
struct leapraid_sas_dev *sas_dev;
|
|
|
|
if (!starget_priv)
|
|
return false;
|
|
|
|
sas_dev = starget_priv->sas_dev;
|
|
return sas_dev && (sas_dev->dev_info & LEAPRAID_DEVTYP_SATA_DEV);
|
|
}
|
|
|
|
static struct attribute *leapraid_sdev_attrs[] = {
|
|
&dev_attr_sas_device_handle.attr,
|
|
&dev_attr_sas_ncq_prio_supported.attr,
|
|
&dev_attr_sas_ncq_prio_enable.attr,
|
|
NULL,
|
|
};
|
|
|
|
static umode_t leapraid_sdev_attr_is_visible(struct kobject *kobj,
|
|
struct attribute *attr, int i)
|
|
{
|
|
struct device *dev = kobj_to_dev(kobj);
|
|
struct scsi_device *sdev = to_scsi_device(dev);
|
|
|
|
if (attr == &dev_attr_sas_ncq_prio_supported.attr ||
|
|
attr == &dev_attr_sas_ncq_prio_enable.attr)
|
|
if (!leapraid_sdev_is_sata(sdev))
|
|
return 0;
|
|
|
|
return attr->mode;
|
|
}
|
|
|
|
static const struct attribute_group leapraid_sdev_attr_group = {
|
|
.attrs = leapraid_sdev_attrs,
|
|
.is_visible = leapraid_sdev_attr_is_visible,
|
|
};
|
|
|
|
static const struct attribute_group *leapraid_sdev_groups[] = {
|
|
&leapraid_sdev_attr_group,
|
|
NULL,
|
|
};
|
|
|
|
static struct scsi_host_template leapraid_driver_template = {
|
|
.module = THIS_MODULE,
|
|
.name = "LEAPIO RAID Host",
|
|
.proc_name = LEAPRAID_DRIVER_NAME,
|
|
.queuecommand = leapraid_queuecommand,
|
|
.cmd_size = sizeof(struct leapraid_io_req_tracker),
|
|
.init_cmd_priv = leapraid_init_cmd_priv,
|
|
.exit_cmd_priv = leapraid_exit_cmd_priv,
|
|
.eh_abort_handler = leapraid_eh_abort_handler,
|
|
.eh_device_reset_handler = leapraid_eh_device_reset_handler,
|
|
.eh_target_reset_handler = leapraid_eh_target_reset_handler,
|
|
.eh_host_reset_handler = leapraid_eh_host_reset_handler,
|
|
.sdev_init = leapraid_sdev_init,
|
|
.sdev_destroy = leapraid_sdev_destroy,
|
|
.sdev_configure = leapraid_sdev_configure,
|
|
.target_alloc = leapraid_target_alloc,
|
|
.target_destroy = leapraid_target_destroy,
|
|
.scan_finished = leapraid_scan_finished,
|
|
.scan_start = leapraid_scan_start,
|
|
.change_queue_depth = leapraid_change_queue_depth,
|
|
.map_queues = leapraid_map_queues,
|
|
.mq_poll = leapraid_blk_mq_poll,
|
|
.bios_param = leapraid_bios_param,
|
|
.can_queue = LEAPRAID_CAN_QUEUE_MIN,
|
|
.this_id = LEAPRAID_THIS_ID_NONE,
|
|
.sg_tablesize = LEAPRAID_SG_DEPTH,
|
|
.max_sectors = LEAPRAID_MAX_SECTORS,
|
|
.max_segment_size = LEAPRAID_MAX_SEGMENT_SIZE,
|
|
.cmd_per_lun = LEAPRAID_CMD_PER_LUN,
|
|
.shost_groups = leapraid_shost_groups,
|
|
.sdev_groups = leapraid_sdev_groups,
|
|
.track_queue_depth = 1,
|
|
};
|
|
|
|
static void leapraid_lock_init(struct leapraid_adapter *adapter)
|
|
{
|
|
mutex_init(&adapter->reset_desc.adapter_reset_mutex);
|
|
mutex_init(&adapter->reset_desc.host_diag_mutex);
|
|
mutex_init(&adapter->access_ctrl.pci_access_lock);
|
|
|
|
spin_lock_init(&adapter->reset_desc.adapter_reset_lock);
|
|
spin_lock_init(&adapter->dynamic_task_desc.task_lock);
|
|
spin_lock_init(&adapter->dev_topo.sas_dev_lock);
|
|
spin_lock_init(&adapter->dev_topo.topo_node_lock);
|
|
spin_lock_init(&adapter->fw_evt_s.fw_evt_lock);
|
|
spin_lock_init(&adapter->dev_topo.raid_volume_lock);
|
|
spin_lock_init(&adapter->dev_topo.enc_lock);
|
|
spin_lock_init(&adapter->boot_devs.lock);
|
|
}
|
|
|
|
static void leapraid_list_init(struct leapraid_adapter *adapter)
|
|
{
|
|
INIT_LIST_HEAD(&adapter->dev_topo.sas_dev_list);
|
|
INIT_LIST_HEAD(&adapter->dev_topo.card_port_list);
|
|
INIT_LIST_HEAD(&adapter->dev_topo.sas_dev_init_list);
|
|
INIT_LIST_HEAD(&adapter->dev_topo.exp_list);
|
|
INIT_LIST_HEAD(&adapter->dev_topo.enc_list);
|
|
INIT_LIST_HEAD(&adapter->fw_evt_s.fw_evt_list);
|
|
INIT_LIST_HEAD(&adapter->dev_topo.raid_volume_list);
|
|
INIT_LIST_HEAD(&adapter->dev_topo.card.sas_port_list);
|
|
}
|
|
|
|
static int leapraid_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|
{
|
|
struct leapraid_adapter *adapter;
|
|
struct Scsi_Host *shost;
|
|
int iopoll_q_count;
|
|
int rc;
|
|
|
|
shost = scsi_host_alloc(&leapraid_driver_template,
|
|
sizeof(struct leapraid_adapter));
|
|
if (!shost) {
|
|
dev_err(&pdev->dev,
|
|
"%s: SCSI host alloc failed\n", __func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
adapter = shost_priv(shost);
|
|
memset(adapter, 0, sizeof(struct leapraid_adapter));
|
|
|
|
init_waitqueue_head(&adapter->access_ctrl.recovery_waitq);
|
|
adapter->adapter_attr.id = atomic_inc_return(&leapraid_ids) - 1;
|
|
|
|
adapter->adapter_attr.enable_mp = enable_mp;
|
|
|
|
adapter = shost_priv(shost);
|
|
INIT_LIST_HEAD(&adapter->list);
|
|
|
|
adapter->shost = shost;
|
|
adapter->pdev = pdev;
|
|
adapter->fw_log_desc.open_pcie_trace = open_pcie_trace;
|
|
atomic_set(&adapter->fw_log_desc.mmap_refcnt, 0);
|
|
init_waitqueue_head(&adapter->fw_log_desc.mmap_waitq);
|
|
leapraid_lock_init(adapter);
|
|
leapraid_list_init(adapter);
|
|
snprintf(adapter->adapter_attr.name, LEAPRAID_NAME_LENGTH, "%s%d",
|
|
LEAPRAID_DRIVER_NAME, adapter->adapter_attr.id);
|
|
|
|
shost->max_cmd_len = LEAPRAID_MAX_CDB_LEN;
|
|
shost->max_lun = LEAPRAID_MAX_LUNS;
|
|
shost->transportt = leapraid_transport_template;
|
|
shost->unique_id = adapter->adapter_attr.id;
|
|
|
|
adapter->fw_evt_s.fw_evt_thread =
|
|
alloc_ordered_workqueue("fw_event_%s%d", 0,
|
|
LEAPRAID_DRIVER_NAME,
|
|
adapter->adapter_attr.id);
|
|
if (!adapter->fw_evt_s.fw_evt_thread) {
|
|
dev_err(&adapter->pdev->dev,
|
|
"%s: Failed to create fw event workqueue\n", __func__);
|
|
rc = -ENODEV;
|
|
goto evt_wq_fail;
|
|
}
|
|
|
|
shost->host_tagset = 1;
|
|
init_waitqueue_head(&adapter->scan_dev_desc.wait_driver_loading);
|
|
adapter->scan_dev_desc.driver_loading = 1;
|
|
if (leapraid_ctrl_init(adapter)) {
|
|
dev_err(&adapter->pdev->dev,
|
|
"%s: Adapter init failed\n", __func__);
|
|
rc = -ENODEV;
|
|
goto ctrl_init_fail;
|
|
}
|
|
|
|
shost->nr_hw_queues = 1;
|
|
if (shost->host_tagset) {
|
|
shost->nr_hw_queues = adapter->adapter_attr.rq_cnt;
|
|
iopoll_q_count = adapter->adapter_attr.rq_cnt -
|
|
adapter->notification_desc.iopoll_qdex;
|
|
shost->nr_maps = iopoll_q_count ? 3 : 1;
|
|
dev_info(&adapter->pdev->dev,
|
|
"Max scsi I/O cmds %d shared with nr_hw_queues=%d\n",
|
|
shost->can_queue, shost->nr_hw_queues);
|
|
}
|
|
|
|
rc = scsi_add_host(shost, &pdev->dev);
|
|
if (rc) {
|
|
dev_err(&pdev->dev,
|
|
"%s: SCSI host add failed\n", __func__);
|
|
goto scsi_add_shost_fail;
|
|
}
|
|
|
|
spin_lock(&leapraid_adapter_lock);
|
|
list_add_tail(&adapter->list, &leapraid_adapter_list);
|
|
spin_unlock(&leapraid_adapter_lock);
|
|
|
|
scsi_scan_host(shost);
|
|
return 0;
|
|
|
|
scsi_add_shost_fail:
|
|
leapraid_remove_ctrl(adapter);
|
|
ctrl_init_fail:
|
|
leapraid_overheat_cleanup(adapter);
|
|
destroy_workqueue(adapter->fw_evt_s.fw_evt_thread);
|
|
evt_wq_fail:
|
|
scsi_host_put(shost);
|
|
return rc;
|
|
}
|
|
|
|
void leapraid_cleanup_lists(struct leapraid_adapter *adapter)
|
|
{
|
|
struct leapraid_raid_volume *raid_volume, *next_raid_volume;
|
|
struct leapraid_starget_priv *starget_priv_data;
|
|
struct leapraid_sas_port *leapraid_port, *next_port;
|
|
struct leapraid_card_port *port, *port_next;
|
|
struct leapraid_vphy *vphy, *vphy_next;
|
|
|
|
list_for_each_entry_safe(raid_volume, next_raid_volume,
|
|
&adapter->dev_topo.raid_volume_list, list) {
|
|
if (raid_volume->starget) {
|
|
starget_priv_data = raid_volume->starget->hostdata;
|
|
if (starget_priv_data)
|
|
starget_priv_data->deleted = 1;
|
|
scsi_remove_target(&raid_volume->starget->dev);
|
|
}
|
|
dev_info(&adapter->pdev->dev,
|
|
"removing hdl=0x%04x, wwid=0x%016llx\n",
|
|
raid_volume->hdl,
|
|
(unsigned long long)raid_volume->wwid);
|
|
leapraid_raid_volume_remove(adapter, raid_volume);
|
|
}
|
|
|
|
list_for_each_entry_safe(leapraid_port, next_port,
|
|
&adapter->dev_topo.card.sas_port_list,
|
|
port_list) {
|
|
if (leapraid_port->remote_identify.device_type ==
|
|
SAS_END_DEVICE)
|
|
leapraid_sas_dev_remove_by_sas_address(
|
|
adapter,
|
|
leapraid_port->remote_identify.sas_address,
|
|
leapraid_port->card_port);
|
|
else if (leapraid_port->remote_identify.device_type ==
|
|
SAS_EDGE_EXPANDER_DEVICE ||
|
|
leapraid_port->remote_identify.device_type ==
|
|
SAS_FANOUT_EXPANDER_DEVICE)
|
|
leapraid_exp_rm(
|
|
adapter,
|
|
leapraid_port->remote_identify.sas_address,
|
|
leapraid_port->card_port);
|
|
}
|
|
|
|
list_for_each_entry_safe(port, port_next,
|
|
&adapter->dev_topo.card_port_list, list) {
|
|
if (port->vphys_mask)
|
|
list_for_each_entry_safe(vphy, vphy_next,
|
|
&port->vphys_list, list) {
|
|
list_del(&vphy->list);
|
|
kfree(vphy);
|
|
}
|
|
list_del(&port->list);
|
|
kfree(port);
|
|
}
|
|
|
|
if (adapter->dev_topo.card.phys_num) {
|
|
kfree(adapter->dev_topo.card.card_phy);
|
|
adapter->dev_topo.card.card_phy = NULL;
|
|
adapter->dev_topo.card.phys_num = 0;
|
|
}
|
|
}
|
|
|
|
static void leapraid_remove(struct pci_dev *pdev)
|
|
{
|
|
struct leapraid_adapter *adapter = pdev_to_adapter(pdev);
|
|
struct Scsi_Host *shost = pci_get_drvdata(pdev);
|
|
struct workqueue_struct *wq;
|
|
unsigned long flags;
|
|
|
|
if (!shost || !adapter) {
|
|
dev_err(&pdev->dev, "Unable to remove!\n");
|
|
return;
|
|
}
|
|
|
|
wait_event(adapter->scan_dev_desc.wait_driver_loading,
|
|
!adapter->scan_dev_desc.driver_loading);
|
|
wait_event(adapter->scan_dev_desc.wait_driver_loading,
|
|
!atomic_read(&adapter->overheat_desc.thermal_alert));
|
|
|
|
WRITE_ONCE(adapter->access_ctrl.host_removing, 1);
|
|
spin_lock(&leapraid_adapter_lock);
|
|
list_del(&adapter->list);
|
|
spin_unlock(&leapraid_adapter_lock);
|
|
|
|
leapraid_wait_cmds_done(adapter);
|
|
|
|
if (leapraid_pci_removed(adapter)) {
|
|
leapraid_mq_polling_pause(adapter);
|
|
leapraid_clean_active_scsi_cmds(adapter);
|
|
}
|
|
leapraid_clean_active_fw_evt(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);
|
|
leapraid_remove_ctrl(adapter);
|
|
scsi_host_put(shost);
|
|
}
|
|
|
|
static void leapraid_shutdown(struct pci_dev *pdev)
|
|
{
|
|
struct leapraid_adapter *adapter = pdev_to_adapter(pdev);
|
|
struct Scsi_Host *shost = pci_get_drvdata(pdev);
|
|
struct workqueue_struct *wq;
|
|
unsigned long flags;
|
|
|
|
if (!shost || !adapter) {
|
|
dev_err(&pdev->dev, "Unable to shutdown!\n");
|
|
return;
|
|
}
|
|
|
|
adapter->access_ctrl.host_removing = 1;
|
|
leapraid_wait_cmds_done(adapter);
|
|
leapraid_clean_active_fw_evt(adapter);
|
|
leapraid_overheat_cleanup(adapter);
|
|
leapraid_fw_log_stop(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);
|
|
|
|
leapraid_disable_controller(adapter);
|
|
}
|
|
|
|
static pci_ers_result_t leapraid_pci_error_detected(struct pci_dev *pdev,
|
|
pci_channel_state_t state)
|
|
{
|
|
struct leapraid_adapter *adapter = pdev_to_adapter(pdev);
|
|
struct Scsi_Host *shost = pci_get_drvdata(pdev);
|
|
|
|
if (!shost || !adapter) {
|
|
dev_err(&pdev->dev, "Failed to error detected for device\n");
|
|
return PCI_ERS_RESULT_DISCONNECT;
|
|
}
|
|
|
|
dev_err(&pdev->dev, "%s: PCI error detected, state=%d\n",
|
|
adapter->adapter_attr.name, state);
|
|
|
|
switch (state) {
|
|
case pci_channel_io_normal:
|
|
return PCI_ERS_RESULT_CAN_RECOVER;
|
|
case pci_channel_io_frozen:
|
|
adapter->access_ctrl.pcie_recovering = 1;
|
|
scsi_block_requests(adapter->shost);
|
|
leapraid_overheat_cleanup(adapter);
|
|
leapraid_check_scheduled_fault_stop(adapter);
|
|
leapraid_fw_log_stop(adapter);
|
|
leapraid_disable_controller(adapter);
|
|
return PCI_ERS_RESULT_NEED_RESET;
|
|
case pci_channel_io_perm_failure:
|
|
adapter->access_ctrl.pcie_recovering = 1;
|
|
leapraid_overheat_cleanup(adapter);
|
|
leapraid_check_scheduled_fault_stop(adapter);
|
|
leapraid_fw_log_stop(adapter);
|
|
leapraid_mq_polling_pause(adapter);
|
|
leapraid_clean_active_scsi_cmds(adapter);
|
|
return PCI_ERS_RESULT_DISCONNECT;
|
|
}
|
|
|
|
return PCI_ERS_RESULT_NEED_RESET;
|
|
}
|
|
|
|
static pci_ers_result_t leapraid_pci_mmio_enabled(struct pci_dev *pdev)
|
|
{
|
|
struct leapraid_adapter *adapter = pdev_to_adapter(pdev);
|
|
struct Scsi_Host *shost = pci_get_drvdata(pdev);
|
|
|
|
if (!shost || !adapter) {
|
|
dev_err(&pdev->dev,
|
|
"Failed to enable mmio for device\n");
|
|
return PCI_ERS_RESULT_DISCONNECT;
|
|
}
|
|
|
|
dev_info(&pdev->dev, "%s: PCI error mmio enabled\n",
|
|
adapter->adapter_attr.name);
|
|
|
|
return PCI_ERS_RESULT_RECOVERED;
|
|
}
|
|
|
|
static pci_ers_result_t leapraid_pci_slot_reset(struct pci_dev *pdev)
|
|
{
|
|
struct leapraid_adapter *adapter = pdev_to_adapter(pdev);
|
|
struct Scsi_Host *shost = pci_get_drvdata(pdev);
|
|
int rc;
|
|
|
|
if (!shost || !adapter) {
|
|
dev_err(&pdev->dev,
|
|
"Failed to slot reset for device\n");
|
|
return PCI_ERS_RESULT_DISCONNECT;
|
|
}
|
|
|
|
dev_err(&pdev->dev, "%s PCI error slot reset\n",
|
|
adapter->adapter_attr.name);
|
|
|
|
adapter->pdev = pdev;
|
|
pci_restore_state(pdev);
|
|
if (leapraid_set_pcie_and_notification(adapter)) {
|
|
dev_err(&pdev->dev,
|
|
"%s: Failed to set PCIe state and notification\n",
|
|
__func__);
|
|
return PCI_ERS_RESULT_DISCONNECT;
|
|
}
|
|
|
|
adapter->access_ctrl.pcie_recovering = 0;
|
|
dev_info(&pdev->dev, "%s: Hard reset triggered by PCI slot reset\n",
|
|
adapter->adapter_attr.name);
|
|
dev_info(&adapter->pdev->dev, "%s: %d: call hard_reset\n",
|
|
__func__, __LINE__);
|
|
rc = leapraid_hard_reset_handler(adapter, FULL_RESET);
|
|
if (rc)
|
|
dev_err(&pdev->dev, "%s hard reset: failed\n",
|
|
adapter->adapter_attr.name);
|
|
|
|
return rc == 0 ? PCI_ERS_RESULT_RECOVERED :
|
|
PCI_ERS_RESULT_DISCONNECT;
|
|
}
|
|
|
|
static void leapraid_pci_resume(struct pci_dev *pdev)
|
|
{
|
|
struct Scsi_Host *shost = pci_get_drvdata(pdev);
|
|
struct leapraid_adapter *adapter = pdev_to_adapter(pdev);
|
|
|
|
if (!shost || !adapter) {
|
|
dev_err(&pdev->dev, "Failed to resume\n");
|
|
return;
|
|
}
|
|
|
|
dev_err(&pdev->dev, "PCI error resume!\n");
|
|
|
|
pci_aer_clear_nonfatal_status(pdev);
|
|
leapraid_check_scheduled_fault_start(adapter);
|
|
leapraid_fw_log_start(adapter);
|
|
scsi_unblock_requests(adapter->shost);
|
|
}
|
|
|
|
MODULE_DEVICE_TABLE(pci, leapraid_pci_table);
|
|
static struct pci_error_handlers leapraid_err_handler = {
|
|
.error_detected = leapraid_pci_error_detected,
|
|
.mmio_enabled = leapraid_pci_mmio_enabled,
|
|
.slot_reset = leapraid_pci_slot_reset,
|
|
.resume = leapraid_pci_resume,
|
|
};
|
|
|
|
#ifdef CONFIG_PM
|
|
static int leapraid_suspend(struct pci_dev *pdev, pm_message_t state)
|
|
{
|
|
struct leapraid_adapter *adapter = pdev_to_adapter(pdev);
|
|
struct Scsi_Host *shost = pci_get_drvdata(pdev);
|
|
pci_power_t device_state;
|
|
|
|
if (!shost || !adapter) {
|
|
dev_err(&pdev->dev,
|
|
"Suspend failed, invalid host or adapter\n");
|
|
return -ENXIO;
|
|
}
|
|
|
|
leapraid_overheat_cleanup(adapter);
|
|
leapraid_check_scheduled_fault_stop(adapter);
|
|
leapraid_fw_log_stop(adapter);
|
|
scsi_block_requests(shost);
|
|
device_state = pci_choose_state(pdev, state);
|
|
|
|
dev_info(&pdev->dev, "Entering PCI power state D%d, (slot=%s)\n",
|
|
device_state, pci_name(pdev));
|
|
|
|
pci_save_state(pdev);
|
|
leapraid_disable_controller(adapter);
|
|
pci_set_power_state(pdev, device_state);
|
|
return 0;
|
|
}
|
|
|
|
static int leapraid_resume(struct pci_dev *pdev)
|
|
{
|
|
struct leapraid_adapter *adapter = pdev_to_adapter(pdev);
|
|
struct Scsi_Host *shost = pci_get_drvdata(pdev);
|
|
pci_power_t device_state = pdev->current_state;
|
|
int rc;
|
|
|
|
if (!shost || !adapter) {
|
|
dev_err(&pdev->dev,
|
|
"Resume failed, invalid host or adapter\n");
|
|
return -ENXIO;
|
|
}
|
|
|
|
dev_info(&pdev->dev,
|
|
"Resuming device %s, previous state D%d\n",
|
|
pci_name(pdev), device_state);
|
|
|
|
pci_set_power_state(pdev, PCI_D0);
|
|
pci_enable_wake(pdev, PCI_D0, 0);
|
|
pci_restore_state(pdev);
|
|
adapter->pdev = pdev;
|
|
rc = leapraid_set_pcie_and_notification(adapter);
|
|
if (rc) {
|
|
dev_err(&pdev->dev,
|
|
"%s: Failed to set PCIe state and notification\n",
|
|
__func__);
|
|
return rc;
|
|
}
|
|
|
|
dev_info(&adapter->pdev->dev, "%s:%d: call hard_reset\n",
|
|
__func__, __LINE__);
|
|
rc = leapraid_hard_reset_handler(adapter, PART_RESET);
|
|
if (rc) {
|
|
dev_err(&adapter->pdev->dev,
|
|
"%s: Hard reset failed during resume, rc=%d\n",
|
|
__func__, rc);
|
|
return rc;
|
|
}
|
|
scsi_unblock_requests(shost);
|
|
leapraid_check_scheduled_fault_start(adapter);
|
|
leapraid_fw_log_start(adapter);
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_PM */
|
|
|
|
static struct pci_driver leapraid_driver = {
|
|
.name = LEAPRAID_DRIVER_NAME,
|
|
.id_table = leapraid_pci_table,
|
|
.probe = leapraid_probe,
|
|
.remove = leapraid_remove,
|
|
.shutdown = leapraid_shutdown,
|
|
.err_handler = &leapraid_err_handler,
|
|
#ifdef CONFIG_PM
|
|
.suspend = leapraid_suspend,
|
|
.resume = leapraid_resume,
|
|
#endif /* CONFIG_PM */
|
|
};
|
|
|
|
static int __init leapraid_init(void)
|
|
{
|
|
int error;
|
|
|
|
pr_info("%s initializing\n", LEAPRAID_DRIVER_NAME);
|
|
|
|
leapraid_transport_template =
|
|
sas_attach_transport(&leapraid_transport_functions);
|
|
if (!leapraid_transport_template) {
|
|
pr_err("%s: Failed to attach SAS transport\n",
|
|
LEAPRAID_DRIVER_NAME);
|
|
return -ENODEV;
|
|
}
|
|
|
|
error = pci_register_driver(&leapraid_driver);
|
|
if (error) {
|
|
pr_err("%s: PCI driver registration failed: %d\n",
|
|
LEAPRAID_DRIVER_NAME, error);
|
|
sas_release_transport(leapraid_transport_template);
|
|
return error;
|
|
}
|
|
|
|
error = leapraid_ctl_init();
|
|
if (error) {
|
|
pci_unregister_driver(&leapraid_driver);
|
|
sas_release_transport(leapraid_transport_template);
|
|
return error;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __exit leapraid_exit(void)
|
|
{
|
|
pr_info("%s exiting\n", LEAPRAID_DRIVER_NAME);
|
|
|
|
leapraid_ctl_exit();
|
|
pci_unregister_driver(&leapraid_driver);
|
|
sas_release_transport(leapraid_transport_template);
|
|
}
|
|
|
|
module_init(leapraid_init);
|
|
module_exit(leapraid_exit);
|