static int nvmf_parse_options()

in host/fabrics.c [555:870]


static int nvmf_parse_options(struct nvmf_ctrl_options *opts,
		const char *buf)
{
	substring_t args[MAX_OPT_ARGS];
	char *options, *o, *p;
	int token, ret = 0;
	size_t nqnlen  = 0;
	int ctrl_loss_tmo = NVMF_DEF_CTRL_LOSS_TMO;
	uuid_t hostid;

	/* Set defaults */
	opts->queue_size = NVMF_DEF_QUEUE_SIZE;
	opts->nr_io_queues = num_online_cpus();
	opts->reconnect_delay = NVMF_DEF_RECONNECT_DELAY;
	opts->kato = 0;
	opts->duplicate_connect = false;
	opts->fast_io_fail_tmo = NVMF_DEF_FAIL_FAST_TMO;
	opts->hdr_digest = false;
	opts->data_digest = false;
	opts->tos = -1; /* < 0 == use transport default */

	options = o = kstrdup(buf, GFP_KERNEL);
	if (!options)
		return -ENOMEM;

	uuid_gen(&hostid);

	while ((p = strsep(&o, ",\n")) != NULL) {
		if (!*p)
			continue;

		token = match_token(p, opt_tokens, args);
		opts->mask |= token;
		switch (token) {
		case NVMF_OPT_TRANSPORT:
			p = match_strdup(args);
			if (!p) {
				ret = -ENOMEM;
				goto out;
			}
			kfree(opts->transport);
			opts->transport = p;
			break;
		case NVMF_OPT_NQN:
			p = match_strdup(args);
			if (!p) {
				ret = -ENOMEM;
				goto out;
			}
			kfree(opts->subsysnqn);
			opts->subsysnqn = p;
			nqnlen = strlen(opts->subsysnqn);
			if (nqnlen >= NVMF_NQN_SIZE) {
				pr_err("%s needs to be < %d bytes\n",
					opts->subsysnqn, NVMF_NQN_SIZE);
				ret = -EINVAL;
				goto out;
			}
			opts->discovery_nqn =
				!(strcmp(opts->subsysnqn,
					 NVME_DISC_SUBSYS_NAME));
			break;
		case NVMF_OPT_TRADDR:
			p = match_strdup(args);
			if (!p) {
				ret = -ENOMEM;
				goto out;
			}
			kfree(opts->traddr);
			opts->traddr = p;
			break;
		case NVMF_OPT_TRSVCID:
			p = match_strdup(args);
			if (!p) {
				ret = -ENOMEM;
				goto out;
			}
			kfree(opts->trsvcid);
			opts->trsvcid = p;
			break;
		case NVMF_OPT_QUEUE_SIZE:
			if (match_int(args, &token)) {
				ret = -EINVAL;
				goto out;
			}
			if (token < NVMF_MIN_QUEUE_SIZE ||
			    token > NVMF_MAX_QUEUE_SIZE) {
				pr_err("Invalid queue_size %d\n", token);
				ret = -EINVAL;
				goto out;
			}
			opts->queue_size = token;
			break;
		case NVMF_OPT_NR_IO_QUEUES:
			if (match_int(args, &token)) {
				ret = -EINVAL;
				goto out;
			}
			if (token <= 0) {
				pr_err("Invalid number of IOQs %d\n", token);
				ret = -EINVAL;
				goto out;
			}
			if (opts->discovery_nqn) {
				pr_debug("Ignoring nr_io_queues value for discovery controller\n");
				break;
			}

			opts->nr_io_queues = min_t(unsigned int,
					num_online_cpus(), token);
			break;
		case NVMF_OPT_KATO:
			if (match_int(args, &token)) {
				ret = -EINVAL;
				goto out;
			}

			if (token < 0) {
				pr_err("Invalid keep_alive_tmo %d\n", token);
				ret = -EINVAL;
				goto out;
			} else if (token == 0 && !opts->discovery_nqn) {
				/* Allowed for debug */
				pr_warn("keep_alive_tmo 0 won't execute keep alives!!!\n");
			}
			opts->kato = token;
			break;
		case NVMF_OPT_CTRL_LOSS_TMO:
			if (match_int(args, &token)) {
				ret = -EINVAL;
				goto out;
			}

			if (token < 0)
				pr_warn("ctrl_loss_tmo < 0 will reconnect forever\n");
			ctrl_loss_tmo = token;
			break;
		case NVMF_OPT_FAIL_FAST_TMO:
			if (match_int(args, &token)) {
				ret = -EINVAL;
				goto out;
			}

			if (token >= 0)
				pr_warn("I/O fail on reconnect controller after %d sec\n",
					token);
			else
				token = -1;

			opts->fast_io_fail_tmo = token;
			break;
		case NVMF_OPT_HOSTNQN:
			if (opts->host) {
				pr_err("hostnqn already user-assigned: %s\n",
				       opts->host->nqn);
				ret = -EADDRINUSE;
				goto out;
			}
			p = match_strdup(args);
			if (!p) {
				ret = -ENOMEM;
				goto out;
			}
			nqnlen = strlen(p);
			if (nqnlen >= NVMF_NQN_SIZE) {
				pr_err("%s needs to be < %d bytes\n",
					p, NVMF_NQN_SIZE);
				kfree(p);
				ret = -EINVAL;
				goto out;
			}
			opts->host = nvmf_host_add(p);
			kfree(p);
			if (!opts->host) {
				ret = -ENOMEM;
				goto out;
			}
			break;
		case NVMF_OPT_RECONNECT_DELAY:
			if (match_int(args, &token)) {
				ret = -EINVAL;
				goto out;
			}
			if (token <= 0) {
				pr_err("Invalid reconnect_delay %d\n", token);
				ret = -EINVAL;
				goto out;
			}
			opts->reconnect_delay = token;
			break;
		case NVMF_OPT_HOST_TRADDR:
			p = match_strdup(args);
			if (!p) {
				ret = -ENOMEM;
				goto out;
			}
			kfree(opts->host_traddr);
			opts->host_traddr = p;
			break;
		case NVMF_OPT_HOST_IFACE:
			p = match_strdup(args);
			if (!p) {
				ret = -ENOMEM;
				goto out;
			}
			kfree(opts->host_iface);
			opts->host_iface = p;
			break;
		case NVMF_OPT_HOST_ID:
			p = match_strdup(args);
			if (!p) {
				ret = -ENOMEM;
				goto out;
			}
			ret = uuid_parse(p, &hostid);
			if (ret) {
				pr_err("Invalid hostid %s\n", p);
				ret = -EINVAL;
				kfree(p);
				goto out;
			}
			kfree(p);
			break;
		case NVMF_OPT_DUP_CONNECT:
			opts->duplicate_connect = true;
			break;
		case NVMF_OPT_DISABLE_SQFLOW:
			opts->disable_sqflow = true;
			break;
		case NVMF_OPT_HDR_DIGEST:
			opts->hdr_digest = true;
			break;
		case NVMF_OPT_DATA_DIGEST:
			opts->data_digest = true;
			break;
		case NVMF_OPT_NR_WRITE_QUEUES:
			if (match_int(args, &token)) {
				ret = -EINVAL;
				goto out;
			}
			if (token <= 0) {
				pr_err("Invalid nr_write_queues %d\n", token);
				ret = -EINVAL;
				goto out;
			}
			opts->nr_write_queues = token;
			break;
		case NVMF_OPT_NR_POLL_QUEUES:
			if (match_int(args, &token)) {
				ret = -EINVAL;
				goto out;
			}
			if (token <= 0) {
				pr_err("Invalid nr_poll_queues %d\n", token);
				ret = -EINVAL;
				goto out;
			}
			opts->nr_poll_queues = token;
			break;
		case NVMF_OPT_TOS:
			if (match_int(args, &token)) {
				ret = -EINVAL;
				goto out;
			}
			if (token < 0) {
				pr_err("Invalid type of service %d\n", token);
				ret = -EINVAL;
				goto out;
			}
			if (token > 255) {
				pr_warn("Clamping type of service to 255\n");
				token = 255;
			}
			opts->tos = token;
			break;
		case NVMF_OPT_DISCOVERY:
			opts->discovery_nqn = true;
			break;
		default:
			pr_warn("unknown parameter or missing value '%s' in ctrl creation request\n",
				p);
			ret = -EINVAL;
			goto out;
		}
	}

	if (opts->discovery_nqn) {
		opts->nr_io_queues = 0;
		opts->nr_write_queues = 0;
		opts->nr_poll_queues = 0;
		opts->duplicate_connect = true;
	} else {
		if (!opts->kato)
			opts->kato = NVME_DEFAULT_KATO;
	}
	if (ctrl_loss_tmo < 0) {
		opts->max_reconnects = -1;
	} else {
		opts->max_reconnects = DIV_ROUND_UP(ctrl_loss_tmo,
						opts->reconnect_delay);
		if (ctrl_loss_tmo < opts->fast_io_fail_tmo)
			pr_warn("failfast tmo (%d) larger than controller loss tmo (%d)\n",
				opts->fast_io_fail_tmo, ctrl_loss_tmo);
	}

	if (!opts->host) {
		kref_get(&nvmf_default_host->ref);
		opts->host = nvmf_default_host;
	}

	uuid_copy(&opts->host->id, &hostid);

out:
	kfree(options);
	return ret;
}