Skip to content

Commit 9c5358e

Browse files
jsmart-ghChristoph Hellwig
authored andcommitted
nvme-fc: revise TRADDR parsing
The FC-NVME spec hasn't locked down on the format string for TRADDR. Currently the spec is lobbying for "nn-<16hexdigits>:pn-<16hexdigits>" where the wwn's are hex values but not prefixed by 0x. Most implementations so far expect a string format of "nn-0x<16hexdigits>:pn-0x<16hexdigits>" to be used. The transport uses the match_u64 parser which requires a leading 0x prefix to set the base properly. If it's not there, a match will either fail or return a base 10 value. The resolution in T11 is pushing out. Therefore, to fix things now and to cover any eventuality and any implementations already in the field, this patch adds support for both formats. The change consists of replacing the token matching routine with a routine that validates the fixed string format, and then builds a local copy of the hex name with a 0x prefix before calling the system parser. Note: the same parser routine exists in both the initiator and target transports. Given this is about the only "shared" item, we chose to replicate rather than create an interdendency on some shared code. Signed-off-by: James Smart <[email protected]> Signed-off-by: Christoph Hellwig <[email protected]>
1 parent 8b25f35 commit 9c5358e

File tree

3 files changed

+125
-97
lines changed

3 files changed

+125
-97
lines changed

drivers/nvme/host/fc.c

Lines changed: 53 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2805,66 +2805,70 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
28052805
return ERR_PTR(ret);
28062806
}
28072807

2808-
enum {
2809-
FCT_TRADDR_ERR = 0,
2810-
FCT_TRADDR_WWNN = 1 << 0,
2811-
FCT_TRADDR_WWPN = 1 << 1,
2812-
};
28132808

28142809
struct nvmet_fc_traddr {
28152810
u64 nn;
28162811
u64 pn;
28172812
};
28182813

2819-
static const match_table_t traddr_opt_tokens = {
2820-
{ FCT_TRADDR_WWNN, "nn-%s" },
2821-
{ FCT_TRADDR_WWPN, "pn-%s" },
2822-
{ FCT_TRADDR_ERR, NULL }
2823-
};
2824-
28252814
static int
2826-
nvme_fc_parse_address(struct nvmet_fc_traddr *traddr, char *buf)
2815+
__nvme_fc_parse_u64(substring_t *sstr, u64 *val)
28272816
{
2828-
substring_t args[MAX_OPT_ARGS];
2829-
char *options, *o, *p;
2830-
int token, ret = 0;
28312817
u64 token64;
28322818

2833-
options = o = kstrdup(buf, GFP_KERNEL);
2834-
if (!options)
2835-
return -ENOMEM;
2819+
if (match_u64(sstr, &token64))
2820+
return -EINVAL;
2821+
*val = token64;
28362822

2837-
while ((p = strsep(&o, ":\n")) != NULL) {
2838-
if (!*p)
2839-
continue;
2823+
return 0;
2824+
}
28402825

2841-
token = match_token(p, traddr_opt_tokens, args);
2842-
switch (token) {
2843-
case FCT_TRADDR_WWNN:
2844-
if (match_u64(args, &token64)) {
2845-
ret = -EINVAL;
2846-
goto out;
2847-
}
2848-
traddr->nn = token64;
2849-
break;
2850-
case FCT_TRADDR_WWPN:
2851-
if (match_u64(args, &token64)) {
2852-
ret = -EINVAL;
2853-
goto out;
2854-
}
2855-
traddr->pn = token64;
2856-
break;
2857-
default:
2858-
pr_warn("unknown traddr token or missing value '%s'\n",
2859-
p);
2860-
ret = -EINVAL;
2861-
goto out;
2862-
}
2863-
}
2826+
/*
2827+
* This routine validates and extracts the WWN's from the TRADDR string.
2828+
* As kernel parsers need the 0x to determine number base, universally
2829+
* build string to parse with 0x prefix before parsing name strings.
2830+
*/
2831+
static int
2832+
nvme_fc_parse_traddr(struct nvmet_fc_traddr *traddr, char *buf, size_t blen)
2833+
{
2834+
char name[2 + NVME_FC_TRADDR_HEXNAMELEN + 1];
2835+
substring_t wwn = { name, &name[sizeof(name)-1] };
2836+
int nnoffset, pnoffset;
2837+
2838+
/* validate it string one of the 2 allowed formats */
2839+
if (strnlen(buf, blen) == NVME_FC_TRADDR_MAXLENGTH &&
2840+
!strncmp(buf, "nn-0x", NVME_FC_TRADDR_OXNNLEN) &&
2841+
!strncmp(&buf[NVME_FC_TRADDR_MAX_PN_OFFSET],
2842+
"pn-0x", NVME_FC_TRADDR_OXNNLEN)) {
2843+
nnoffset = NVME_FC_TRADDR_OXNNLEN;
2844+
pnoffset = NVME_FC_TRADDR_MAX_PN_OFFSET +
2845+
NVME_FC_TRADDR_OXNNLEN;
2846+
} else if ((strnlen(buf, blen) == NVME_FC_TRADDR_MINLENGTH &&
2847+
!strncmp(buf, "nn-", NVME_FC_TRADDR_NNLEN) &&
2848+
!strncmp(&buf[NVME_FC_TRADDR_MIN_PN_OFFSET],
2849+
"pn-", NVME_FC_TRADDR_NNLEN))) {
2850+
nnoffset = NVME_FC_TRADDR_NNLEN;
2851+
pnoffset = NVME_FC_TRADDR_MIN_PN_OFFSET + NVME_FC_TRADDR_NNLEN;
2852+
} else
2853+
goto out_einval;
28642854

2865-
out:
2866-
kfree(options);
2867-
return ret;
2855+
name[0] = '0';
2856+
name[1] = 'x';
2857+
name[2 + NVME_FC_TRADDR_HEXNAMELEN] = 0;
2858+
2859+
memcpy(&name[2], &buf[nnoffset], NVME_FC_TRADDR_HEXNAMELEN);
2860+
if (__nvme_fc_parse_u64(&wwn, &traddr->nn))
2861+
goto out_einval;
2862+
2863+
memcpy(&name[2], &buf[pnoffset], NVME_FC_TRADDR_HEXNAMELEN);
2864+
if (__nvme_fc_parse_u64(&wwn, &traddr->pn))
2865+
goto out_einval;
2866+
2867+
return 0;
2868+
2869+
out_einval:
2870+
pr_warn("%s: bad traddr string\n", __func__);
2871+
return -EINVAL;
28682872
}
28692873

