[dm-devel] [PATCH 1/5] libmultipath: nvme: fix path detection for kernel 4.16
Benjamin Marzinski
bmarzins at redhat.com
Fri Sep 21 22:51:18 UTC 2018
On Fri, Sep 14, 2018 at 02:50:59PM +0200, Martin Wilck wrote:
Reviewed-by: Benjamin Marzinski <bmarzins at redhat.com>
> The path detection logic of the NVMe code relies on the "slaves"
> symlinks from NVMe subsys to controllers in sysfs, which have
> been removed in the 4.16 kernel.
>
> With this patch, we use the symlinks on the NVMe subsys level
> instead.
>
> Example: a multipathed NVMe subsystem with 2 controllers:
>
> /sys/devices/virtual/nvme-subsystem/nvme-subsys2/nvme2n1
> /sys/devices/virtual/nvme-fabrics/ctl/nvme2/nvme2c6n1
> /sys/devices/virtual/nvme-fabrics/ctl/nvme3/nvme2c8n1
>
> The controllers are found from the subsystem like this:
>
> /sys/devices/virtual/nvme-subsystem/nvme-subsys2/nvme2 ->
> ../../nvme-fabrics/ctl/nvme2
> /sys/devices/virtual/nvme-subsystem/nvme-subsys2/nvme3 ->
> ../../nvme-fabrics/ctl/nvme3
>
> Signed-off-by: Martin Wilck <mwilck at suse.com>
> ---
> libmultipath/foreign/nvme.c | 153 ++++++++++++++++++++++++++++++------
> 1 file changed, 127 insertions(+), 26 deletions(-)
>
> diff --git a/libmultipath/foreign/nvme.c b/libmultipath/foreign/nvme.c
> index 280b6bd2..fca3235f 100644
> --- a/libmultipath/foreign/nvme.c
> +++ b/libmultipath/foreign/nvme.c
> @@ -26,6 +26,7 @@
> #include <limits.h>
> #include <dirent.h>
> #include <errno.h>
> +#include <ctype.h>
> #include "vector.h"
> #include "generic.h"
> #include "foreign.h"
> @@ -442,17 +443,92 @@ _find_path_by_syspath(struct nvme_map *map, const char *syspath)
> return NULL;
> }
>
> -static int no_dotfiles(const struct dirent *di)
> +static void _udev_device_unref(void *p)
> {
> - return di->d_name[0] != '.';
> + udev_device_unref(p);
> }
>
> -static void _find_slaves(struct context *ctx, struct nvme_map *map)
> +static void _udev_enumerate_unref(void *p)
> {
> - char pathbuf[PATH_MAX];
> + udev_enumerate_unref(p);
> +}
> +
> +static int _dirent_controller(const struct dirent *di)
> +{
> + static const char nvme_prefix[] = "nvme";
> + const char *p;
> +
> +#ifdef _DIRENT_HAVE_D_TYPE
> + if (di->d_type != DT_LNK)
> + return 0;
> +#endif
> + if (strncmp(di->d_name, nvme_prefix, sizeof(nvme_prefix) - 1))
> + return 0;
> + p = di->d_name + sizeof(nvme_prefix) - 1;
> + if (*p == '\0' || !isdigit(*p))
> + return 0;
> + for (++p; *p != '\0'; ++p)
> + if (!isdigit(*p))
> + return 0;
> + return 1;
> +}
> +
> +/* Find the block device for a given nvme controller */
> +struct udev_device *get_ctrl_blkdev(const struct context *ctx,
> + struct udev_device *ctrl)
> +{
> + struct udev_list_entry *item;
> + struct udev_device *blkdev = NULL;
> + struct udev_enumerate *enm = udev_enumerate_new(ctx->udev);
> +
> + if (enm == NULL)
> + return NULL;
> +
> + pthread_cleanup_push(_udev_enumerate_unref, enm);
> + if (udev_enumerate_add_match_parent(enm, ctrl) < 0)
> + goto out;
> + if (udev_enumerate_add_match_subsystem(enm, "block"))
> + goto out;
> +
> + if (udev_enumerate_scan_devices(enm) < 0) {
> + condlog(1, "%s: %s: error enumerating devices", __func__, THIS);
> + goto out;
> + }
> +
> + for (item = udev_enumerate_get_list_entry(enm);
> + item != NULL;
> + item = udev_list_entry_get_next(item)) {
> + struct udev_device *tmp;
> +
> + tmp = udev_device_new_from_syspath(ctx->udev,
> + udev_list_entry_get_name(item));
> + if (tmp == NULL)
> + continue;
> + if (!strcmp(udev_device_get_devtype(tmp), "disk")) {
> + blkdev = tmp;
> + break;
> + } else
> + udev_device_unref(tmp);
> + }
> +
> + if (blkdev == NULL)
> + condlog(1, "%s: %s: failed to get blockdev for %s",
> + __func__, THIS, udev_device_get_sysname(ctrl));
> + else
> + condlog(5, "%s: %s: got %s", __func__, THIS,
> + udev_device_get_sysname(blkdev));
> +out:
> + pthread_cleanup_pop(1);
> + return blkdev;
> +}
> +
> +static void _find_controllers(struct context *ctx, struct nvme_map *map)
> +{
> + char pathbuf[PATH_MAX], realbuf[PATH_MAX];
> struct dirent **di = NULL;
> + struct udev_device *subsys;
> struct nvme_path *path;
> - int r, i;
> + int r, i, n;
>
> if (map == NULL || map->udev == NULL)
> return;
> @@ -460,33 +536,65 @@ static void _find_slaves(struct context *ctx, struct nvme_map *map)
> vector_foreach_slot(map->pathvec, path, i)
> path->seen = false;
>
> - snprintf(pathbuf, sizeof(pathbuf),
> - "%s/slaves",
> - udev_device_get_syspath(map->udev));
> + subsys = udev_device_get_parent_with_subsystem_devtype(map->udev,
> + "nvme-subsystem",
> + NULL);
> + if (subsys == NULL) {
> + condlog(1, "%s: %s: BUG: no NVME subsys for %s", __func__, THIS,
> + udev_device_get_sysname(map->udev));
> + return;
> + }
>
> - r = scandir(pathbuf, &di, no_dotfiles, alphasort);
> + n = snprintf(pathbuf, sizeof(pathbuf), "%s",
> + udev_device_get_syspath(subsys));
> + r = scandir(pathbuf, &di, _dirent_controller, alphasort);
>
> if (r == 0) {
> - condlog(3, "%s: %s: no paths for %s", __func__, THIS,
> + condlog(3, "%s: %s: no controllers for %s", __func__, THIS,
> udev_device_get_sysname(map->udev));
> return;
> } else if (r < 0) {
> - condlog(1, "%s: %s: error %d scanning paths of %s", __func__,
> - THIS, errno, udev_device_get_sysname(map->udev));
> + condlog(1, "%s: %s: error %d scanning controllers of %s",
> + __func__, THIS, errno,
> + udev_device_get_sysname(map->udev));
> return;
> }
>
> pthread_cleanup_push(free, di);
> for (i = 0; i < r; i++) {
> char *fn = di[i]->d_name;
> - struct udev_device *udev;
> + struct udev_device *ctrl, *udev;
> +
> + if (snprintf(pathbuf + n, sizeof(pathbuf) - n, "/%s", fn)
> + >= sizeof(pathbuf) - n)
> + continue;
> + if (realpath(pathbuf, realbuf) == NULL) {
> + condlog(3, "%s: %s: realpath: %s", __func__, THIS,
> + strerror(errno));
> + continue;
> + }
> + condlog(4, "%s: %s: found %s", __func__, THIS, realbuf);
>
> - if (snprintf(pathbuf, sizeof(pathbuf), "%s/slaves/%s",
> - udev_device_get_syspath(map->udev), fn)
> - >= sizeof(pathbuf))
> + ctrl = udev_device_new_from_syspath(ctx->udev, realbuf);
> + if (ctrl == NULL) {
> + condlog(1, "%s: %s: failed to get udev device for %s",
> + __func__, THIS, realbuf);
> + continue;
> + }
> +
> + pthread_cleanup_push(_udev_device_unref, ctrl);
> + udev = get_ctrl_blkdev(ctx, ctrl);
> + /*
> + * We give up the reference to the nvme device here and get
> + * it back from the child below.
> + * This way we don't need to worry about unreffing it.
> + */
> + pthread_cleanup_pop(1);
> +
> + if (udev == NULL)
> continue;
>
> - path = _find_path_by_syspath(map, pathbuf);
> + path = _find_path_by_syspath(map, udev_device_get_syspath(udev));
> if (path != NULL) {
> path->seen = true;
> condlog(4, "%s: %s already known",
> @@ -494,13 +602,6 @@ static void _find_slaves(struct context *ctx, struct nvme_map *map)
> continue;
> }
>
> - udev = udev_device_new_from_syspath(ctx->udev, pathbuf);
> - if (udev == NULL) {
> - condlog(1, "%s: %s: failed to get udev device for %s",
> - __func__, THIS, fn);
> - continue;
> - }
> -
> path = calloc(1, sizeof(*path));
> if (path == NULL)
> continue;
> @@ -591,7 +692,7 @@ static int _add_map(struct context *ctx, struct udev_device *ud,
> return FOREIGN_ERR;
> }
> vector_set_slot(ctx->mpvec, map);
> - _find_slaves(ctx, map);
> + _find_controllers(ctx, map);
>
> return FOREIGN_CLAIMED;
> }
> @@ -688,7 +789,7 @@ void _check(struct context *ctx)
> vector_foreach_slot(ctx->mpvec, gm, i) {
> struct nvme_map *map = gen_mp_to_nvme(gm);
>
> - _find_slaves(ctx, map);
> + _find_controllers(ctx, map);
> }
> }
>
> --
> 2.18.0
More information about the dm-devel
mailing list