[PATCH 6/6] nvme: track shared namespaces in a siblings list

Sagi Grimberg sagi at grimberg.me
Mon Jun 19 03:12:54 PDT 2017



On 19/06/17 12:57, Christoph Hellwig wrote:
> Verify that our IDs match for shared namespaces in a subystem, and link
> them up in a headless queue so that we can find siblings easily.
> 
> Signed-off-by: Christoph Hellwig <hch at lst.de>
> ---
>   drivers/nvme/host/core.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++
>   drivers/nvme/host/nvme.h |  1 +
>   2 files changed, 100 insertions(+)
> 
> diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
> index d88cd6a4a549..8a44d68a3f98 100644
> --- a/drivers/nvme/host/core.c
> +++ b/drivers/nvme/host/core.c
> @@ -2261,6 +2261,100 @@ static struct nvme_ns *nvme_find_get_ns(struct nvme_ctrl *ctrl, unsigned nsid)
>   	return ret;
>   }
>   
> +/*
> + * Check if the two nvme_ns structures refer to the same namespace.
> + *
> + * Returns 1 if they refer to the same namespace, 0 if not, or a negative
> + * errno value on error.
> + */
> +static int nvme_ns_compare_ids(struct nvme_ns *ns, struct nvme_id_ns *id,
> +		struct nvme_ns *cur)
> +{
> +	bool is_shared = id->nmic & (1 << 0);
> +	bool have_id = false;
> +
> +	if (!uuid_is_null(&ns->uuid)) {
> +		have_id = true;
> +		if (!uuid_equal(&ns->uuid, &cur->uuid))
> +			return 0;
> +	}
> +	if (memchr_inv(ns->nguid, 0, sizeof(ns->nguid))) {
> +		have_id = true;
> +		if (memcmp(&ns->nguid, &cur->nguid, sizeof(ns->nguid)))
> +			return 0;
> +	}
> +	if (memchr_inv(ns->eui, 0, sizeof(ns->eui))) {
> +		have_id = true;
> +		if (memcmp(&ns->eui, &cur->eui, sizeof(ns->eui)))
> +			return 0;
> +	}
> +
> +	/*
> +	 * Note that the unique identifiers are only a "should" condition in
> +	 * the specification.  But we require them for multipath operations
> +	 * due to safety concerns.
> +	 */
> +	if (!have_id && is_shared) {
> +		dev_err(ns->ctrl->device,
> +			"shared namespace %u without unique ID.\n",
> +			ns->ns_id);
> +		return -EINVAL;
> +	}
> +
> +	if (have_id && !is_shared) {
> +		dev_err(ns->ctrl->device,
> +			"private namespace %u with duplicate ID.\n",
> +			ns->ns_id);
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * As of NVMe 1.3 (Section 6.1) this is only strictly required if namespace
> +	 * management is supported, but sort of implied for shared namespaces
> +	 * by omitting the defintion.  We'll need an ECN for this language
> +	 * eventually.
> +	 */
> +	if (is_shared && ns->ns_id != cur->ns_id) {
> +		dev_err(ns->ctrl->device,
> +			"non-matching nsids (%u vs %u) for shared namespaces\n",
> +			ns->ns_id, cur->ns_id);
> +		return -EINVAL;
> +	}
> +
> +	return 1;
> +}
> +
> +static int nvme_ns_find_siblings(struct nvme_ns *ns, struct nvme_id_ns *id)
> +{
> +	struct nvme_subsystem *subsys = ns->ctrl->subsys;
> +	struct nvme_ctrl *ctrl;
> +	struct nvme_ns *cur;
> +	int err = 0, ret;
> +
> +	mutex_lock(&subsys->lock);
> +	list_for_each_entry(ctrl, &subsys->ctrls, subsys_entry) {
> +		if (ctrl == ns->ctrl)
> +			continue;
> +
> +		mutex_lock(&ctrl->namespaces_mutex);
> +		list_for_each_entry(cur, &ctrl->namespaces, list) {
> +			ret = nvme_ns_compare_ids(ns, id, cur);
> +			if (ret < 0) {
> +				err = ret;
> +				break;
> +			}
> +			if (ret > 0)

			} else if (ret > 0) {
				list_add(&ns->siblings, &cur->siblings);
			}

> +				list_add(&ns->siblings, &cur->siblings);
> +		}
> +		mutex_unlock(&ctrl->namespaces_mutex);
> +
> +		if (err)
> +			break;
> +	}
> +	mutex_unlock(&subsys->lock);
> +	return err;
> +}
> +

General question, Would it make sense to invoke this search from the
block layer as part of adding a new disk (a new block_device operation)?
And should the sibling association maybe move up to struct block_device?
(we could cache nvme_id_ns in nvme_ns for that to happen)?



More information about the Linux-nvme mailing list