28702874
static struct nvme_ctrl *
@@ -2878,11 +2882,11 @@ nvme_fc_create_ctrl(struct device *dev, struct nvmf_ctrl_options *opts)
28782882
unsigned long flags;
28792883
int ret;
28802884

2881-
ret = nvme_fc_parse_address(&raddr, opts->traddr);
2885+
ret = nvme_fc_parse_traddr(&raddr, opts->traddr, NVMF_TRADDR_SIZE);
28822886
if (ret || !raddr.nn || !raddr.pn)
28832887
return ERR_PTR(-EINVAL);
28842888

2885-
ret = nvme_fc_parse_address(&laddr, opts->host_traddr);
2889+
ret = nvme_fc_parse_traddr(&laddr, opts->host_traddr, NVMF_TRADDR_SIZE);
28862890
if (ret || !laddr.nn || !laddr.pn)
28872891
return ERR_PTR(-EINVAL);
28882892

drivers/nvme/target/fc.c

Lines changed: 53 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2293,66 +2293,70 @@ nvmet_fc_rcv_fcp_abort(struct nvmet_fc_target_port *target_port,
22932293
}
22942294
EXPORT_SYMBOL_GPL(nvmet_fc_rcv_fcp_abort);
22952295

2296-
enum {
2297-
FCT_TRADDR_ERR = 0,
2298-
FCT_TRADDR_WWNN = 1 << 0,
2299-
FCT_TRADDR_WWPN = 1 << 1,
2300-
};
23012296

23022297
struct nvmet_fc_traddr {
23032298
u64 nn;
23042299
u64 pn;
23052300
};
23062301

