NVMe Linux驱动系列一:host端[fabrics.c]<68>

nvmf_parse_options


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;
	char hostnqn[NVMF_NQN_SIZE];

	/* 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;

	/* use default host if not given by user space */
	uuid_copy(&hostid, &nvmf_default_host->id);
	strscpy(hostnqn, nvmf_default_host->nqn, NVMF_NQN_SIZE);

	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;
			}
			strscpy(hostnqn, p, NVMF_NQN_SIZE);
			kfree(p);
			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;
		case NVMF_OPT_DHCHAP_SECRET:
			p = match_strdup(args);
			if (!p) {
				ret = -ENOMEM;
				goto out;
			}
			if (strlen(p) < 11 || strncmp(p, "DHHC-1:", 7)) {
				pr_err("Invalid DH-CHAP secret %s\n", p);
				ret = -EINVAL;
				goto out;
			}
			kfree(opts->dhchap_secret);
			opts->dhchap_secret = p;
			break;
		case NVMF_OPT_DHCHAP_CTRL_SECRET:
			p = match_strdup(args);
			if (!p) {
				ret = -ENOMEM;
				goto out;
			}
			if (strlen(p) < 11 || strncmp(p, "DHHC-1:", 7)) {
				pr_err("Invalid DH-CHAP secret %s\n", p);
				ret = -EINVAL;
				goto out;
			}
			kfree(opts->dhchap_ctrl_secret);
			opts->dhchap_ctrl_secret = p;
			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);
	}

	opts->host = nvmf_host_add(hostnqn, &hostid);
	if (IS_ERR(opts->host)) {
		ret = PTR_ERR(opts->host);
		opts->host = NULL;
		goto out;
	}

out:
	kfree(options);
	return ret;
}

这段代码定义了一个函数 nvmf_parse_options,用于解析 NVMe 控制器选项字符串,并将解析后的选项值存储在一个 nvmf_ctrl_options 结构体中。下面是这个函数的主要步骤:

  1. 首先,为了保护原始选项字符串,它对输入的 buf 进行了一次内存拷贝,生成了一个名为 options 的字符串。

  2. 然后,函数开始解析选项字符串。它使用 strsep 函数将字符串按照逗号 , 或换行符 \n 分割成各个子字符串,并遍历这些子字符串。

  3. 对于每个子字符串,函数使用 match_token 函数将其与 opt_tokens 匹配表中的标识符进行匹配,得到对应的标识符。然后,根据标识符进行相应的处理。

  4. 对于每个标识符,函数执行相应的操作。例如,对于 NVMF_OPT_TRANSPORT,它将选项的值(字符串)分配给 opts->transport 字段。

  5. 函数在解析选项的过程中,根据情况更新了 nvmf_ctrl_options 结构体的各个字段。

  6. 解析完成后,函数根据解析结果,对一些字段进行了一些额外的处理。例如,根据是否启用了发现控制器,调整了一些字段的值。

  7. 最后,函数通过调用 nvmf_host_add 函数,根据主机名和主机 ID 添加了一个主机,这是 nvmf_ctrl_options 结构体中的一个字段。

  8. 函数释放了之前为保护选项字符串而创建的 options 字符串,并返回解析过程中的结果。

总体来说,这个函数的作用是将 NVMe 控制器选项字符串解析为一个结构体,以供后续在初始化 NVMe 控制器时使用。

nvmf_set_io_queues


void nvmf_set_io_queues(struct nvmf_ctrl_options *opts, u32 nr_io_queues,
			u32 io_queues[HCTX_MAX_TYPES])
{
	if (opts->nr_write_queues && opts->nr_io_queues < nr_io_queues) {
		/*
		 * separate read/write queues
		 * hand out dedicated default queues only after we have
		 * sufficient read queues.
		 */
		io_queues[HCTX_TYPE_READ] = opts->nr_io_queues;
		nr_io_queues -= io_queues[HCTX_TYPE_READ];
		io_queues[HCTX_TYPE_DEFAULT] =
			min(opts->nr_write_queues, nr_io_queues);
		nr_io_queues -= io_queues[HCTX_TYPE_DEFAULT];
	} else {
		/*
		 * shared read/write queues
		 * either no write queues were requested, or we don't have
		 * sufficient queue count to have dedicated default queues.
		 */
		io_queues[HCTX_TYPE_DEFAULT] =
			min(opts->nr_io_queues, nr_io_queues);
		nr_io_queues -= io_queues[HCTX_TYPE_DEFAULT];
	}

	if (opts->nr_poll_queues && nr_io_queues) {
		/* map dedicated poll queues only if we have queues left */
		io_queues[HCTX_TYPE_POLL] =
			min(opts->nr_poll_queues, nr_io_queues);
	}
}
EXPORT_SYMBOL_GPL(nvmf_set_io_queues);

这段代码定义了一个函数 nvmf_set_io_queues,用于根据 NVMe 控制器选项设置 I/O 队列的分配方式。该函数会更新传入的 io_queues 数组,指定每种类型的 I/O 队列数量。

