aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Smart <jsmart2021@gmail.com>2017-07-17 16:59:39 -0400
committerChristoph Hellwig <hch@lst.de>2017-07-25 12:05:25 -0400
commit9c5358e15ca12ed3dc3b1e51671dee5d155de8e0 (patch)
tree53ce8b540ad8f55686ef56a42dcc309b14e513d3
parent8b25f351929b5a5216ccb2c8882965134019679d (diff)
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 <james.smart@broadcom.com> Signed-off-by: Christoph Hellwig <hch@lst.de>
-rw-r--r--drivers/nvme/host/fc.c102
-rw-r--r--drivers/nvme/target/fc.c101
-rw-r--r--include/linux/nvme-fc.h19
3 files changed, 125 insertions, 97 deletions
diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c
index 5630ca46c3b5..5c2a08ef08ba 100644
--- a/drivers/nvme/host/fc.c
+++ b/drivers/nvme/host/fc.c
@@ -2805,66 +2805,70 @@ out_fail:
2805 return ERR_PTR(ret); 2805 return ERR_PTR(ret);
2806} 2806}
2807 2807
2808enum {
2809 FCT_TRADDR_ERR = 0,
2810 FCT_TRADDR_WWNN = 1 << 0,
2811 FCT_TRADDR_WWPN = 1 << 1,
2812};
2813 2808
2814struct nvmet_fc_traddr { 2809struct nvmet_fc_traddr {
2815 u64 nn; 2810 u64 nn;
2816 u64 pn; 2811 u64 pn;
2817}; 2812};
2818 2813
2819static 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
2825static int 2814static int
2826nvme_fc_parse_address(struct nvmet_fc_traddr *traddr, char *buf) 2815__nvme_fc_parse_u64(substring_t *sstr, u64 *val)
2827{ 2816{
2828 substring_t args[MAX_OPT_ARGS];
2829 char *options, *o, *p;
2830 int token, ret = 0;
2831 u64 token64; 2817 u64 token64;
2832 2818
2833 options = o = kstrdup(buf, GFP_KERNEL); 2819 if (match_u64(sstr, &token64))
2834 if (!options) 2820 return -EINVAL;
2835 return -ENOMEM; 2821 *val = token64;
2836 2822
2837 while ((p = strsep(&o, ":\n")) != NULL) { 2823 return 0;
2838 if (!*p) 2824}
2839 continue;
2840 2825
2841 token = match_token(p, traddr_opt_tokens, args); 2826/*
2842 switch (token) { 2827 * This routine validates and extracts the WWN's from the TRADDR string.
2843 case FCT_TRADDR_WWNN: 2828 * As kernel parsers need the 0x to determine number base, universally
2844 if (match_u64(args, &token64)) { 2829 * build string to parse with 0x prefix before parsing name strings.
2845 ret = -EINVAL; 2830 */
2846 goto out; 2831static int
2847 } 2832nvme_fc_parse_traddr(struct nvmet_fc_traddr *traddr, char *buf, size_t blen)
2848 traddr->nn = token64; 2833{
2849 break; 2834 char name[2 + NVME_FC_TRADDR_HEXNAMELEN + 1];
2850 case FCT_TRADDR_WWPN: 2835 substring_t wwn = { name, &name[sizeof(name)-1] };
2851 if (match_u64(args, &token64)) { 2836 int nnoffset, pnoffset;
2852 ret = -EINVAL; 2837
2853 goto out; 2838 /* validate it string one of the 2 allowed formats */
2854 } 2839 if (strnlen(buf, blen) == NVME_FC_TRADDR_MAXLENGTH &&
2855 traddr->pn = token64; 2840 !strncmp(buf, "nn-0x", NVME_FC_TRADDR_OXNNLEN) &&
2856 break; 2841 !strncmp(&buf[NVME_FC_TRADDR_MAX_PN_OFFSET],
2857 default: 2842 "pn-0x", NVME_FC_TRADDR_OXNNLEN)) {
2858 pr_warn("unknown traddr token or missing value '%s'\n", 2843 nnoffset = NVME_FC_TRADDR_OXNNLEN;
2859 p); 2844 pnoffset = NVME_FC_TRADDR_MAX_PN_OFFSET +
2860 ret = -EINVAL; 2845 NVME_FC_TRADDR_OXNNLEN;
2861 goto out; 2846 } else if ((strnlen(buf, blen) == NVME_FC_TRADDR_MINLENGTH &&
2862 } 2847 !strncmp(buf, "nn-", NVME_FC_TRADDR_NNLEN) &&
2863 } 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;
2864 2854
2865out: 2855 name[0] = '0';
2866 kfree(options); 2856 name[1] = 'x';
2867 return ret; 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
2869out_einval:
2870 pr_warn("%s: bad traddr string\n", __func__);
2871 return -EINVAL;
2868} 2872}
2869 2873
2870static struct nvme_ctrl * 2874static struct nvme_ctrl *
@@ -2878,11 +2882,11 @@ nvme_fc_create_ctrl(struct device *dev, struct nvmf_ctrl_options *opts)
2878 unsigned long flags; 2882 unsigned long flags;
2879 int ret; 2883 int ret;
2880 2884
2881 ret = nvme_fc_parse_address(&raddr, opts->traddr); 2885 ret = nvme_fc_parse_traddr(&raddr, opts->traddr, NVMF_TRADDR_SIZE);
2882 if (ret || !raddr.nn || !raddr.pn) 2886 if (ret || !raddr.nn || !raddr.pn)
2883 return ERR_PTR(-EINVAL); 2887 return ERR_PTR(-EINVAL);
2884 2888
2885 ret = nvme_fc_parse_address(&laddr, opts->host_traddr); 2889 ret = nvme_fc_parse_traddr(&laddr, opts->host_traddr, NVMF_TRADDR_SIZE);
2886 if (ret || !laddr.nn || !laddr.pn) 2890 if (ret || !laddr.nn || !laddr.pn)
2887 return ERR_PTR(-EINVAL); 2891 return ERR_PTR(-EINVAL);
2888 2892
diff --git a/drivers/nvme/target/fc.c b/drivers/nvme/target/fc.c
index d5801c150b1c..31ca55dfcb1d 100644
--- a/drivers/nvme/target/fc.c
+++ b/drivers/nvme/target/fc.c
@@ -2293,66 +2293,70 @@ nvmet_fc_rcv_fcp_abort(struct nvmet_fc_target_port *target_port,
2293} 2293}
2294EXPORT_SYMBOL_GPL(nvmet_fc_rcv_fcp_abort); 2294EXPORT_SYMBOL_GPL(nvmet_fc_rcv_fcp_abort);
2295 2295
2296enum {
2297 FCT_TRADDR_ERR = 0,
2298 FCT_TRADDR_WWNN = 1 << 0,
2299 FCT_TRADDR_WWPN = 1 << 1,
2300};
2301 2296
2302struct nvmet_fc_traddr { 2297struct nvmet_fc_traddr {
2303 u64 nn; 2298 u64 nn;
2304 u64 pn; 2299 u64 pn;
2305}; 2300};
2306 2301
2307static 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
2313static int 2302static int
2314nvmet_fc_parse_traddr(struct nvmet_fc_traddr *traddr, char *buf) 2303__nvme_fc_parse_u64(substring_t *sstr, u64 *val)
2315{ 2304{
2316 substring_t args[MAX_OPT_ARGS];
2317 char *options, *o, *p;
2318 int token, ret = 0;
2319 u64 token64; 2305 u64 token64;
2320 2306
2321 options = o = kstrdup(buf, GFP_KERNEL); 2307 if (match_u64(sstr, &token64))
2322 if (!options) 2308 return -EINVAL;
2323 return -ENOMEM; 2309 *val = token64;
2324 2310
2325 while ((p = strsep(&o, ":\n")) != NULL) { 2311 return 0;
2326 if (!*p) 2312}
2327 continue;
2328 2313
2329 token = match_token(p, traddr_opt_tokens, args); 2314/*
2330 switch (token) { 2315 * This routine validates and extracts the WWN's from the TRADDR string.
2331 case FCT_TRADDR_WWNN: 2316 * As kernel parsers need the 0x to determine number base, universally
2332 if (match_u64(args, &token64)) { 2317 * build string to parse with 0x prefix before parsing name strings.
2333 ret = -EINVAL; 2318 */
2334 goto out; 2319static int
2335 } 2320nvme_fc_parse_traddr(struct nvmet_fc_traddr *traddr, char *buf, size_t blen)
2336 traddr->nn = token64; 2321{
2337 break; 2322 char name[2 + NVME_FC_TRADDR_HEXNAMELEN + 1];
2338 case FCT_TRADDR_WWPN: 2323 substring_t wwn = { name, &name[sizeof(name)-1] };
2339 if (match_u64(args, &token64)) { 2324 int nnoffset, pnoffset;
2340 ret = -EINVAL; 2325
2341 goto out; 2326 /* validate it string one of the 2 allowed formats */
2342 } 2327 if (strnlen(buf, blen) == NVME_FC_TRADDR_MAXLENGTH &&
2343 traddr->pn = token64; 2328 !strncmp(buf, "nn-0x", NVME_FC_TRADDR_OXNNLEN) &&
2344 break; 2329 !strncmp(&buf[NVME_FC_TRADDR_MAX_PN_OFFSET],
2345 default: 2330 "pn-0x", NVME_FC_TRADDR_OXNNLEN)) {
2346 pr_warn("unknown traddr token or missing value '%s'\n", 2331 nnoffset = NVME_FC_TRADDR_OXNNLEN;
2347 p); 2332 pnoffset = NVME_FC_TRADDR_MAX_PN_OFFSET +
2348 ret = -EINVAL; 2333 NVME_FC_TRADDR_OXNNLEN;
2349 goto out; 2334 } else if ((strnlen(buf, blen) == NVME_FC_TRADDR_MINLENGTH &&
2350 } 2335 !strncmp(buf, "nn-", NVME_FC_TRADDR_NNLEN) &&
2351 } 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;
2352 2354
2353out: 2355 return 0;
2354 kfree(options); 2356
2355 return ret; 2357out_einval:
2358 pr_warn("%s: bad traddr string\n", __func__);
2359 return -EINVAL;
2356} 2360}
2357 2361
2358static int 2362static int
@@ -2370,7 +2374,8 @@ nvmet_fc_add_port(struct nvmet_port *port)
2370 2374
2371 /* map the traddr address info to a target port */ 2375 /* map the traddr address info to a target port */
2372 2376
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));
2374 if (ret) 2379 if (ret)
2375 return ret; 2380 return ret;
2376 2381
diff --git a/include/linux/nvme-fc.h b/include/linux/nvme-fc.h
index 21c37e39e41a..36cca93a5ff2 100644
--- a/include/linux/nvme-fc.h
+++ b/include/linux/nvme-fc.h
@@ -334,5 +334,24 @@ struct fcnvme_ls_disconnect_acc {
334#define NVME_FC_LS_TIMEOUT_SEC 2 /* 2 seconds */ 334#define NVME_FC_LS_TIMEOUT_SEC 2 /* 2 seconds */
335#define NVME_FC_TGTOP_TIMEOUT_SEC 2 /* 2 seconds */ 335#define NVME_FC_TGTOP_TIMEOUT_SEC 2 /* 2 seconds */
336 336
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
337 356
338#endif /* _NVME_FC_H */ 357#endif /* _NVME_FC_H */