2307-
static const match_table_t traddr_opt_tokens = {
2308-
{ FCT_TRADDR_WWNN, "nn-%s" },
2309-
{ FCT_TRADDR_WWPN, "pn-%s" },
2310-
{ FCT_TRADDR_ERR, NULL }
2311-
};
2312-
23132302
static int
2314-
nvmet_fc_parse_traddr(struct nvmet_fc_traddr *traddr, char *buf)
2303+
__nvme_fc_parse_u64(substring_t *sstr, u64 *val)
23152304
{
2316-
substring_t args[MAX_OPT_ARGS];
2317-
char *options, *o, *p;
2318-
int token, ret = 0;
23192305
u64 token64;
23202306

2321-
options = o = kstrdup(buf, GFP_KERNEL);
2322-
if (!options)
2323-
return -ENOMEM;
2307+
if (match_u64(sstr, &token64))
2308+
return -EINVAL;
2309+
*val = token64;
23242310

2325-
while ((p = strsep(&o, ":\n")) != NULL) {
2326-
if (!*p)
2327-
continue;
2311+
return 0;
2312+
}
23282313

2329-
token = match_token(p, traddr_opt_tokens, args);
2330-
switch (token) {
2331-
case FCT_TRADDR_WWNN:
2332-
if (match_u64(args, &token64)) {
2333-
ret = -EINVAL;
2334-
goto out;
2335-
}
2336-
traddr->nn = token64;
2337-
break;
2338-
case FCT_TRADDR_WWPN:
2339-
if (match_u64(args, &token64)) {
2340-
ret = -EINVAL;
2341-
goto out;
2342-
}
2343-
traddr->pn = token64;
2344-
break;
2345-
default:
2346-
pr_warn("unknown traddr token or missing value '%s'\n",
2347-
p);
2348-
ret = -EINVAL;
2349-
goto out;
2350-
}
2351-
}
2314+
/*
2315+
* This routine validates and extracts the WWN's from the TRADDR string.
2316+
* As kernel parsers need the 0x to determine number base, universally
2317+
* build string to parse with 0x prefix before parsing name strings.
2318+
*/
2319+
static int
2320+
nvme_fc_parse_traddr(struct nvmet_fc_traddr *traddr, char *buf, size_t blen)
2321+
{
2322+
char name[2 + NVME_FC_TRADDR_HEXNAMELEN + 1];
2323+
substring_t wwn = { name, &name[sizeof(name)-1] };
2324+
int nnoffset, pnoffset;
2325+
2326+
/* validate it string one of the 2 allowed formats */
2327+
if (strnlen(buf, blen) == NVME_FC_TRADDR_MAXLENGTH &&
2328+
!strncmp(buf, "nn-0x", NVME_FC_TRADDR_OXNNLEN) &&
2329+
!strncmp(&buf[NVME_FC_TRADDR_MAX_PN_OFFSET],
2330+
"pn-0x", NVME_FC_TRADDR_OXNNLEN)) {
2331+
nnoffset = NVME_FC_TRADDR_OXNNLEN;
2332+
pnoffset = NVME_FC_TRADDR_MAX_PN_OFFSET +
2333+
NVME_FC_TRADDR_OXNNLEN;
2334+
} else if ((strnlen(buf, blen) == NVME_FC_TRADDR_MINLENGTH &&
2335+
!strncmp(buf, "nn-", NVME_FC_TRADDR_NNLEN) &&
2336+
!strncmp(&buf[NVME_FC_TRADDR_MIN_PN_OFFSET],
2337+
"pn-", NVME_FC_TRADDR_NNLEN))) {
2338+
nnoffset = NVME_FC_TRADDR_NNLEN;
2339+
pnoffset = NVME_FC_TRADDR_MIN_PN_OFFSET + NVME_FC_TRADDR_NNLEN;
2340+
} else
2341+
goto out_einval;
2342+
2343+
name[0] = '0';
2344+
name[1] = 'x';
2345+
name[2 + NVME_FC_TRADDR_HEXNAMELEN] = 0;
2346+
2347+
memcpy(&name[2], &buf[nnoffset], NVME_FC_TRADDR_HEXNAMELEN);
2348+
if (__nvme_fc_parse_u64(&wwn, &traddr->nn))
2349+
goto out_einval;
2350+
2351+
memcpy(&name[2], &buf[pnoffset], NVME_FC_TRADDR_HEXNAMELEN);
2352+
if (__nvme_fc_parse_u64(&wwn, &traddr->pn))
2353+
goto out_einval;
23522354

2353-
out:
2354-
kfree(options);
2355-
return ret;
2355+
return 0;
2356+
2357+
out_einval:
2358+
pr_warn("%s: bad traddr string\n", __func__);
2359+
return -EINVAL;
23562360
}
23572361

23582362
static int
@@ -2370,7 +2374,8 @@ nvmet_fc_add_port(struct nvmet_port *port)
23702374

23712375
/* map the traddr address info to a target port */
23722376

2373-
ret = nvmet_fc_parse_traddr(&traddr, port->disc_addr.traddr);
2377+
ret = nvme_fc_parse_traddr(&traddr, port->disc_addr.traddr,
2378+
sizeof(port->disc_addr.traddr));
23742379
if (ret)
23752380
return ret;
23762381

include/linux/nvme-fc.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,5 +334,24 @@ struct fcnvme_ls_disconnect_acc {
334334
#define NVME_FC_LS_TIMEOUT_SEC 2 /* 2 seconds */
335335
#define NVME_FC_TGTOP_TIMEOUT_SEC 2 /* 2 seconds */
336336

337+
/*
338+
* TRADDR string must be of form "nn-<16hexdigits>:pn-<16hexdigits>"
339+
* the string is allowed to be specified with or without a "0x" prefix
340+
* infront of the <16hexdigits>. Without is considered the "min" string
341+
* and with is considered the "max" string. The hexdigits may be upper
342+
* or lower case.
343+
*/
344+
#define NVME_FC_TRADDR_NNLEN 3 /* "?n-" */
345+
#define NVME_FC_TRADDR_OXNNLEN 5 /* "?n-0x" */
346+
#define NVME_FC_TRADDR_HEXNAMELEN 16
347+
#define NVME_FC_TRADDR_MINLENGTH \
348+
(2 * (NVME_FC_TRADDR_NNLEN + NVME_FC_TRADDR_HEXNAMELEN) + 1)
349+
#define NVME_FC_TRADDR_MAXLENGTH \
350+
(2 * (NVME_FC_TRADDR_OXNNLEN + NVME_FC_TRADDR_HEXNAMELEN) + 1)
351+
#define NVME_FC_TRADDR_MIN_PN_OFFSET \
352+
(NVME_FC_TRADDR_NNLEN + NVME_FC_TRADDR_HEXNAMELEN + 1)
353+
#define NVME_FC_TRADDR_MAX_PN_OFFSET \
354+
(NVME_FC_TRADDR_OXNNLEN + NVME_FC_TRADDR_HEXNAMELEN + 1)
355+
337356

338357
#endif /* _NVME_FC_H */

0 commit comments

Comments
 (0)