函数的主要思路如下:

  1. 函数根据传入的 nr_io_queuesio_queues 数组来设置不同类型的 I/O 队列数量。

  2. 首先,函数会检查是否有写队列(opts->nr_write_queues)且分配的总队列数 opts->nr_io_queues 小于传入的 nr_io_queues

  3. 如果有写队列,函数会将读队列(HCTX_TYPE_READ)的数量设为 opts->nr_io_queues,然后将剩余队列分配给默认队列(HCTX_TYPE_DEFAULT)。

  4. 如果没有写队列,或者写队列数量超过剩余队列数,函数会将默认队列(HCTX_TYPE_DEFAULT)的数量设为 opts->nr_io_queues

  5. 接下来,函数会检查是否有轮询队列(opts->nr_poll_queues)且剩余队列数不为零。

  6. 如果有轮询队列且剩余队列数大于零,函数会将轮询队列(HCTX_TYPE_POLL)的数量设为 opts->nr_poll_queues

  7. 最终,io_queues 数组中的各个元素会被更新为相应的队列数量。

总的来说,该函数的目的是根据 NVMe 控制器选项以及可用的队列数,设置不同类型的 I/O 队列数量,以便在初始化 NVMe 控制器时进行合适的队列分配。

nvmf_map_queues


void nvmf_map_queues(struct blk_mq_tag_set *set, struct nvme_ctrl *ctrl,
		     u32 io_queues[HCTX_MAX_TYPES])
{
	struct nvmf_ctrl_options *opts = ctrl->opts;

	if (opts->nr_write_queues && io_queues[HCTX_TYPE_READ]) {
		/* separate read/write queues */
		set->map[HCTX_TYPE_DEFAULT].nr_queues =
			io_queues[HCTX_TYPE_DEFAULT];
		set->map[HCTX_TYPE_DEFAULT].queue_offset = 0;
		set->map[HCTX_TYPE_READ].nr_queues =
			io_queues[HCTX_TYPE_READ];
		set->map[HCTX_TYPE_READ].queue_offset =
			io_queues[HCTX_TYPE_DEFAULT];
	} else {
		/* shared read/write queues */
		set->map[HCTX_TYPE_DEFAULT].nr_queues =
			io_queues[HCTX_TYPE_DEFAULT];
		set->map[HCTX_TYPE_DEFAULT].queue_offset = 0;
		set->map[HCTX_TYPE_READ].nr_queues =
			io_queues[HCTX_TYPE_DEFAULT];
		set->map[HCTX_TYPE_READ].queue_offset = 0;
	}

	blk_mq_map_queues(&set->map[HCTX_TYPE_DEFAULT]);
	blk_mq_map_queues(&set->map[HCTX_TYPE_READ]);
	if (opts->nr_poll_queues && io_queues[HCTX_TYPE_POLL]) {
		/* map dedicated poll queues only if we have queues left */
		set->map[HCTX_TYPE_POLL].nr_queues = io_queues[HCTX_TYPE_POLL];
		set->map[HCTX_TYPE_POLL].queue_offset =
			io_queues[HCTX_TYPE_DEFAULT] +
			io_queues[HCTX_TYPE_READ];
		blk_mq_map_queues(&set->map[HCTX_TYPE_POLL]);
	}

	dev_info(ctrl->device,
		"mapped %d/%d/%d default/read/poll queues.\n",
		io_queues[HCTX_TYPE_DEFAULT],
		io_queues[HCTX_TYPE_READ],
		io_queues[HCTX_TYPE_POLL]);
}
EXPORT_SYMBOL_GPL(nvmf_map_queues);

这段代码定义了一个函数 nvmf_map_queues,用于在块多队列(blk-mq)中映射 NVMe 控制器的不同类型的 I/O 队列。这个函数将根据传入的 NVMe 控制器选项和队列信息来配置 blk-mq 的队列映射。

函数的主要流程如下:

  1. 首先,函数根据 NVMe 控制器选项和传入的 io_queues 数组,判断是否需要分离读写队列。

  2. 如果存在写队列且读队列数量不为零,那么将读和写队列分开。

  3. 设置默认队列(HCTX_TYPE_DEFAULT)的映射信息,包括队列数量和队列偏移。

  4. 设置读队列(HCTX_TYPE_READ)的映射信息,包括队列数量和队列偏移。

  5. 如果没有分离读写队列,或者分离后写队列数量为零,那么将读队列和默认队列合并。

  6. 调用 blk_mq_map_queues 函数来实际完成队列映射。

  7. 如果存在轮询队列且轮询队列数量不为零,设置轮询队列(HCTX_TYPE_POLL)的映射信息,包括队列数量和队列偏移。

  8. 最后,打印日志信息,指示映射了多少个默认、读和轮询队列。

总的来说,这个函数负责根据 NVMe 控制器选项和队列信息,在块多队列(blk-mq)中设置适当的队列映射,以便在 NVMe 控制器初始化过程中能够正确地使用这些队列。

nvmf_check_required_opts


