[SCSI] qla2xxx: Implementation of bidirectional.
[jejb: merge fix for introduced warning] Signed-off-by: Saurav Kashyap <saurav.kashyap@qlogic.com> Signed-off-by: Chad Dupuis <chad.dupuis@qlogic.com> Signed-off-by: James Bottomley <JBottomley@Parallels.com>
This commit is contained in:
parent
5f16b331d8
commit
a9b6f722f6
11 changed files with 656 additions and 12 deletions
|
@ -1251,6 +1251,31 @@ qla2x00_fw_state_show(struct device *dev, struct device_attribute *attr,
|
|||
state[1], state[2], state[3], state[4]);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
qla2x00_diag_requests_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
|
||||
|
||||
if (!IS_BIDI_CAPABLE(vha->hw))
|
||||
return snprintf(buf, PAGE_SIZE, "\n");
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%llu\n", vha->bidi_stats.io_count);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
qla2x00_diag_megabytes_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
|
||||
|
||||
if (!IS_BIDI_CAPABLE(vha->hw))
|
||||
return snprintf(buf, PAGE_SIZE, "\n");
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%llu\n",
|
||||
vha->bidi_stats.transfer_bytes >> 20);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(driver_version, S_IRUGO, qla2x00_drvr_version_show, NULL);
|
||||
static DEVICE_ATTR(fw_version, S_IRUGO, qla2x00_fw_version_show, NULL);
|
||||
static DEVICE_ATTR(serial_num, S_IRUGO, qla2x00_serial_num_show, NULL);
|
||||
|
@ -1289,6 +1314,8 @@ static DEVICE_ATTR(vn_port_mac_address, S_IRUGO,
|
|||
static DEVICE_ATTR(fabric_param, S_IRUGO, qla2x00_fabric_param_show, NULL);
|
||||
static DEVICE_ATTR(fw_state, S_IRUGO, qla2x00_fw_state_show, NULL);
|
||||
static DEVICE_ATTR(thermal_temp, S_IRUGO, qla2x00_thermal_temp_show, NULL);
|
||||
static DEVICE_ATTR(diag_requests, S_IRUGO, qla2x00_diag_requests_show, NULL);
|
||||
static DEVICE_ATTR(diag_megabytes, S_IRUGO, qla2x00_diag_megabytes_show, NULL);
|
||||
|
||||
struct device_attribute *qla2x00_host_attrs[] = {
|
||||
&dev_attr_driver_version,
|
||||
|
@ -1318,6 +1345,8 @@ struct device_attribute *qla2x00_host_attrs[] = {
|
|||
&dev_attr_fw_state,
|
||||
&dev_attr_optrom_gold_fw_version,
|
||||
&dev_attr_thermal_temp,
|
||||
&dev_attr_diag_requests,
|
||||
&dev_attr_diag_megabytes,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
|
|
@ -1649,6 +1649,186 @@ done:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
qla24xx_process_bidir_cmd(struct fc_bsg_job *bsg_job)
|
||||
{
|
||||
struct Scsi_Host *host = bsg_job->shost;
|
||||
scsi_qla_host_t *vha = shost_priv(host);
|
||||
struct qla_hw_data *ha = vha->hw;
|
||||
uint16_t thread_id;
|
||||
uint32_t rval = EXT_STATUS_OK;
|
||||
uint16_t req_sg_cnt = 0;
|
||||
uint16_t rsp_sg_cnt = 0;
|
||||
uint16_t nextlid = 0;
|
||||
uint32_t tot_dsds;
|
||||
srb_t *sp = NULL;
|
||||
uint32_t req_data_len = 0;
|
||||
uint32_t rsp_data_len = 0;
|
||||
|
||||
/* Check the type of the adapter */
|
||||
if (!IS_BIDI_CAPABLE(ha)) {
|
||||
ql_log(ql_log_warn, vha, 0x70a0,
|
||||
"This adapter is not supported\n");
|
||||
rval = EXT_STATUS_NOT_SUPPORTED;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) ||
|
||||
test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) ||
|
||||
test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) {
|
||||
rval = EXT_STATUS_BUSY;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check if host is online */
|
||||
if (!vha->flags.online) {
|
||||
ql_log(ql_log_warn, vha, 0x70a1,
|
||||
"Host is not online\n");
|
||||
rval = EXT_STATUS_DEVICE_OFFLINE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check if cable is plugged in or not */
|
||||
if (vha->device_flags & DFLG_NO_CABLE) {
|
||||
ql_log(ql_log_warn, vha, 0x70a2,
|
||||
"Cable is unplugged...\n");
|
||||
rval = EXT_STATUS_INVALID_CFG;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check if the switch is connected or not */
|
||||
if (ha->current_topology != ISP_CFG_F) {
|
||||
ql_log(ql_log_warn, vha, 0x70a3,
|
||||
"Host is not connected to the switch\n");
|
||||
rval = EXT_STATUS_INVALID_CFG;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check if operating mode is P2P */
|
||||
if (ha->operating_mode != P2P) {
|
||||
ql_log(ql_log_warn, vha, 0x70a4,
|
||||
"Host is operating mode is not P2p\n");
|
||||
rval = EXT_STATUS_INVALID_CFG;
|
||||
goto done;
|
||||
}
|
||||
|
||||
thread_id = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1];
|
||||
|
||||
mutex_lock(&ha->selflogin_lock);
|
||||
if (vha->self_login_loop_id == 0) {
|
||||
/* Initialize all required fields of fcport */
|
||||
vha->bidir_fcport.vha = vha;
|
||||
vha->bidir_fcport.d_id.b.al_pa = vha->d_id.b.al_pa;
|
||||
vha->bidir_fcport.d_id.b.area = vha->d_id.b.area;
|
||||
vha->bidir_fcport.d_id.b.domain = vha->d_id.b.domain;
|
||||
vha->bidir_fcport.loop_id = vha->loop_id;
|
||||
|
||||
if (qla2x00_fabric_login(vha, &(vha->bidir_fcport), &nextlid)) {
|
||||
ql_log(ql_log_warn, vha, 0x70a7,
|
||||
"Failed to login port %06X for bidirectional IOCB\n",
|
||||
vha->bidir_fcport.d_id.b24);
|
||||
mutex_unlock(&ha->selflogin_lock);
|
||||
rval = EXT_STATUS_MAILBOX;
|
||||
goto done;
|
||||
}
|
||||
vha->self_login_loop_id = nextlid - 1;
|
||||
|
||||
}
|
||||
/* Assign the self login loop id to fcport */
|
||||
mutex_unlock(&ha->selflogin_lock);
|
||||
|
||||
vha->bidir_fcport.loop_id = vha->self_login_loop_id;
|
||||
|
||||
req_sg_cnt = dma_map_sg(&ha->pdev->dev,
|
||||
bsg_job->request_payload.sg_list,
|
||||
bsg_job->request_payload.sg_cnt,
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
if (!req_sg_cnt) {
|
||||
rval = EXT_STATUS_NO_MEMORY;
|
||||
goto done;
|
||||
}
|
||||
|
||||
rsp_sg_cnt = dma_map_sg(&ha->pdev->dev,
|
||||
bsg_job->reply_payload.sg_list, bsg_job->reply_payload.sg_cnt,
|
||||
DMA_FROM_DEVICE);
|
||||
|
||||
if (!rsp_sg_cnt) {
|
||||
rval = EXT_STATUS_NO_MEMORY;
|
||||
goto done_unmap_req_sg;
|
||||
}
|
||||
|
||||
if ((req_sg_cnt != bsg_job->request_payload.sg_cnt) ||
|
||||
(rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) {
|
||||
ql_dbg(ql_dbg_user, vha, 0x70a9,
|
||||
"Dma mapping resulted in different sg counts "
|
||||
"[request_sg_cnt: %x dma_request_sg_cnt: %x reply_sg_cnt: "
|
||||
"%x dma_reply_sg_cnt: %x]\n",
|
||||
bsg_job->request_payload.sg_cnt, req_sg_cnt,
|
||||
bsg_job->reply_payload.sg_cnt, rsp_sg_cnt);
|
||||
rval = EXT_STATUS_NO_MEMORY;
|
||||
goto done_unmap_sg;
|
||||
}
|
||||
|
||||
if (req_data_len != rsp_data_len) {
|
||||
rval = EXT_STATUS_BUSY;
|
||||
ql_log(ql_log_warn, vha, 0x70aa,
|
||||
"req_data_len != rsp_data_len\n");
|
||||
goto done_unmap_sg;
|
||||
}
|
||||
|
||||
req_data_len = bsg_job->request_payload.payload_len;
|
||||
rsp_data_len = bsg_job->reply_payload.payload_len;
|
||||
|
||||
|
||||
/* Alloc SRB structure */
|
||||
sp = qla2x00_get_sp(vha, &(vha->bidir_fcport), GFP_KERNEL);
|
||||
if (!sp) {
|
||||
ql_dbg(ql_dbg_user, vha, 0x70ac,
|
||||
"Alloc SRB structure failed\n");
|
||||
rval = EXT_STATUS_NO_MEMORY;
|
||||
goto done_unmap_sg;
|
||||
}
|
||||
|
||||
/*Populate srb->ctx with bidir ctx*/
|
||||
sp->u.bsg_job = bsg_job;
|
||||
sp->free = qla2x00_bsg_sp_free;
|
||||
sp->type = SRB_BIDI_CMD;
|
||||
sp->done = qla2x00_bsg_job_done;
|
||||
|
||||
/* Add the read and write sg count */
|
||||
tot_dsds = rsp_sg_cnt + req_sg_cnt;
|
||||
|
||||
rval = qla2x00_start_bidir(sp, vha, tot_dsds);
|
||||
if (rval != EXT_STATUS_OK)
|
||||
goto done_free_srb;
|
||||
/* the bsg request will be completed in the interrupt handler */
|
||||
return rval;
|
||||
|
||||
done_free_srb:
|
||||
mempool_free(sp, ha->srb_mempool);
|
||||
done_unmap_sg:
|
||||
dma_unmap_sg(&ha->pdev->dev,
|
||||
bsg_job->reply_payload.sg_list,
|
||||
bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
|
||||
done_unmap_req_sg:
|
||||
dma_unmap_sg(&ha->pdev->dev,
|
||||
bsg_job->request_payload.sg_list,
|
||||
bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
|
||||
done:
|
||||
|
||||
/* Return an error vendor specific response
|
||||
* and complete the bsg request
|
||||
*/
|
||||
bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = rval;
|
||||
bsg_job->reply_len = sizeof(struct fc_bsg_reply);
|
||||
bsg_job->reply->reply_payload_rcv_len = 0;
|
||||
bsg_job->reply->result = (DID_OK) << 16;
|
||||
bsg_job->job_done(bsg_job);
|
||||
/* Always retrun success, vendor rsp carries correct status */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
qla2x00_process_vendor_specific(struct fc_bsg_job *bsg_job)
|
||||
{
|
||||
|
@ -1692,6 +1872,9 @@ qla2x00_process_vendor_specific(struct fc_bsg_job *bsg_job)
|
|||
case QL_VND_READ_I2C:
|
||||
return qla2x00_read_i2c(bsg_job);
|
||||
|
||||
case QL_VND_DIAG_IO_CMD:
|
||||
return qla24xx_process_bidir_cmd(bsg_job);
|
||||
|
||||
default:
|
||||
bsg_job->reply->result = (DID_ERROR << 16);
|
||||
bsg_job->job_done(bsg_job);
|
||||
|
|
|
@ -19,15 +19,31 @@
|
|||
#define QL_VND_SET_FRU_VERSION 0x0B
|
||||
#define QL_VND_READ_FRU_STATUS 0x0C
|
||||
#define QL_VND_WRITE_FRU_STATUS 0x0D
|
||||
#define QL_VND_DIAG_IO_CMD 0x0A
|
||||
#define QL_VND_WRITE_I2C 0x10
|
||||
#define QL_VND_READ_I2C 0x11
|
||||
|
||||
/* BSG Vendor specific subcode returns */
|
||||
#define EXT_STATUS_OK 0
|
||||
#define EXT_STATUS_ERR 1
|
||||
#define EXT_STATUS_BUSY 2
|
||||
#define EXT_STATUS_INVALID_PARAM 6
|
||||
#define EXT_STATUS_DATA_OVERRUN 7
|
||||
#define EXT_STATUS_DATA_UNDERRUN 8
|
||||
#define EXT_STATUS_MAILBOX 11
|
||||
#define EXT_STATUS_NO_MEMORY 17
|
||||
#define EXT_STATUS_DEVICE_OFFLINE 22
|
||||
|
||||
/*
|
||||
* To support bidirectional iocb
|
||||
* BSG Vendor specific returns
|
||||
*/
|
||||
#define EXT_STATUS_NOT_SUPPORTED 27
|
||||
#define EXT_STATUS_INVALID_CFG 28
|
||||
#define EXT_STATUS_DMA_ERR 29
|
||||
#define EXT_STATUS_TIMEOUT 30
|
||||
#define EXT_STATUS_THREAD_FAILED 31
|
||||
#define EXT_STATUS_DATA_CMP_FAILED 32
|
||||
|
||||
/* BSG definations for interpreting CommandSent field */
|
||||
#define INT_DEF_LB_LOOPBACK_CMD 0
|
||||
|
|
|
@ -15,17 +15,20 @@
|
|||
* | Mailbox commands | 0x1140 | 0x111a-0x111b |
|
||||
* | | | 0x112c-0x112e |
|
||||
* | | | 0x113a |
|
||||
* | Device Discovery | 0x2086 | 0x2020-0x2022 |
|
||||
* | Device Discovery | 0x2087 | 0x2020-0x2022 |
|
||||
* | Queue Command and IO tracing | 0x3030 | 0x3006,0x3008 |
|
||||
* | | | 0x302d-0x302e |
|
||||
* | DPC Thread | 0x401c | 0x4002,0x4013 |
|
||||
* | Async Events | 0x505f | 0x502b-0x502f |
|
||||
* | | | 0x5047,0x5052 |
|
||||
* | Timer Routines | 0x6011 | |
|
||||
* | User Space Interactions | 0x709f | 0x7018,0x702e, |
|
||||
* | User Space Interactions | 0x70bb | 0x7018,0x702e, |
|
||||
* | | | 0x7039,0x7045, |
|
||||
* | | | 0x7073-0x7075, |
|
||||
* | | | 0x708c |
|
||||
* | | | 0x708c, |
|
||||
* | | | 0x70a5,0x70a6, |
|
||||
* | | | 0x70a8,0x70ab, |
|
||||
* | | | 0x70ad-0x70ae |
|
||||
* | Task Management | 0x803c | 0x8025-0x8026 |
|
||||
* | | | 0x800b,0x8039 |
|
||||
* | AER/EEH | 0x9011 | |
|
||||
|
|
|
@ -260,6 +260,7 @@ struct srb_iocb {
|
|||
#define SRB_ADISC_CMD 6
|
||||
#define SRB_TM_CMD 7
|
||||
#define SRB_SCSI_CMD 8
|
||||
#define SRB_BIDI_CMD 9
|
||||
|
||||
typedef struct srb {
|
||||
atomic_t ref_count;
|
||||
|
@ -1510,6 +1511,13 @@ typedef struct {
|
|||
#define CS_RETRY 0x82 /* Driver defined */
|
||||
#define CS_LOOP_DOWN_ABORT 0x83 /* Driver defined */
|
||||
|
||||
#define CS_BIDIR_RD_OVERRUN 0x700
|
||||
#define CS_BIDIR_RD_WR_OVERRUN 0x707
|
||||
#define CS_BIDIR_RD_OVERRUN_WR_UNDERRUN 0x715
|
||||
#define CS_BIDIR_RD_UNDERRUN 0x1500
|
||||
#define CS_BIDIR_RD_UNDERRUN_WR_OVERRUN 0x1507
|
||||
#define CS_BIDIR_RD_WR_UNDERRUN 0x1515
|
||||
#define CS_BIDIR_DMA 0x200
|
||||
/*
|
||||
* Status entry status flags
|
||||
*/
|
||||
|
@ -2374,6 +2382,11 @@ struct qla_statistics {
|
|||
uint64_t output_bytes;
|
||||
};
|
||||
|
||||
struct bidi_statistics {
|
||||
unsigned long long io_count;
|
||||
unsigned long long transfer_bytes;
|
||||
};
|
||||
|
||||
/* Multi queue support */
|
||||
#define MBC_INITIALIZE_MULTIQ 0x1f
|
||||
#define QLA_QUE_PAGE 0X1000
|
||||
|
@ -2671,6 +2684,7 @@ struct qla_hw_data {
|
|||
#define HAS_EXTENDED_IDS(ha) ((ha)->device_type & DT_EXTENDED_IDS)
|
||||
#define IS_CT6_SUPPORTED(ha) ((ha)->device_type & DT_CT6_SUPPORTED)
|
||||
#define IS_MQUE_CAPABLE(ha) ((ha)->mqenable || IS_QLA83XX(ha))
|
||||
#define IS_BIDI_CAPABLE(ha) ((IS_QLA25XX(ha) || IS_QLA2031(ha)))
|
||||
|
||||
/* HBA serial number */
|
||||
uint8_t serial0;
|
||||
|
@ -2754,6 +2768,7 @@ struct qla_hw_data {
|
|||
struct completion mbx_intr_comp; /* Used for completion notification */
|
||||
struct completion dcbx_comp; /* For set port config notification */
|
||||
int notify_dcbx_comp;
|
||||
struct mutex selflogin_lock;
|
||||
|
||||
/* Basic firmware related information. */
|
||||
uint16_t fw_major_version;
|
||||
|
@ -2987,6 +3002,13 @@ typedef struct scsi_qla_host {
|
|||
|
||||
/* ISP configuration data. */
|
||||
uint16_t loop_id; /* Host adapter loop id */
|
||||
uint16_t self_login_loop_id; /* host adapter loop id
|
||||
* get it on self login
|
||||
*/
|
||||
fc_port_t bidir_fcport; /* fcport used for bidir cmnds
|
||||
* no need of allocating it for
|
||||
* each command
|
||||
*/
|
||||
|
||||
port_id_t d_id; /* Host adapter port id */
|
||||
uint8_t marker_needed;
|
||||
|
@ -3040,6 +3062,7 @@ typedef struct scsi_qla_host {
|
|||
int seconds_since_last_heartbeat;
|
||||
struct fc_host_statistics fc_host_stat;
|
||||
struct qla_statistics qla_stats;
|
||||
struct bidi_statistics bidi_stats;
|
||||
|
||||
atomic_t vref_count;
|
||||
} scsi_qla_host_t;
|
||||
|
|
|
@ -381,6 +381,44 @@ struct init_cb_24xx {
|
|||
/*
|
||||
* ISP queue - command entry structure definition.
|
||||
*/
|
||||
#define COMMAND_BIDIRECTIONAL 0x75
|
||||
struct cmd_bidir {
|
||||
uint8_t entry_type; /* Entry type. */
|
||||
uint8_t entry_count; /* Entry count. */
|
||||
uint8_t sys_define; /* System defined */
|
||||
uint8_t entry_status; /* Entry status. */
|
||||
|
||||
uint32_t handle; /* System handle. */
|
||||
|
||||
uint16_t nport_handle; /* N_PORT hanlde. */
|
||||
|
||||
uint16_t timeout; /* Commnad timeout. */
|
||||
|
||||
uint16_t wr_dseg_count; /* Write Data segment count. */
|
||||
uint16_t rd_dseg_count; /* Read Data segment count. */
|
||||
|
||||
struct scsi_lun lun; /* FCP LUN (BE). */
|
||||
|
||||
uint16_t control_flags; /* Control flags. */
|
||||
#define BD_WRAP_BACK BIT_3
|
||||
#define BD_READ_DATA BIT_1
|
||||
#define BD_WRITE_DATA BIT_0
|
||||
|
||||
uint16_t fcp_cmnd_dseg_len; /* Data segment length. */
|
||||
uint32_t fcp_cmnd_dseg_address[2]; /* Data segment address. */
|
||||
|
||||
uint16_t reserved[2]; /* Reserved */
|
||||
|
||||
uint32_t rd_byte_count; /* Total Byte count Read. */
|
||||
uint32_t wr_byte_count; /* Total Byte count write. */
|
||||
|
||||
uint8_t port_id[3]; /* PortID of destination port.*/
|
||||
uint8_t vp_index;
|
||||
|
||||
uint32_t fcp_data_dseg_address[2]; /* Data segment address. */
|
||||
uint16_t fcp_data_dseg_len; /* Data segment length. */
|
||||
};
|
||||
|
||||
#define COMMAND_TYPE_6 0x48 /* Command Type 6 entry */
|
||||
struct cmd_type_6 {
|
||||
uint8_t entry_type; /* Entry type. */
|
||||
|
|
|
@ -188,6 +188,8 @@ extern int qla2x00_start_sp(srb_t *);
|
|||
extern uint16_t qla24xx_calc_iocbs(scsi_qla_host_t *, uint16_t);
|
||||
extern void qla24xx_build_scsi_iocbs(srb_t *, struct cmd_type_7 *, uint16_t);
|
||||
extern int qla24xx_dif_start_scsi(srb_t *);
|
||||
extern int qla2x00_start_bidir(srb_t *, struct scsi_qla_host *, uint32_t);
|
||||
extern unsigned long qla2x00_get_async_timeout(struct scsi_qla_host *);
|
||||
|
||||
extern void *qla2x00_alloc_iocbs(scsi_qla_host_t *, srb_t *);
|
||||
extern int qla2x00_issue_marker(scsi_qla_host_t *, int);
|
||||
|
|
|
@ -77,7 +77,7 @@ qla2x00_sp_free(void *data, void *ptr)
|
|||
|
||||
/* Asynchronous Login/Logout Routines -------------------------------------- */
|
||||
|
||||
static inline unsigned long
|
||||
unsigned long
|
||||
qla2x00_get_async_timeout(struct scsi_qla_host *vha)
|
||||
{
|
||||
unsigned long tmo;
|
||||
|
|
|
@ -2665,3 +2665,201 @@ done:
|
|||
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
||||
return rval;
|
||||
}
|
||||
|
||||
static void
|
||||
qla25xx_build_bidir_iocb(srb_t *sp, struct scsi_qla_host *vha,
|
||||
struct cmd_bidir *cmd_pkt, uint32_t tot_dsds)
|
||||
{
|
||||
uint16_t avail_dsds;
|
||||
uint32_t *cur_dsd;
|
||||
uint32_t req_data_len = 0;
|
||||
uint32_t rsp_data_len = 0;
|
||||
struct scatterlist *sg;
|
||||
int index;
|
||||
int entry_count = 1;
|
||||
struct fc_bsg_job *bsg_job = sp->u.bsg_job;
|
||||
|
||||
/*Update entry type to indicate bidir command */
|
||||
*((uint32_t *)(&cmd_pkt->entry_type)) =
|
||||
__constant_cpu_to_le32(COMMAND_BIDIRECTIONAL);
|
||||
|
||||
/* Set the transfer direction, in this set both flags
|
||||
* Also set the BD_WRAP_BACK flag, firmware will take care
|
||||
* assigning DID=SID for outgoing pkts.
|
||||
*/
|
||||
cmd_pkt->wr_dseg_count = cpu_to_le16(bsg_job->request_payload.sg_cnt);
|
||||
cmd_pkt->rd_dseg_count = cpu_to_le16(bsg_job->reply_payload.sg_cnt);
|
||||
cmd_pkt->control_flags =
|
||||
__constant_cpu_to_le16(BD_WRITE_DATA | BD_READ_DATA |
|
||||
BD_WRAP_BACK);
|
||||
|
||||
req_data_len = rsp_data_len = bsg_job->request_payload.payload_len;
|
||||
cmd_pkt->wr_byte_count = cpu_to_le32(req_data_len);
|
||||
cmd_pkt->rd_byte_count = cpu_to_le32(rsp_data_len);
|
||||
cmd_pkt->timeout = cpu_to_le16(qla2x00_get_async_timeout(vha) + 2);
|
||||
|
||||
vha->bidi_stats.transfer_bytes += req_data_len;
|
||||
vha->bidi_stats.io_count++;
|
||||
|
||||
/* Only one dsd is available for bidirectional IOCB, remaining dsds
|
||||
* are bundled in continuation iocb
|
||||
*/
|
||||
avail_dsds = 1;
|
||||
cur_dsd = (uint32_t *)&cmd_pkt->fcp_data_dseg_address;
|
||||
|
||||
index = 0;
|
||||
|
||||
for_each_sg(bsg_job->request_payload.sg_list, sg,
|
||||
bsg_job->request_payload.sg_cnt, index) {
|
||||
dma_addr_t sle_dma;
|
||||
cont_a64_entry_t *cont_pkt;
|
||||
|
||||
/* Allocate additional continuation packets */
|
||||
if (avail_dsds == 0) {
|
||||
/* Continuation type 1 IOCB can accomodate
|
||||
* 5 DSDS
|
||||
*/
|
||||
cont_pkt = qla2x00_prep_cont_type1_iocb(vha, vha->req);
|
||||
cur_dsd = (uint32_t *) cont_pkt->dseg_0_address;
|
||||
avail_dsds = 5;
|
||||
entry_count++;
|
||||
}
|
||||
sle_dma = sg_dma_address(sg);
|
||||
*cur_dsd++ = cpu_to_le32(LSD(sle_dma));
|
||||
*cur_dsd++ = cpu_to_le32(MSD(sle_dma));
|
||||
*cur_dsd++ = cpu_to_le32(sg_dma_len(sg));
|
||||
avail_dsds--;
|
||||
}
|
||||
/* For read request DSD will always goes to continuation IOCB
|
||||
* and follow the write DSD. If there is room on the current IOCB
|
||||
* then it is added to that IOCB else new continuation IOCB is
|
||||
* allocated.
|
||||
*/
|
||||
for_each_sg(bsg_job->reply_payload.sg_list, sg,
|
||||
bsg_job->reply_payload.sg_cnt, index) {
|
||||
dma_addr_t sle_dma;
|
||||
cont_a64_entry_t *cont_pkt;
|
||||
|
||||
/* Allocate additional continuation packets */
|
||||
if (avail_dsds == 0) {
|
||||
/* Continuation type 1 IOCB can accomodate
|
||||
* 5 DSDS
|
||||
*/
|
||||
cont_pkt = qla2x00_prep_cont_type1_iocb(vha, vha->req);
|
||||
cur_dsd = (uint32_t *) cont_pkt->dseg_0_address;
|
||||
avail_dsds = 5;
|
||||
entry_count++;
|
||||
}
|
||||
sle_dma = sg_dma_address(sg);
|
||||
*cur_dsd++ = cpu_to_le32(LSD(sle_dma));
|
||||
*cur_dsd++ = cpu_to_le32(MSD(sle_dma));
|
||||
*cur_dsd++ = cpu_to_le32(sg_dma_len(sg));
|
||||
avail_dsds--;
|
||||
}
|
||||
/* This value should be same as number of IOCB required for this cmd */
|
||||
cmd_pkt->entry_count = entry_count;
|
||||
}
|
||||
|
||||
int
|
||||
qla2x00_start_bidir(srb_t *sp, struct scsi_qla_host *vha, uint32_t tot_dsds)
|
||||
{
|
||||
|
||||
struct qla_hw_data *ha = vha->hw;
|
||||
unsigned long flags;
|
||||
uint32_t handle;
|
||||
uint32_t index;
|
||||
uint16_t req_cnt;
|
||||
uint16_t cnt;
|
||||
uint32_t *clr_ptr;
|
||||
struct cmd_bidir *cmd_pkt = NULL;
|
||||
struct rsp_que *rsp;
|
||||
struct req_que *req;
|
||||
int rval = EXT_STATUS_OK;
|
||||
device_reg_t __iomem *reg = ISP_QUE_REG(ha, vha->req->id);
|
||||
|
||||
rval = QLA_SUCCESS;
|
||||
|
||||
rsp = ha->rsp_q_map[0];
|
||||
req = vha->req;
|
||||
|
||||
/* Send marker if required */
|
||||
if (vha->marker_needed != 0) {
|
||||
if (qla2x00_marker(vha, req,
|
||||
rsp, 0, 0, MK_SYNC_ALL) != QLA_SUCCESS)
|
||||
return EXT_STATUS_MAILBOX;
|
||||
vha->marker_needed = 0;
|
||||
}
|
||||
|
||||
/* Acquire ring specific lock */
|
||||
spin_lock_irqsave(&ha->hardware_lock, flags);
|
||||
|
||||
/* Check for room in outstanding command list. */
|
||||
handle = req->current_outstanding_cmd;
|
||||
for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) {
|
||||
handle++;
|
||||
if (handle == MAX_OUTSTANDING_COMMANDS)
|
||||
handle = 1;
|
||||
if (!req->outstanding_cmds[handle])
|
||||
break;
|
||||
}
|
||||
|
||||
if (index == MAX_OUTSTANDING_COMMANDS) {
|
||||
rval = EXT_STATUS_BUSY;
|
||||
goto queuing_error;
|
||||
}
|
||||
|
||||
/* Calculate number of IOCB required */
|
||||
req_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
|
||||
|
||||
/* Check for room on request queue. */
|
||||
if (req->cnt < req_cnt + 2) {
|
||||
if (ha->mqenable)
|
||||
cnt = RD_REG_DWORD(®->isp25mq.req_q_out);
|
||||
else if (IS_QLA82XX(ha))
|
||||
cnt = RD_REG_DWORD(®->isp82.req_q_out);
|
||||
else if (IS_FWI2_CAPABLE(ha))
|
||||
cnt = RD_REG_DWORD(®->isp24.req_q_out);
|
||||
else
|
||||
cnt = qla2x00_debounce_register(
|
||||
ISP_REQ_Q_OUT(ha, ®->isp));
|
||||
|
||||
if (req->ring_index < cnt)
|
||||
req->cnt = cnt - req->ring_index;
|
||||
else
|
||||
req->cnt = req->length -
|
||||
(req->ring_index - cnt);
|
||||
}
|
||||
if (req->cnt < req_cnt + 2) {
|
||||
rval = EXT_STATUS_BUSY;
|
||||
goto queuing_error;
|
||||
}
|
||||
|
||||
cmd_pkt = (struct cmd_bidir *)req->ring_ptr;
|
||||
cmd_pkt->handle = MAKE_HANDLE(req->id, handle);
|
||||
|
||||
/* Zero out remaining portion of packet. */
|
||||
/* tagged queuing modifier -- default is TSK_SIMPLE (0).*/
|
||||
clr_ptr = (uint32_t *)cmd_pkt + 2;
|
||||
memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
|
||||
|
||||
/* Set NPORT-ID (of vha)*/
|
||||
cmd_pkt->nport_handle = cpu_to_le16(vha->self_login_loop_id);
|
||||
cmd_pkt->port_id[0] = vha->d_id.b.al_pa;
|
||||
cmd_pkt->port_id[1] = vha->d_id.b.area;
|
||||
cmd_pkt->port_id[2] = vha->d_id.b.domain;
|
||||
|
||||
qla25xx_build_bidir_iocb(sp, vha, cmd_pkt, tot_dsds);
|
||||
cmd_pkt->entry_status = (uint8_t) rsp->id;
|
||||
/* Build command packet. */
|
||||
req->current_outstanding_cmd = handle;
|
||||
req->outstanding_cmds[handle] = sp;
|
||||
sp->handle = handle;
|
||||
req->cnt -= req_cnt;
|
||||
|
||||
/* Send the command to the firmware */
|
||||
wmb();
|
||||
qla2x00_start_iocbs(vha, req);
|
||||
queuing_error:
|
||||
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
||||
return rval;
|
||||
}
|
||||
|
|
|
@ -1546,6 +1546,149 @@ qla2x00_handle_dif_error(srb_t *sp, struct sts_entry_24xx *sts24)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
qla25xx_process_bidir_status_iocb(scsi_qla_host_t *vha, void *pkt,
|
||||
struct req_que *req, uint32_t index)
|
||||
{
|
||||
struct qla_hw_data *ha = vha->hw;
|
||||
srb_t *sp;
|
||||
uint16_t comp_status;
|
||||
uint16_t scsi_status;
|
||||
uint16_t thread_id;
|
||||
uint32_t rval = EXT_STATUS_OK;
|
||||
struct fc_bsg_job *bsg_job = NULL;
|
||||
sts_entry_t *sts;
|
||||
struct sts_entry_24xx *sts24;
|
||||
sts = (sts_entry_t *) pkt;
|
||||
sts24 = (struct sts_entry_24xx *) pkt;
|
||||
|
||||
/* Validate handle. */
|
||||
if (index >= MAX_OUTSTANDING_COMMANDS) {
|
||||
ql_log(ql_log_warn, vha, 0x70af,
|
||||
"Invalid SCSI completion handle 0x%x.\n", index);
|
||||
set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
|
||||
return;
|
||||
}
|
||||
|
||||
sp = req->outstanding_cmds[index];
|
||||
if (sp) {
|
||||
/* Free outstanding command slot. */
|
||||
req->outstanding_cmds[index] = NULL;
|
||||
bsg_job = sp->u.bsg_job;
|
||||
} else {
|
||||
ql_log(ql_log_warn, vha, 0x70b0,
|
||||
"Req:%d: Invalid ISP SCSI completion handle(0x%x)\n",
|
||||
req->id, index);
|
||||
|
||||
set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IS_FWI2_CAPABLE(ha)) {
|
||||
comp_status = le16_to_cpu(sts24->comp_status);
|
||||
scsi_status = le16_to_cpu(sts24->scsi_status) & SS_MASK;
|
||||
} else {
|
||||
comp_status = le16_to_cpu(sts->comp_status);
|
||||
scsi_status = le16_to_cpu(sts->scsi_status) & SS_MASK;
|
||||
}
|
||||
|
||||
thread_id = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1];
|
||||
switch (comp_status) {
|
||||
case CS_COMPLETE:
|
||||
if (scsi_status == 0) {
|
||||
bsg_job->reply->reply_payload_rcv_len =
|
||||
bsg_job->reply_payload.payload_len;
|
||||
rval = EXT_STATUS_OK;
|
||||
}
|
||||
goto done;
|
||||
|
||||
case CS_DATA_OVERRUN:
|
||||
ql_dbg(ql_dbg_user, vha, 0x70b1,
|
||||
"Command completed with date overrun thread_id=%d\n",
|
||||
thread_id);
|
||||
rval = EXT_STATUS_DATA_OVERRUN;
|
||||
break;
|
||||
|
||||
case CS_DATA_UNDERRUN:
|
||||
ql_dbg(ql_dbg_user, vha, 0x70b2,
|
||||
"Command completed with date underrun thread_id=%d\n",
|
||||
thread_id);
|
||||
rval = EXT_STATUS_DATA_UNDERRUN;
|
||||
break;
|
||||
case CS_BIDIR_RD_OVERRUN:
|
||||
ql_dbg(ql_dbg_user, vha, 0x70b3,
|
||||
"Command completed with read data overrun thread_id=%d\n",
|
||||
thread_id);
|
||||
rval = EXT_STATUS_DATA_OVERRUN;
|
||||
break;
|
||||
|
||||
case CS_BIDIR_RD_WR_OVERRUN:
|
||||
ql_dbg(ql_dbg_user, vha, 0x70b4,
|
||||
"Command completed with read and write data overrun "
|
||||
"thread_id=%d\n", thread_id);
|
||||
rval = EXT_STATUS_DATA_OVERRUN;
|
||||
break;
|
||||
|
||||
case CS_BIDIR_RD_OVERRUN_WR_UNDERRUN:
|
||||
ql_dbg(ql_dbg_user, vha, 0x70b5,
|
||||
"Command completed with read data over and write data "
|
||||
"underrun thread_id=%d\n", thread_id);
|
||||
rval = EXT_STATUS_DATA_OVERRUN;
|
||||
break;
|
||||
|
||||
case CS_BIDIR_RD_UNDERRUN:
|
||||
ql_dbg(ql_dbg_user, vha, 0x70b6,
|
||||
"Command completed with read data data underrun "
|
||||
"thread_id=%d\n", thread_id);
|
||||
rval = EXT_STATUS_DATA_UNDERRUN;
|
||||
break;
|
||||
|
||||
case CS_BIDIR_RD_UNDERRUN_WR_OVERRUN:
|
||||
ql_dbg(ql_dbg_user, vha, 0x70b7,
|
||||
"Command completed with read data under and write data "
|
||||
"overrun thread_id=%d\n", thread_id);
|
||||
rval = EXT_STATUS_DATA_UNDERRUN;
|
||||
break;
|
||||
|
||||
case CS_BIDIR_RD_WR_UNDERRUN:
|
||||
ql_dbg(ql_dbg_user, vha, 0x70b8,
|
||||
"Command completed with read and write data underrun "
|
||||
"thread_id=%d\n", thread_id);
|
||||
rval = EXT_STATUS_DATA_UNDERRUN;
|
||||
break;
|
||||
|
||||
case CS_BIDIR_DMA:
|
||||
ql_dbg(ql_dbg_user, vha, 0x70b9,
|
||||
"Command completed with data DMA error thread_id=%d\n",
|
||||
thread_id);
|
||||
rval = EXT_STATUS_DMA_ERR;
|
||||
break;
|
||||
|
||||
case CS_TIMEOUT:
|
||||
ql_dbg(ql_dbg_user, vha, 0x70ba,
|
||||
"Command completed with timeout thread_id=%d\n",
|
||||
thread_id);
|
||||
rval = EXT_STATUS_TIMEOUT;
|
||||
break;
|
||||
default:
|
||||
ql_dbg(ql_dbg_user, vha, 0x70bb,
|
||||
"Command completed with completion status=0x%x "
|
||||
"thread_id=%d\n", comp_status, thread_id);
|
||||
rval = EXT_STATUS_ERR;
|
||||
break;
|
||||
}
|
||||
bsg_job->reply->reply_payload_rcv_len = 0;
|
||||
|
||||
done:
|
||||
/* Return the vendor specific reply to API */
|
||||
bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = rval;
|
||||
bsg_job->reply_len = sizeof(struct fc_bsg_reply);
|
||||
/* Always return DID_OK, bsg will send the vendor specific response
|
||||
* in this case only */
|
||||
sp->done(vha, sp, (DID_OK << 6));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* qla2x00_status_entry() - Process a Status IOCB entry.
|
||||
* @ha: SCSI driver HA context
|
||||
|
@ -1573,12 +1716,14 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
|
|||
struct req_que *req;
|
||||
int logit = 1;
|
||||
int res = 0;
|
||||
uint16_t state_flags = 0;
|
||||
|
||||
sts = (sts_entry_t *) pkt;
|
||||
sts24 = (struct sts_entry_24xx *) pkt;
|
||||
if (IS_FWI2_CAPABLE(ha)) {
|
||||
comp_status = le16_to_cpu(sts24->comp_status);
|
||||
scsi_status = le16_to_cpu(sts24->scsi_status) & SS_MASK;
|
||||
state_flags = le16_to_cpu(sts24->state_flags);
|
||||
} else {
|
||||
comp_status = le16_to_cpu(sts->comp_status);
|
||||
scsi_status = le16_to_cpu(sts->scsi_status) & SS_MASK;
|
||||
|
@ -1587,17 +1732,9 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
|
|||
que = MSW(sts->handle);
|
||||
req = ha->req_q_map[que];
|
||||
|
||||
/* Fast path completion. */
|
||||
if (comp_status == CS_COMPLETE && scsi_status == 0) {
|
||||
qla2x00_process_completed_request(vha, req, handle);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Validate handle. */
|
||||
if (handle < MAX_OUTSTANDING_COMMANDS) {
|
||||
sp = req->outstanding_cmds[handle];
|
||||
req->outstanding_cmds[handle] = NULL;
|
||||
} else
|
||||
sp = NULL;
|
||||
|
||||
|
@ -1612,6 +1749,20 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
|
|||
qla2xxx_wake_dpc(vha);
|
||||
return;
|
||||
}
|
||||
|
||||
if (unlikely((state_flags & BIT_1) && (sp->type == SRB_BIDI_CMD))) {
|
||||
qla25xx_process_bidir_status_iocb(vha, pkt, req, handle);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Fast path completion. */
|
||||
if (comp_status == CS_COMPLETE && scsi_status == 0) {
|
||||
qla2x00_process_completed_request(vha, req, handle);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
req->outstanding_cmds[handle] = NULL;
|
||||
cp = GET_CMD_SP(sp);
|
||||
if (cp == NULL) {
|
||||
ql_dbg(ql_dbg_io, vha, 0x3018,
|
||||
|
|
|
@ -2203,6 +2203,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||
ha->mem_only = mem_only;
|
||||
spin_lock_init(&ha->hardware_lock);
|
||||
spin_lock_init(&ha->vport_slock);
|
||||
mutex_init(&ha->selflogin_lock);
|
||||
|
||||
/* Set ISP-type information. */
|
||||
qla2x00_set_isp_flags(ha);
|
||||
|
|
Loading…
Reference in a new issue