|
@@ -1888,7 +1888,7 @@ nvme_fc_start_fcp_op(struct nvme_fc_ctrl *ctrl, struct nvme_fc_queue *queue,
|
|
|
* the target device is present
|
|
|
*/
|
|
|
if (ctrl->rport->remoteport.port_state != FC_OBJSTATE_ONLINE)
|
|
|
- return BLK_STS_IOERR;
|
|
|
+ goto busy;
|
|
|
|
|
|
if (!nvme_fc_ctrl_get(ctrl))
|
|
|
return BLK_STS_IOERR;
|
|
@@ -1958,22 +1958,25 @@ nvme_fc_start_fcp_op(struct nvme_fc_ctrl *ctrl, struct nvme_fc_queue *queue,
|
|
|
queue->lldd_handle, &op->fcp_req);
|
|
|
|
|
|
if (ret) {
|
|
|
- if (op->rq) /* normal request */
|
|
|
+ if (!(op->flags & FCOP_FLAGS_AEN))
|
|
|
nvme_fc_unmap_data(ctrl, op->rq, op);
|
|
|
- /* else - aen. no cleanup needed */
|
|
|
|
|
|
nvme_fc_ctrl_put(ctrl);
|
|
|
|
|
|
- if (ret != -EBUSY)
|
|
|
+ if (ctrl->rport->remoteport.port_state == FC_OBJSTATE_ONLINE &&
|
|
|
+ ret != -EBUSY)
|
|
|
return BLK_STS_IOERR;
|
|
|
|
|
|
- if (op->rq)
|
|
|
- blk_mq_delay_run_hw_queue(queue->hctx, NVMEFC_QUEUE_DELAY);
|
|
|
-
|
|
|
- return BLK_STS_RESOURCE;
|
|
|
+ goto busy;
|
|
|
}
|
|
|
|
|
|
return BLK_STS_OK;
|
|
|
+
|
|
|
+busy:
|
|
|
+ if (!(op->flags & FCOP_FLAGS_AEN) && queue->hctx)
|
|
|
+ blk_mq_delay_run_hw_queue(queue->hctx, NVMEFC_QUEUE_DELAY);
|
|
|
+
|
|
|
+ return BLK_STS_RESOURCE;
|
|
|
}
|
|
|
|
|
|
static blk_status_t
|
|
@@ -2802,66 +2805,70 @@ out_fail:
|
|
|
return ERR_PTR(ret);
|
|
|
}
|
|
|
|
|
|
-enum {
|
|
|
- FCT_TRADDR_ERR = 0,
|
|
|
- FCT_TRADDR_WWNN = 1 << 0,
|
|
|
- FCT_TRADDR_WWPN = 1 << 1,
|
|
|
-};
|
|
|
|
|
|
struct nvmet_fc_traddr {
|
|
|
u64 nn;
|
|
|
u64 pn;
|
|
|
};
|
|
|
|
|
|
-static const match_table_t traddr_opt_tokens = {
|
|
|
- { FCT_TRADDR_WWNN, "nn-%s" },
|
|
|
- { FCT_TRADDR_WWPN, "pn-%s" },
|
|
|
- { FCT_TRADDR_ERR, NULL }
|
|
|
-};
|
|
|
-
|
|
|
static int
|
|
|
-nvme_fc_parse_address(struct nvmet_fc_traddr *traddr, char *buf)
|
|
|
+__nvme_fc_parse_u64(substring_t *sstr, u64 *val)
|
|
|
{
|
|
|
- substring_t args[MAX_OPT_ARGS];
|
|
|
- char *options, *o, *p;
|
|
|
- int token, ret = 0;
|
|
|
u64 token64;
|
|
|
|
|
|
- options = o = kstrdup(buf, GFP_KERNEL);
|
|
|
- if (!options)
|
|
|
- return -ENOMEM;
|
|
|
+ if (match_u64(sstr, &token64))
|
|
|
+ return -EINVAL;
|
|
|
+ *val = token64;
|
|
|
|
|
|
- while ((p = strsep(&o, ":\n")) != NULL) {
|
|
|
- if (!*p)
|
|
|
- continue;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
- token = match_token(p, traddr_opt_tokens, args);
|
|
|
- switch (token) {
|
|
|
- case FCT_TRADDR_WWNN:
|
|
|
- if (match_u64(args, &token64)) {
|
|
|
- ret = -EINVAL;
|
|
|
- goto out;
|
|
|
- }
|
|
|
- traddr->nn = token64;
|
|
|
- break;
|
|
|
- case FCT_TRADDR_WWPN:
|
|
|
- if (match_u64(args, &token64)) {
|
|
|
- ret = -EINVAL;
|
|
|
- goto out;
|
|
|
- }
|
|
|
- traddr->pn = token64;
|
|
|
- break;
|
|
|
- default:
|
|
|
- pr_warn("unknown traddr token or missing value '%s'\n",
|
|
|
- p);
|
|
|
- ret = -EINVAL;
|
|
|
- goto out;
|
|
|
- }
|
|
|
- }
|
|
|
+/*
|
|
|
+ * This routine validates and extracts the WWN's from the TRADDR string.
|
|
|
+ * As kernel parsers need the 0x to determine number base, universally
|
|
|
+ * build string to parse with 0x prefix before parsing name strings.
|
|
|
+ */
|
|
|
+static int
|
|
|
+nvme_fc_parse_traddr(struct nvmet_fc_traddr *traddr, char *buf, size_t blen)
|
|
|
+{
|
|
|
+ char name[2 + NVME_FC_TRADDR_HEXNAMELEN + 1];
|
|
|
+ substring_t wwn = { name, &name[sizeof(name)-1] };
|
|
|
+ int nnoffset, pnoffset;
|
|
|
+
|
|
|
+ /* validate it string one of the 2 allowed formats */
|
|
|
+ if (strnlen(buf, blen) == NVME_FC_TRADDR_MAXLENGTH &&
|
|
|
+ !strncmp(buf, "nn-0x", NVME_FC_TRADDR_OXNNLEN) &&
|
|
|
+ !strncmp(&buf[NVME_FC_TRADDR_MAX_PN_OFFSET],
|
|
|
+ "pn-0x", NVME_FC_TRADDR_OXNNLEN)) {
|
|
|
+ nnoffset = NVME_FC_TRADDR_OXNNLEN;
|
|
|
+ pnoffset = NVME_FC_TRADDR_MAX_PN_OFFSET +
|
|
|
+ NVME_FC_TRADDR_OXNNLEN;
|
|
|
+ } else if ((strnlen(buf, blen) == NVME_FC_TRADDR_MINLENGTH &&
|
|
|
+ !strncmp(buf, "nn-", NVME_FC_TRADDR_NNLEN) &&
|
|
|
+ !strncmp(&buf[NVME_FC_TRADDR_MIN_PN_OFFSET],
|
|
|
+ "pn-", NVME_FC_TRADDR_NNLEN))) {
|
|
|
+ nnoffset = NVME_FC_TRADDR_NNLEN;
|
|
|
+ pnoffset = NVME_FC_TRADDR_MIN_PN_OFFSET + NVME_FC_TRADDR_NNLEN;
|
|
|
+ } else
|
|
|
+ goto out_einval;
|
|
|
|
|
|
-out:
|
|
|
- kfree(options);
|
|
|
- return ret;
|
|
|
+ name[0] = '0';
|
|
|
+ name[1] = 'x';
|
|
|
+ name[2 + NVME_FC_TRADDR_HEXNAMELEN] = 0;
|
|
|
+
|
|
|
+ memcpy(&name[2], &buf[nnoffset], NVME_FC_TRADDR_HEXNAMELEN);
|
|
|
+ if (__nvme_fc_parse_u64(&wwn, &traddr->nn))
|
|
|
+ goto out_einval;
|
|
|
+
|
|
|
+ memcpy(&name[2], &buf[pnoffset], NVME_FC_TRADDR_HEXNAMELEN);
|
|
|
+ if (__nvme_fc_parse_u64(&wwn, &traddr->pn))
|
|
|
+ goto out_einval;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+out_einval:
|
|
|
+ pr_warn("%s: bad traddr string\n", __func__);
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
|
|
|
static struct nvme_ctrl *
|
|
@@ -2875,11 +2882,11 @@ nvme_fc_create_ctrl(struct device *dev, struct nvmf_ctrl_options *opts)
|
|
|
unsigned long flags;
|
|
|
int ret;
|
|
|
|
|
|
- ret = nvme_fc_parse_address(&raddr, opts->traddr);
|
|
|
+ ret = nvme_fc_parse_traddr(&raddr, opts->traddr, NVMF_TRADDR_SIZE);
|
|
|
if (ret || !raddr.nn || !raddr.pn)
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
|
|
- ret = nvme_fc_parse_address(&laddr, opts->host_traddr);
|
|
|
+ ret = nvme_fc_parse_traddr(&laddr, opts->host_traddr, NVMF_TRADDR_SIZE);
|
|
|
if (ret || !laddr.nn || !laddr.pn)
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|