static int nvmf_check_required_opts(struct nvmf_ctrl_options *opts,
		unsigned int required_opts)
{
	if ((opts->mask & required_opts) != required_opts) {
		unsigned int i;

		for (i = 0; i < ARRAY_SIZE(opt_tokens); i++) {
			if ((opt_tokens[i].token & required_opts) &&
			    !(opt_tokens[i].token & opts->mask)) {
				pr_warn("missing parameter '%s'\n",
					opt_tokens[i].pattern);
			}
		}

		return -EINVAL;
	}

	return 0;
}

这段代码定义了一个函数 nvmf_check_required_opts,用于检查是否存在所需的 NVMe 控制器选项。函数接收一个包含选项掩码和所需选项掩码的参数,然后检查给定的选项掩码中是否包含了所有所需的选项掩码。

函数的主要步骤如下:

  1. 首先,函数检查给定的选项掩码中是否包含了所有所需选项掩码。如果包含了所有所需选项,那么函数返回 0,表示选项是完整的。

  2. 如果给定的选项掩码缺少某个所需选项,那么函数会遍历预定义的 opt_tokens 数组,该数组包含所有可识别的选项信息。对于每个所需选项,函数会检查是否存在对应的选项掩码,并且该选项掩码是否缺失在给定的选项掩码中。

  3. 如果发现某个所需选项缺失,函数会打印警告消息,指示缺失的参数。

  4. 最后,函数返回 -EINVAL,表示选项不完整且存在缺失的参数。

总的来说,这个函数用于验证是否存在所有所需的 NVMe 控制器选项,并在发现缺失的参数时打印警告消息。这可以帮助确保在使用 NVMe Fabrics 驱动程序时,所有必要的选项都已正确设置。

nvmf_ip_options_match


bool nvmf_ip_options_match(struct nvme_ctrl *ctrl,
		struct nvmf_ctrl_options *opts)
{
	if (!nvmf_ctlr_matches_baseopts(ctrl, opts) ||
	    strcmp(opts->traddr, ctrl->opts->traddr) ||
	    strcmp(opts->trsvcid, ctrl->opts->trsvcid))
		return false;

	/*
	 * Checking the local address or host interfaces is rough.
	 *
	 * In most cases, none is specified and the host port or
	 * host interface is selected by the stack.
	 *
	 * Assume no match if:
	 * -  local address or host interface is specified and address
	 *    or host interface is not the same
	 * -  local address or host interface is not specified but
	 *    remote is, or vice versa (admin using specific
	 *    host_traddr/host_iface when it matters).
	 */
	if ((opts->mask & NVMF_OPT_HOST_TRADDR) &&
	    (ctrl->opts->mask & NVMF_OPT_HOST_TRADDR)) {
		if (strcmp(opts->host_traddr, ctrl->opts->host_traddr))
			return false;
	} else if ((opts->mask & NVMF_OPT_HOST_TRADDR) ||
		   (ctrl->opts->mask & NVMF_OPT_HOST_TRADDR)) {
		return false;
	}

	if ((opts->mask & NVMF_OPT_HOST_IFACE) &&
	    (ctrl->opts->mask & NVMF_OPT_HOST_IFACE)) {
		if (strcmp(opts->host_iface, ctrl->opts->host_iface))
			return false;
	} else if ((opts->mask & NVMF_OPT_HOST_IFACE) ||
		   (ctrl->opts->mask & NVMF_OPT_HOST_IFACE)) {
		return false;
	}

	return true;
}
EXPORT_SYMBOL_GPL(nvmf_ip_options_match);

这段代码定义了一个名为 nvmf_ip_options_match 的函数,用于比较给定的 NVMe 控制器选项和控制器实例的选项是否匹配。这可以用来确定是否可以建立连接。以下是函数的工作方式:

  1. 首先,通过调用 nvmf_ctlr_matches_baseopts(ctrl, opts) 函数检查控制器选项和实例选项的基本选项是否匹配,如果不匹配,则返回 false

  2. 接下来,比较 traddr(传输地址)和 trsvcid(传输服务 ID)是否匹配。如果它们不匹配,则返回 false

  3. 针对本地地址或主机接口的匹配情况,做如下判断:

    • 如果给定的选项中同时包含 NVMF_OPT_HOST_TRADDR 并且实例选项中也包含 NVMF_OPT_HOST_TRADDR,则比较 host_traddr 是否相同,如果不相同则返回 false

    • 如果只有一个选项中包含 NVMF_OPT_HOST_TRADDR,则返回 false

    • 如果给定的选项中同时包含 NVMF_OPT_HOST_IFACE 并且实例选项中也包含 NVMF_OPT_HOST_IFACE,则比较 host_iface 是否相同,如果不相同则返回 false

    • 如果只有一个选项中包含 NVMF_OPT_HOST_IFACE,则返回 false

  4. 如果上述所有检查都通过,则返回 true,表示选项匹配。

此外,该函数使用 EXPORT_SYMBOL_GPL 宏将 nvmf_ip_options_match 函数导出,以便其他模块可以使用该函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值