summaryrefslogtreecommitdiff
path: root/drivers/scsi/qla2xxx/qla_bsg.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/qla2xxx/qla_bsg.c')
-rw-r--r--drivers/scsi/qla2xxx/qla_bsg.c750
1 files changed, 681 insertions, 69 deletions
diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
index 88c0338a2ec7..ccfc2d26dd37 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -1,10 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* QLogic Fibre Channel HBA Driver
* Copyright (c) 2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
*/
#include "qla_def.h"
+#include "qla_gbl.h"
#include <linux/kthread.h>
#include <linux/vmalloc.h>
@@ -24,11 +24,21 @@ void qla2x00_bsg_job_done(srb_t *sp, int res)
{
struct bsg_job *bsg_job = sp->u.bsg_job;
struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+ struct completion *comp = sp->comp;
+
+ ql_dbg(ql_dbg_user, sp->vha, 0x7009,
+ "%s: sp hdl %x, result=%x bsg ptr %p\n",
+ __func__, sp->handle, res, bsg_job);
+
+ /* ref: INIT */
+ kref_put(&sp->cmd_kref, qla2x00_sp_release);
bsg_reply->result = res;
bsg_job_done(bsg_job, bsg_reply->result,
bsg_reply->reply_payload_rcv_len);
- sp->free(sp);
+
+ if (comp)
+ complete(comp);
}
void qla2x00_bsg_sp_free(srb_t *sp)
@@ -52,11 +62,19 @@ void qla2x00_bsg_sp_free(srb_t *sp)
bsg_job->reply_payload.sg_list,
bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
} else {
- dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
- bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
- dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list,
- bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+ if (sp->remap.remapped) {
+ dma_pool_free(ha->purex_dma_pool, sp->remap.rsp.buf,
+ sp->remap.rsp.dma);
+ dma_pool_free(ha->purex_dma_pool, sp->remap.req.buf,
+ sp->remap.req.dma);
+ } else {
+ dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
+
+ dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+ }
}
if (sp->type == SRB_CT_CMD ||
@@ -223,8 +241,7 @@ qla24xx_proc_fcp_prio_cfg_cmd(struct bsg_job *bsg_job)
/* validate fcp priority data */
- if (!qla24xx_fcp_prio_cfg_valid(vha,
- (struct qla_fcp_prio_cfg *) ha->fcp_prio_cfg, 1)) {
+ if (!qla24xx_fcp_prio_cfg_valid(vha, ha->fcp_prio_cfg, 1)) {
bsg_reply->result = (DID_ERROR << 16);
ret = -EINVAL;
/* If buffer was invalidatic int
@@ -265,10 +282,15 @@ qla2x00_process_els(struct bsg_job *bsg_job)
const char *type;
int req_sg_cnt, rsp_sg_cnt;
int rval = (DID_ERROR << 16);
- uint16_t nextlid = 0;
+ uint32_t els_cmd = 0;
+ int qla_port_allocated = 0;
if (bsg_request->msgcode == FC_BSG_RPT_ELS) {
rport = fc_bsg_to_rport(bsg_job);
+ if (!rport) {
+ rval = -ENOMEM;
+ goto done;
+ }
fcport = *(fc_port_t **) rport->dd_data;
host = rport_to_shost(rport);
vha = shost_priv(host);
@@ -279,6 +301,9 @@ qla2x00_process_els(struct bsg_job *bsg_job)
vha = shost_priv(host);
ha = vha->hw;
type = "FC_BSG_HST_ELS_NOLOGIN";
+ els_cmd = bsg_request->rqst_data.h_els.command_code;
+ if (els_cmd == ELS_AUTH_ELS)
+ return qla_edif_process_els(vha, bsg_job);
}
if (!vha->flags.online) {
@@ -303,7 +328,7 @@ qla2x00_process_els(struct bsg_job *bsg_job)
"request_sg_cnt=%x reply_sg_cnt=%x.\n",
bsg_job->request_payload.sg_cnt,
bsg_job->reply_payload.sg_cnt);
- rval = -EPERM;
+ rval = -ENOBUFS;
goto done;
}
@@ -312,9 +337,9 @@ qla2x00_process_els(struct bsg_job *bsg_job)
/* make sure the rport is logged in,
* if not perform fabric login
*/
- if (qla2x00_fabric_login(vha, fcport, &nextlid)) {
+ if (atomic_read(&fcport->state) != FCS_ONLINE) {
ql_dbg(ql_dbg_user, vha, 0x7003,
- "Failed to login port %06X for ELS passthru.\n",
+ "Port %06X is not online for ELS passthru.\n",
fcport->d_id.b24);
rval = -EIO;
goto done;
@@ -331,6 +356,7 @@ qla2x00_process_els(struct bsg_job *bsg_job)
goto done;
}
+ qla_port_allocated = 1;
/* Initialize all required fields of fcport */
fcport->vha = vha;
fcport->d_id.b.al_pa =
@@ -415,7 +441,7 @@ done_unmap_sg:
goto done_free_fcport;
done_free_fcport:
- if (bsg_request->msgcode == FC_BSG_RPT_ELS)
+ if (qla_port_allocated)
qla2x00_free_fcport(fcport);
done:
return rval;
@@ -468,16 +494,6 @@ qla2x00_process_ct(struct bsg_job *bsg_job)
goto done;
}
- if ((req_sg_cnt != bsg_job->request_payload.sg_cnt) ||
- (rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) {
- ql_log(ql_log_warn, vha, 0x7011,
- "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 = -EAGAIN;
- goto done_unmap_sg;
- }
-
if (!vha->flags.online) {
ql_log(ql_log_warn, vha, 0x7012,
"Host is not online.\n");
@@ -2408,6 +2424,89 @@ qla2x00_do_dport_diagnostics(struct bsg_job *bsg_job)
}
static int
+qla2x00_do_dport_diagnostics_v2(struct bsg_job *bsg_job)
+{
+ struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+ struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
+ scsi_qla_host_t *vha = shost_priv(host);
+ int rval;
+ struct qla_dport_diag_v2 *dd;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ uint16_t options;
+
+ if (!IS_DPORT_CAPABLE(vha->hw))
+ return -EPERM;
+
+ dd = kzalloc(sizeof(*dd), GFP_KERNEL);
+ if (!dd)
+ return -ENOMEM;
+
+ sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, dd, sizeof(*dd));
+
+ options = dd->options;
+
+ /* Check dport Test in progress */
+ if (options == QLA_GET_DPORT_RESULT_V2 &&
+ vha->dport_status & DPORT_DIAG_IN_PROGRESS) {
+ bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+ EXT_STATUS_DPORT_DIAG_IN_PROCESS;
+ goto dportcomplete;
+ }
+
+ /* Check chip reset in progress and start/restart requests arrive */
+ if (vha->dport_status & DPORT_DIAG_CHIP_RESET_IN_PROGRESS &&
+ (options == QLA_START_DPORT_TEST_V2 ||
+ options == QLA_RESTART_DPORT_TEST_V2)) {
+ vha->dport_status &= ~DPORT_DIAG_CHIP_RESET_IN_PROGRESS;
+ }
+
+ /* Check chip reset in progress and get result request arrive */
+ if (vha->dport_status & DPORT_DIAG_CHIP_RESET_IN_PROGRESS &&
+ options == QLA_GET_DPORT_RESULT_V2) {
+ bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+ EXT_STATUS_DPORT_DIAG_NOT_RUNNING;
+ goto dportcomplete;
+ }
+
+ rval = qla26xx_dport_diagnostics_v2(vha, dd, mcp);
+
+ if (rval == QLA_SUCCESS) {
+ bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+ EXT_STATUS_OK;
+ if (options == QLA_START_DPORT_TEST_V2 ||
+ options == QLA_RESTART_DPORT_TEST_V2) {
+ dd->mbx1 = mcp->mb[0];
+ dd->mbx2 = mcp->mb[1];
+ vha->dport_status |= DPORT_DIAG_IN_PROGRESS;
+ } else if (options == QLA_GET_DPORT_RESULT_V2) {
+ dd->mbx1 = le16_to_cpu(vha->dport_data[1]);
+ dd->mbx2 = le16_to_cpu(vha->dport_data[2]);
+ }
+ } else {
+ dd->mbx1 = mcp->mb[0];
+ dd->mbx2 = mcp->mb[1];
+ bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+ EXT_STATUS_DPORT_DIAG_ERR;
+ }
+
+dportcomplete:
+ sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, dd, sizeof(*dd));
+
+ bsg_reply->reply_payload_rcv_len = sizeof(*dd);
+ bsg_job->reply_len = sizeof(*bsg_reply);
+ bsg_reply->result = DID_OK << 16;
+ bsg_job_done(bsg_job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
+
+ kfree(dd);
+
+ return 0;
+}
+
+static int
qla2x00_get_flash_image_status(struct bsg_job *bsg_job)
{
scsi_qla_host_t *vha = shost_priv(fc_bsg_to_shost(bsg_job));
@@ -2419,19 +2518,23 @@ qla2x00_get_flash_image_status(struct bsg_job *bsg_job)
qla27xx_get_active_image(vha, &active_regions);
regions.global_image = active_regions.global;
+ if (IS_QLA27XX(ha))
+ regions.nvme_params = QLA27XX_PRIMARY_IMAGE;
+
if (IS_QLA28XX(ha)) {
qla28xx_get_aux_images(vha, &active_regions);
regions.board_config = active_regions.aux.board_config;
regions.vpd_nvram = active_regions.aux.vpd_nvram;
regions.npiv_config_0_1 = active_regions.aux.npiv_config_0_1;
regions.npiv_config_2_3 = active_regions.aux.npiv_config_2_3;
+ regions.nvme_params = active_regions.aux.nvme_params;
}
ql_dbg(ql_dbg_user, vha, 0x70e1,
- "%s(%lu): FW=%u BCFG=%u VPDNVR=%u NPIV01=%u NPIV02=%u\n",
+ "%s(%lu): FW=%u BCFG=%u VPDNVR=%u NPIV01=%u NPIV02=%u NVME_PARAMS=%u\n",
__func__, vha->host_no, regions.global_image,
regions.board_config, regions.vpd_nvram,
- regions.npiv_config_0_1, regions.npiv_config_2_3);
+ regions.npiv_config_0_1, regions.npiv_config_2_3, regions.nvme_params);
sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
bsg_job->reply_payload.sg_cnt, &regions, sizeof(regions));
@@ -2447,10 +2550,334 @@ qla2x00_get_flash_image_status(struct bsg_job *bsg_job)
}
static int
-qla2x00_process_vendor_specific(struct bsg_job *bsg_job)
+qla2x00_manage_host_stats(struct bsg_job *bsg_job)
+{
+ scsi_qla_host_t *vha = shost_priv(fc_bsg_to_shost(bsg_job));
+ struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+ struct ql_vnd_mng_host_stats_param *req_data;
+ struct ql_vnd_mng_host_stats_resp rsp_data;
+ u32 req_data_len;
+ int ret = 0;
+
+ if (!vha->flags.online) {
+ ql_log(ql_log_warn, vha, 0x0000, "Host is not online.\n");
+ return -EIO;
+ }
+
+ req_data_len = bsg_job->request_payload.payload_len;
+
+ if (req_data_len != sizeof(struct ql_vnd_mng_host_stats_param)) {
+ ql_log(ql_log_warn, vha, 0x0000, "req_data_len invalid.\n");
+ return -EIO;
+ }
+
+ req_data = kzalloc(sizeof(*req_data), GFP_KERNEL);
+ if (!req_data) {
+ ql_log(ql_log_warn, vha, 0x0000, "req_data memory allocation failure.\n");
+ return -ENOMEM;
+ }
+
+ /* Copy the request buffer in req_data */
+ sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, req_data,
+ req_data_len);
+
+ switch (req_data->action) {
+ case QLA_STOP:
+ ret = qla2xxx_stop_stats(vha->host, req_data->stat_type);
+ break;
+ case QLA_START:
+ ret = qla2xxx_start_stats(vha->host, req_data->stat_type);
+ break;
+ case QLA_CLEAR:
+ ret = qla2xxx_reset_stats(vha->host, req_data->stat_type);
+ break;
+ default:
+ ql_log(ql_log_warn, vha, 0x0000, "Invalid action.\n");
+ ret = -EIO;
+ break;
+ }
+
+ kfree(req_data);
+
+ /* Prepare response */
+ rsp_data.status = ret;
+ bsg_job->reply_payload.payload_len = sizeof(struct ql_vnd_mng_host_stats_resp);
+
+ bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = EXT_STATUS_OK;
+ bsg_reply->reply_payload_rcv_len =
+ sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt,
+ &rsp_data,
+ sizeof(struct ql_vnd_mng_host_stats_resp));
+
+ bsg_reply->result = DID_OK;
+ bsg_job_done(bsg_job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
+
+ return ret;
+}
+
+static int
+qla2x00_get_host_stats(struct bsg_job *bsg_job)
+{
+ scsi_qla_host_t *vha = shost_priv(fc_bsg_to_shost(bsg_job));
+ struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+ struct ql_vnd_stats_param *req_data;
+ struct ql_vnd_host_stats_resp rsp_data;
+ u32 req_data_len;
+ int ret = 0;
+ u64 ini_entry_count = 0;
+ u64 entry_count = 0;
+ u64 tgt_num = 0;
+ u64 tmp_stat_type = 0;
+ u64 response_len = 0;
+ void *data;
+
+ req_data_len = bsg_job->request_payload.payload_len;
+
+ if (req_data_len != sizeof(struct ql_vnd_stats_param)) {
+ ql_log(ql_log_warn, vha, 0x0000, "req_data_len invalid.\n");
+ return -EIO;
+ }
+
+ req_data = kzalloc(sizeof(*req_data), GFP_KERNEL);
+ if (!req_data) {
+ ql_log(ql_log_warn, vha, 0x0000, "req_data memory allocation failure.\n");
+ return -ENOMEM;
+ }
+
+ /* Copy the request buffer in req_data */
+ sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, req_data, req_data_len);
+
+ /* Copy stat type to work on it */
+ tmp_stat_type = req_data->stat_type;
+
+ if (tmp_stat_type & QLA2XX_TGT_SHT_LNK_DOWN) {
+ /* Num of tgts connected to this host */
+ tgt_num = qla2x00_get_num_tgts(vha);
+ /* unset BIT_17 */
+ tmp_stat_type &= ~(1 << 17);
+ }
+
+ /* Total ini stats */
+ ini_entry_count = qla2x00_count_set_bits(tmp_stat_type);
+
+ /* Total number of entries */
+ entry_count = ini_entry_count + tgt_num;
+
+ response_len = sizeof(struct ql_vnd_host_stats_resp) +
+ (sizeof(struct ql_vnd_stat_entry) * entry_count);
+
+ if (response_len > bsg_job->reply_payload.payload_len) {
+ rsp_data.status = EXT_STATUS_BUFFER_TOO_SMALL;
+ bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = EXT_STATUS_BUFFER_TOO_SMALL;
+ bsg_job->reply_payload.payload_len = sizeof(struct ql_vnd_mng_host_stats_resp);
+
+ bsg_reply->reply_payload_rcv_len =
+ sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, &rsp_data,
+ sizeof(struct ql_vnd_mng_host_stats_resp));
+
+ bsg_reply->result = DID_OK;
+ bsg_job_done(bsg_job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
+ goto host_stat_out;
+ }
+
+ data = kzalloc(response_len, GFP_KERNEL);
+ if (!data) {
+ ret = -ENOMEM;
+ goto host_stat_out;
+ }
+
+ ret = qla2xxx_get_ini_stats(fc_bsg_to_shost(bsg_job), req_data->stat_type,
+ data, response_len);
+
+ rsp_data.status = EXT_STATUS_OK;
+ bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = EXT_STATUS_OK;
+
+ bsg_reply->reply_payload_rcv_len = sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt,
+ data, response_len);
+ bsg_reply->result = DID_OK;
+ bsg_job_done(bsg_job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
+
+ kfree(data);
+host_stat_out:
+ kfree(req_data);
+ return ret;
+}
+
+static struct fc_rport *
+qla2xxx_find_rport(scsi_qla_host_t *vha, uint32_t tgt_num)
+{
+ fc_port_t *fcport = NULL;
+
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
+ if (fcport->rport->number == tgt_num)
+ return fcport->rport;
+ }
+ return NULL;
+}
+
+static int
+qla2x00_get_tgt_stats(struct bsg_job *bsg_job)
+{
+ scsi_qla_host_t *vha = shost_priv(fc_bsg_to_shost(bsg_job));
+ struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+ struct ql_vnd_tgt_stats_param *req_data;
+ u32 req_data_len;
+ int ret = 0;
+ u64 response_len = 0;
+ struct ql_vnd_tgt_stats_resp *data = NULL;
+ struct fc_rport *rport = NULL;
+
+ if (!vha->flags.online) {
+ ql_log(ql_log_warn, vha, 0x0000, "Host is not online.\n");
+ return -EIO;
+ }
+
+ req_data_len = bsg_job->request_payload.payload_len;
+
+ if (req_data_len != sizeof(struct ql_vnd_stat_entry)) {
+ ql_log(ql_log_warn, vha, 0x0000, "req_data_len invalid.\n");
+ return -EIO;
+ }
+
+ req_data = kzalloc(sizeof(*req_data), GFP_KERNEL);
+ if (!req_data) {
+ ql_log(ql_log_warn, vha, 0x0000, "req_data memory allocation failure.\n");
+ return -ENOMEM;
+ }
+
+ /* Copy the request buffer in req_data */
+ sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt,
+ req_data, req_data_len);
+
+ response_len = sizeof(struct ql_vnd_tgt_stats_resp) +
+ sizeof(struct ql_vnd_stat_entry);
+
+ /* structure + size for one entry */
+ data = kzalloc(response_len, GFP_KERNEL);
+ if (!data) {
+ kfree(req_data);
+ return -ENOMEM;
+ }
+
+ if (response_len > bsg_job->reply_payload.payload_len) {
+ data->status = EXT_STATUS_BUFFER_TOO_SMALL;
+ bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = EXT_STATUS_BUFFER_TOO_SMALL;
+ bsg_job->reply_payload.payload_len = sizeof(struct ql_vnd_mng_host_stats_resp);
+
+ bsg_reply->reply_payload_rcv_len =
+ sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, data,
+ sizeof(struct ql_vnd_tgt_stats_resp));
+
+ bsg_reply->result = DID_OK;
+ bsg_job_done(bsg_job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
+ goto tgt_stat_out;
+ }
+
+ rport = qla2xxx_find_rport(vha, req_data->tgt_id);
+ if (!rport) {
+ ql_log(ql_log_warn, vha, 0x0000, "target %d not found.\n", req_data->tgt_id);
+ ret = EXT_STATUS_INVALID_PARAM;
+ data->status = EXT_STATUS_INVALID_PARAM;
+ goto reply;
+ }
+
+ ret = qla2xxx_get_tgt_stats(fc_bsg_to_shost(bsg_job), req_data->stat_type,
+ rport, (void *)data, response_len);
+
+ bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = EXT_STATUS_OK;
+reply:
+ bsg_reply->reply_payload_rcv_len =
+ sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, data,
+ response_len);
+ bsg_reply->result = DID_OK;
+ bsg_job_done(bsg_job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
+
+tgt_stat_out:
+ kfree(data);
+ kfree(req_data);
+
+ return ret;
+}
+
+static int
+qla2x00_manage_host_port(struct bsg_job *bsg_job)
+{
+ scsi_qla_host_t *vha = shost_priv(fc_bsg_to_shost(bsg_job));
+ struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+ struct ql_vnd_mng_host_port_param *req_data;
+ struct ql_vnd_mng_host_port_resp rsp_data;
+ u32 req_data_len;
+ int ret = 0;
+
+ req_data_len = bsg_job->request_payload.payload_len;
+
+ if (req_data_len != sizeof(struct ql_vnd_mng_host_port_param)) {
+ ql_log(ql_log_warn, vha, 0x0000, "req_data_len invalid.\n");
+ return -EIO;
+ }
+
+ req_data = kzalloc(sizeof(*req_data), GFP_KERNEL);
+ if (!req_data) {
+ ql_log(ql_log_warn, vha, 0x0000, "req_data memory allocation failure.\n");
+ return -ENOMEM;
+ }
+
+ /* Copy the request buffer in req_data */
+ sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, req_data, req_data_len);
+
+ switch (req_data->action) {
+ case QLA_ENABLE:
+ ret = qla2xxx_enable_port(vha->host);
+ break;
+ case QLA_DISABLE:
+ ret = qla2xxx_disable_port(vha->host);
+ break;
+ default:
+ ql_log(ql_log_warn, vha, 0x0000, "Invalid action.\n");
+ ret = -EIO;
+ break;
+ }
+
+ kfree(req_data);
+
+ /* Prepare response */
+ rsp_data.status = ret;
+ bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = EXT_STATUS_OK;
+ bsg_job->reply_payload.payload_len = sizeof(struct ql_vnd_mng_host_port_resp);
+
+ bsg_reply->reply_payload_rcv_len =
+ sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, &rsp_data,
+ sizeof(struct ql_vnd_mng_host_port_resp));
+ bsg_reply->result = DID_OK;
+ bsg_job_done(bsg_job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
+
+ return ret;
+}
+
+static int
+qla2x00_process_vendor_specific(struct scsi_qla_host *vha, struct bsg_job *bsg_job)
{
struct fc_bsg_request *bsg_request = bsg_job->request;
+ ql_dbg(ql_dbg_edif, vha, 0x911b, "%s FC_BSG_HST_VENDOR cmd[0]=0x%x\n",
+ __func__, bsg_request->rqst_data.h_vendor.vendor_cmd[0]);
+
switch (bsg_request->rqst_data.h_vendor.vendor_cmd[0]) {
case QL_VND_LOOPBACK:
return qla2x00_process_loopback(bsg_job);
@@ -2519,9 +2946,30 @@ qla2x00_process_vendor_specific(struct bsg_job *bsg_job)
case QL_VND_DPORT_DIAGNOSTICS:
return qla2x00_do_dport_diagnostics(bsg_job);
+ case QL_VND_DPORT_DIAGNOSTICS_V2:
+ return qla2x00_do_dport_diagnostics_v2(bsg_job);
+
+ case QL_VND_EDIF_MGMT:
+ return qla_edif_app_mgmt(bsg_job);
+
case QL_VND_SS_GET_FLASH_IMAGE_STATUS:
return qla2x00_get_flash_image_status(bsg_job);
+ case QL_VND_MANAGE_HOST_STATS:
+ return qla2x00_manage_host_stats(bsg_job);
+
+ case QL_VND_GET_HOST_STATS:
+ return qla2x00_get_host_stats(bsg_job);
+
+ case QL_VND_GET_TGT_STATS:
+ return qla2x00_get_tgt_stats(bsg_job);
+
+ case QL_VND_MANAGE_HOST_PORT:
+ return qla2x00_manage_host_port(bsg_job);
+
+ case QL_VND_MBX_PASSTHRU:
+ return qla2x00_mailbox_passthru(bsg_job);
+
default:
return -ENOSYS;
}
@@ -2542,6 +2990,8 @@ qla24xx_bsg_request(struct bsg_job *bsg_job)
if (bsg_request->msgcode == FC_BSG_RPT_ELS) {
rport = fc_bsg_to_rport(bsg_job);
+ if (!rport)
+ return ret;
host = rport_to_shost(rport);
vha = shost_priv(host);
} else {
@@ -2549,15 +2999,34 @@ qla24xx_bsg_request(struct bsg_job *bsg_job)
vha = shost_priv(host);
}
+ /* Disable port will bring down the chip, allow enable command */
+ if (bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_MANAGE_HOST_PORT ||
+ bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_GET_HOST_STATS)
+ goto skip_chip_chk;
+
+ if (vha->hw->flags.port_isolated) {
+ bsg_reply->result = DID_ERROR;
+ /* operation not permitted */
+ return -EPERM;
+ }
+
if (qla2x00_chip_is_down(vha)) {
ql_dbg(ql_dbg_user, vha, 0x709f,
"BSG: ISP abort active/needed -- cmd=%d.\n",
bsg_request->msgcode);
+ SET_DID_STATUS(bsg_reply->result, DID_ERROR);
return -EBUSY;
}
- ql_dbg(ql_dbg_user, vha, 0x7000,
- "Entered %s msgcode=0x%x.\n", __func__, bsg_request->msgcode);
+ if (test_bit(PFLG_DRIVER_REMOVING, &vha->pci_flags)) {
+ SET_DID_STATUS(bsg_reply->result, DID_ERROR);
+ return -EIO;
+ }
+
+skip_chip_chk:
+ ql_dbg(ql_dbg_user + ql_dbg_verbose, vha, 0x7000,
+ "Entered %s msgcode=0x%x. bsg ptr %px\n",
+ __func__, bsg_request->msgcode, bsg_job);
switch (bsg_request->msgcode) {
case FC_BSG_RPT_ELS:
@@ -2568,7 +3037,7 @@ qla24xx_bsg_request(struct bsg_job *bsg_job)
ret = qla2x00_process_ct(bsg_job);
break;
case FC_BSG_HST_VENDOR:
- ret = qla2x00_process_vendor_specific(bsg_job);
+ ret = qla2x00_process_vendor_specific(vha, bsg_job);
break;
case FC_BSG_HST_ADD_RPORT:
case FC_BSG_HST_DEL_RPORT:
@@ -2577,60 +3046,203 @@ qla24xx_bsg_request(struct bsg_job *bsg_job)
ql_log(ql_log_warn, vha, 0x705a, "Unsupported BSG request.\n");
break;
}
+
+ ql_dbg(ql_dbg_user + ql_dbg_verbose, vha, 0x7000,
+ "%s done with return %x\n", __func__, ret);
+
return ret;
}
-int
-qla24xx_bsg_timeout(struct bsg_job *bsg_job)
+static bool qla_bsg_found(struct qla_qpair *qpair, struct bsg_job *bsg_job)
{
+ bool found, do_bsg_done;
struct fc_bsg_reply *bsg_reply = bsg_job->reply;
scsi_qla_host_t *vha = shost_priv(fc_bsg_to_shost(bsg_job));
struct qla_hw_data *ha = vha->hw;
- srb_t *sp;
- int cnt, que;
+ srb_t *sp = NULL;
+ int cnt;
unsigned long flags;
struct req_que *req;
+ int rval;
+ DECLARE_COMPLETION_ONSTACK(comp);
+ uint32_t ratov_j;
- /* find the bsg job from the active list of commands */
- spin_lock_irqsave(&ha->hardware_lock, flags);
- for (que = 0; que < ha->max_req_queues; que++) {
- req = ha->req_q_map[que];
- if (!req)
- continue;
+ found = do_bsg_done = false;
- for (cnt = 1; cnt < req->num_outstanding_cmds; cnt++) {
- sp = req->outstanding_cmds[cnt];
- if (sp) {
- if (((sp->type == SRB_CT_CMD) ||
- (sp->type == SRB_ELS_CMD_HST) ||
- (sp->type == SRB_FXIOCB_BCMD))
- && (sp->u.bsg_job == bsg_job)) {
- req->outstanding_cmds[cnt] = NULL;
- spin_unlock_irqrestore(&ha->hardware_lock, flags);
- if (ha->isp_ops->abort_command(sp)) {
- ql_log(ql_log_warn, vha, 0x7089,
- "mbx abort_command "
- "failed.\n");
- bsg_reply->result = -EIO;
- } else {
- ql_dbg(ql_dbg_user, vha, 0x708a,
- "mbx abort_command "
- "success.\n");
- bsg_reply->result = 0;
- }
- spin_lock_irqsave(&ha->hardware_lock, flags);
- goto done;
- }
- }
+ spin_lock_irqsave(qpair->qp_lock_ptr, flags);
+ req = qpair->req;
+
+ for (cnt = 1; cnt < req->num_outstanding_cmds; cnt++) {
+ sp = req->outstanding_cmds[cnt];
+ if (sp &&
+ (sp->type == SRB_CT_CMD ||
+ sp->type == SRB_ELS_CMD_HST ||
+ sp->type == SRB_ELS_CMD_HST_NOLOGIN) &&
+ sp->u.bsg_job == bsg_job) {
+
+ found = true;
+ sp->comp = &comp;
+ break;
}
}
- spin_unlock_irqrestore(&ha->hardware_lock, flags);
- ql_log(ql_log_info, vha, 0x708b, "SRB not found to abort.\n");
+ spin_unlock_irqrestore(qpair->qp_lock_ptr, flags);
+
+ if (!found)
+ return false;
+
+ if (ha->flags.eeh_busy) {
+ /* skip over abort. EEH handling will return the bsg. Wait for it */
+ rval = QLA_SUCCESS;
+ ql_dbg(ql_dbg_user, vha, 0x802c,
+ "eeh encounter. bsg %p sp=%p handle=%x \n",
+ bsg_job, sp, sp->handle);
+ } else {
+ rval = ha->isp_ops->abort_command(sp);
+ ql_dbg(ql_dbg_user, vha, 0x802c,
+ "Aborting bsg %p sp=%p handle=%x rval=%x\n",
+ bsg_job, sp, sp->handle, rval);
+ }
+
+ switch (rval) {
+ case QLA_SUCCESS:
+ /* Wait for the command completion. */
+ ratov_j = ha->r_a_tov / 10 * 4;
+ ratov_j = secs_to_jiffies(ratov_j);
+
+ if (!wait_for_completion_timeout(&comp, ratov_j)) {
+ ql_log(ql_log_info, vha, 0x7089,
+ "bsg abort timeout. bsg=%p sp=%p handle %#x .\n",
+ bsg_job, sp, sp->handle);
+
+ do_bsg_done = true;
+ } else {
+ /* fw had returned the bsg */
+ ql_dbg(ql_dbg_user, vha, 0x708a,
+ "bsg abort success. bsg %p sp=%p handle=%#x\n",
+ bsg_job, sp, sp->handle);
+ do_bsg_done = false;
+ }
+ break;
+ default:
+ ql_log(ql_log_info, vha, 0x704f,
+ "bsg abort fail. bsg=%p sp=%p rval=%x.\n",
+ bsg_job, sp, rval);
+
+ do_bsg_done = true;
+ break;
+ }
+
+ if (!do_bsg_done)
+ return true;
+
+ spin_lock_irqsave(qpair->qp_lock_ptr, flags);
+ /*
+ * recheck to make sure it's still the same bsg_job due to
+ * qp_lock_ptr was released earlier.
+ */
+ if (req->outstanding_cmds[cnt] &&
+ req->outstanding_cmds[cnt]->u.bsg_job != bsg_job) {
+ /* fw had returned the bsg */
+ spin_unlock_irqrestore(qpair->qp_lock_ptr, flags);
+ return true;
+ }
+ req->outstanding_cmds[cnt] = NULL;
+ spin_unlock_irqrestore(qpair->qp_lock_ptr, flags);
+
+ /* ref: INIT */
+ sp->comp = NULL;
+ kref_put(&sp->cmd_kref, qla2x00_sp_release);
bsg_reply->result = -ENXIO;
- return 0;
+ bsg_reply->reply_payload_rcv_len = 0;
+
+ ql_dbg(ql_dbg_user, vha, 0x7051,
+ "%s bsg_job_done : bsg %p result %#x sp %p.\n",
+ __func__, bsg_job, bsg_reply->result, sp);
+
+ bsg_job_done(bsg_job, bsg_reply->result, bsg_reply->reply_payload_rcv_len);
+
+ return true;
+}
+
+int
+qla24xx_bsg_timeout(struct bsg_job *bsg_job)
+{
+ struct fc_bsg_request *bsg_request = bsg_job->request;
+ scsi_qla_host_t *vha = shost_priv(fc_bsg_to_shost(bsg_job));
+ struct qla_hw_data *ha = vha->hw;
+ int i;
+ struct qla_qpair *qpair;
+
+ ql_log(ql_log_info, vha, 0x708b,
+ "%s CMD timeout. bsg ptr %p msgcode %x vendor cmd %x\n",
+ __func__, bsg_job, bsg_request->msgcode,
+ bsg_request->rqst_data.h_vendor.vendor_cmd[0]);
+
+ if (qla2x00_isp_reg_stat(ha)) {
+ ql_log(ql_log_info, vha, 0x9007,
+ "PCI/Register disconnect.\n");
+ qla_pci_set_eeh_busy(vha);
+ }
+
+ if (qla_bsg_found(ha->base_qpair, bsg_job))
+ goto done;
+
+ /* find the bsg job from the active list of commands */
+ for (i = 0; i < ha->max_qpairs; i++) {
+ qpair = vha->hw->queue_pair_map[i];
+ if (!qpair)
+ continue;
+ if (qla_bsg_found(qpair, bsg_job))
+ goto done;
+ }
+
+ ql_log(ql_log_info, vha, 0x708b, "SRB not found to abort.\n");
done:
- spin_unlock_irqrestore(&ha->hardware_lock, flags);
- sp->free(sp);
return 0;
}
+
+int qla2x00_mailbox_passthru(struct bsg_job *bsg_job)
+{
+ struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+ scsi_qla_host_t *vha = shost_priv(fc_bsg_to_shost(bsg_job));
+ int ret = -EINVAL;
+ int ptsize = sizeof(struct qla_mbx_passthru);
+ struct qla_mbx_passthru *req_data = NULL;
+ uint32_t req_data_len;
+
+ req_data_len = bsg_job->request_payload.payload_len;
+ if (req_data_len != ptsize) {
+ ql_log(ql_log_warn, vha, 0xf0a3, "req_data_len invalid.\n");
+ return -EIO;
+ }
+ req_data = kzalloc(ptsize, GFP_KERNEL);
+ if (!req_data) {
+ ql_log(ql_log_warn, vha, 0xf0a4,
+ "req_data memory allocation failure.\n");
+ return -ENOMEM;
+ }
+
+ /* Copy the request buffer in req_data */
+ sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, req_data, ptsize);
+ ret = qla_mailbox_passthru(vha, req_data->mbx_in, req_data->mbx_out);
+
+ /* Copy the req_data in request buffer */
+ sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, req_data, ptsize);
+
+ bsg_reply->reply_payload_rcv_len = ptsize;
+ if (ret == QLA_SUCCESS)
+ bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = EXT_STATUS_OK;
+ else
+ bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = EXT_STATUS_ERR;
+
+ bsg_job->reply_len = sizeof(*bsg_job->reply);
+ bsg_reply->result = DID_OK << 16;
+ bsg_job_done(bsg_job, bsg_reply->result, bsg_reply->reply_payload_rcv_len);
+
+ kfree(req_data);
+
+ return ret;
+}