[dm-devel] [PATCH v2 17/23] libmultipath/foreign: nvme foreign library
Benjamin Marzinski
bmarzins at redhat.com
Wed Mar 7 19:09:23 UTC 2018
On Tue, Mar 06, 2018 at 12:15:01AM +0100, Martin Wilck wrote:
> This still contains stubs for path handling and checking, but it's functional
> for printing already.
>
Reviewed-by: Benjamin Marzinski <bmarzins at redhat.com>
> Signed-off-by: Martin Wilck <mwilck at suse.com>
> ---
> Makefile | 1 +
> libmultipath/foreign/Makefile | 30 +++
> libmultipath/foreign/nvme.c | 455 ++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 486 insertions(+)
> create mode 100644 libmultipath/foreign/Makefile
> create mode 100644 libmultipath/foreign/nvme.c
>
> diff --git a/Makefile b/Makefile
> index 11c46eb4dbc9..4b145c593605 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -7,6 +7,7 @@ BUILDDIRS = \
> libmultipath \
> libmultipath/prioritizers \
> libmultipath/checkers \
> + libmultipath/foreign \
> libmpathpersist \
> multipath \
> multipathd \
> diff --git a/libmultipath/foreign/Makefile b/libmultipath/foreign/Makefile
> new file mode 100644
> index 000000000000..dfba11e86d76
> --- /dev/null
> +++ b/libmultipath/foreign/Makefile
> @@ -0,0 +1,30 @@
> +#
> +# Copyright (C) 2003 Christophe Varoqui, <christophe.varoqui at opensvc.com>
> +#
> +include ../../Makefile.inc
> +
> +CFLAGS += $(LIB_CFLAGS) -I..
> +
> +# If you add or remove a checker also update multipath/multipath.conf.5
> +LIBS= \
> + libforeign-nvme.so
> +
> +all: $(LIBS)
> +
> +libforeign-%.so: %.o
> + $(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^
> +
> +install:
> + $(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(libdir)
> +
> +uninstall:
> + for file in $(LIBS); do $(RM) $(DESTDIR)$(libdir)/$$file; done
> +
> +clean: dep_clean
> + $(RM) core *.a *.o *.gz *.so
> +
> +OBJS := $(LIBS:libforeign-%.so=%.o)
> +include $(wildcard $(OBJS:.o=.d))
> +
> +dep_clean:
> + $(RM) $(OBJS:.o=.d)
> diff --git a/libmultipath/foreign/nvme.c b/libmultipath/foreign/nvme.c
> new file mode 100644
> index 000000000000..32bd5c96c44a
> --- /dev/null
> +++ b/libmultipath/foreign/nvme.c
> @@ -0,0 +1,455 @@
> +/*
> + Copyright (c) 2018 Martin Wilck, SUSE Linux GmbH
> +
> + This program is free software; you can redistribute it and/or
> + modify it under the terms of the GNU General Public License
> + as published by the Free Software Foundation; either version 2
> + of the License, or (at your option) any later version.
> +
> + This program is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + GNU General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program; if not, write to the Free Software
> + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
> + USA.
> +*/
> +
> +#include <sys/sysmacros.h>
> +#include <libudev.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <stdbool.h>
> +#include <libudev.h>
> +#include <pthread.h>
> +#include "vector.h"
> +#include "generic.h"
> +#include "foreign.h"
> +#include "debug.h"
> +
> +const char *THIS;
> +
> +struct nvme_map {
> + struct gen_multipath gen;
> + struct udev_device *udev;
> + struct udev_device *subsys;
> + dev_t devt;
> +};
> +
> +#define NAME_LEN 64 /* buffer length temp model name */
> +#define const_gen_mp_to_nvme(g) ((const struct nvme_map*)(g))
> +#define gen_mp_to_nvme(g) ((struct nvme_map*)(g))
> +#define nvme_mp_to_gen(n) &((n)->gen)
> +
> +static void cleanup_nvme_map(struct nvme_map *map)
> +{
> + if (map->udev)
> + udev_device_unref(map->udev);
> + if (map->subsys)
> + udev_device_unref(map->subsys);
> + free(map);
> +}
> +
> +static const struct _vector*
> +nvme_mp_get_pgs(const struct gen_multipath *gmp) {
> + return NULL;
> +}
> +
> +static void
> +nvme_mp_rel_pgs(const struct gen_multipath *gmp, const struct _vector *v)
> +{
> +}
> +
> +static void rstrip(char *str)
> +{
> + int n;
> +
> + for (n = strlen(str) - 1; n >= 0 && str[n] == ' '; n--);
> + str[n+1] = '\0';
> +}
> +
> +static int snprint_nvme_map(const struct gen_multipath *gmp,
> + char *buff, int len, char wildcard)
> +{
> + const struct nvme_map *nvm = const_gen_mp_to_nvme(gmp);
> + static const char nvme_vendor[] = "NVMe";
> + char fld[NAME_LEN];
> + const char *val;
> +
> + switch (wildcard) {
> + case 'd':
> + return snprintf(buff, len, "%s",
> + udev_device_get_sysname(nvm->udev));
> + case 'n':
> + return snprintf(buff, len, "%s:NQN:%s",
> + udev_device_get_sysname(nvm->subsys),
> + udev_device_get_sysattr_value(nvm->subsys,
> + "subsysnqn"));
> + case 'w':
> + return snprintf(buff, len, "%s",
> + udev_device_get_sysattr_value(nvm->udev,
> + "wwid"));
> + case 'S':
> + return snprintf(buff, len, "%s",
> + udev_device_get_sysattr_value(nvm->udev,
> + "size"));
> + case 'v':
> + return snprintf(buff, len, "%s", nvme_vendor);
> + case 's':
> + case 'p':
> + snprintf(fld, sizeof(fld), "%s",
> + udev_device_get_sysattr_value(nvm->subsys,
> + "model"));
> + rstrip(fld);
> + if (wildcard == 'p')
> + return snprintf(buff, len, "%s", fld);
> + return snprintf(buff, len, "%s,%s,%s", nvme_vendor, fld,
> + udev_device_get_sysattr_value(nvm->subsys,
> + "firmware_rev"));
> + case 'e':
> + return snprintf(buff, len, "%s",
> + udev_device_get_sysattr_value(nvm->subsys,
> + "firmware_rev"));
> + case 'r':
> + val = udev_device_get_sysattr_value(nvm->udev, "ro");
> + if (val[0] == 1)
> + return snprintf(buff, len, "%s", "ro");
> + else
> + return snprintf(buff, len, "%s", "rw");
> + case 'G':
> + return snprintf(buff, len, "%s", THIS);
> + default:
> + return snprintf(buff, len, "N/A");
> + break;
> + }
> + return 0;
> +}
> +
> +static const struct _vector*
> +nvme_pg_get_paths(const struct gen_pathgroup *gpg) {
> + return NULL;
> +}
> +
> +static void
> +nvme_pg_rel_paths(const struct gen_pathgroup *gpg, const struct _vector *v)
> +{
> +}
> +
> +static int snprint_nvme_pg(const struct gen_pathgroup *gmp,
> + char *buff, int len, char wildcard)
> +{
> + return 0;
> +}
> +
> +static int snprint_nvme_path(const struct gen_path *gmp,
> + char *buff, int len, char wildcard)
> +{
> + switch (wildcard) {
> + case 'R':
> + return snprintf(buff, len, "[foreign: %s]", THIS);
> + default:
> + break;
> + }
> + return 0;
> +}
> +
> +static const struct gen_multipath_ops nvme_map_ops = {
> + .get_pathgroups = nvme_mp_get_pgs,
> + .rel_pathgroups = nvme_mp_rel_pgs,
> + .style = generic_style,
> + .snprint = snprint_nvme_map,
> +};
> +
> +static const struct gen_pathgroup_ops nvme_pg_ops __attribute__((unused)) = {
> + .get_paths = nvme_pg_get_paths,
> + .rel_paths = nvme_pg_rel_paths,
> + .snprint = snprint_nvme_pg,
> +};
> +
> +static const struct gen_path_ops nvme_path_ops __attribute__((unused)) = {
> + .snprint = snprint_nvme_path,
> +};
> +
> +struct context {
> + pthread_mutex_t mutex;
> + vector mpvec;
> +};
> +
> +void lock(struct context *ctx)
> +{
> + pthread_mutex_lock(&ctx->mutex);
> +}
> +
> +void unlock(void *arg)
> +{
> + struct context *ctx = arg;
> +
> + pthread_mutex_unlock(&ctx->mutex);
> +}
> +
> +static int _delete_all(struct context *ctx)
> +{
> + struct nvme_map *nm;
> + int n = VECTOR_SIZE(ctx->mpvec), i;
> +
> + if (n == 0)
> + return FOREIGN_IGNORED;
> +
> + vector_foreach_slot_backwards(ctx->mpvec, nm, i) {
> + vector_del_slot(ctx->mpvec, i);
> + cleanup_nvme_map(nm);
> + }
> + return FOREIGN_OK;
> +}
> +
> +int delete_all(struct context *ctx)
> +{
> + int rc;
> +
> + condlog(5, "%s called for \"%s\"", __func__, THIS);
> +
> + lock(ctx);
> + pthread_cleanup_push(unlock, ctx);
> + rc = _delete_all(ctx);
> + pthread_cleanup_pop(1);
> +
> + return rc;
> +}
> +
> +void cleanup(struct context *ctx)
> +{
> + (void)delete_all(ctx);
> +
> + lock(ctx);
> + /*
> + * Locking is not strictly necessary here, locking in foreign.c
> + * makes sure that no other code is called with this ctx any more.
> + * But this should make static checkers feel better.
> + */
> + pthread_cleanup_push(unlock, ctx);
> + if (ctx->udev)
> + udev_unref(ctx->udev);
> + if (ctx->mpvec)
> + vector_free(ctx->mpvec);
> + ctx->mpvec = NULL;
> + ctx->udev = NULL;
> + pthread_cleanup_pop(1);
> + pthread_mutex_destroy(&ctx->mutex);
> +
> + free(ctx);
> +}
> +
> +struct context *init(unsigned int api, const char *name)
> +{
> + struct context *ctx;
> +
> + if (api > LIBMP_FOREIGN_API) {
> + condlog(0, "%s: api version mismatch: %08x > %08x\n",
> + __func__, api, LIBMP_FOREIGN_API);
> + return NULL;
> + }
> +
> + if ((ctx = calloc(1, sizeof(*ctx)))== NULL)
> + return NULL;
> +
> + pthread_mutex_init(&ctx->mutex, NULL);
> +
> + ctx->mpvec = vector_alloc();
> + if (ctx->mpvec == NULL)
> + goto err;
> +
> + THIS = name;
> + return ctx;
> +err:
> + cleanup(ctx);
> + return NULL;
> +}
> +
> +static struct nvme_map *_find_nvme_map_by_devt(const struct context *ctx,
> + dev_t devt)
> +{
> + struct nvme_map *nm;
> + int i;
> +
> + if (ctx->mpvec == NULL)
> + return NULL;
> +
> + vector_foreach_slot(ctx->mpvec, nm, i) {
> + if (nm->devt == devt)
> + return nm;
> + }
> +
> + return NULL;
> +}
> +
> +static int _add_map(struct context *ctx, struct udev_device *ud,
> + struct udev_device *subsys)
> +{
> + dev_t devt = udev_device_get_devnum(ud);
> + struct nvme_map *map;
> +
> + if (_find_nvme_map_by_devt(ctx, devt) != NULL)
> + return FOREIGN_OK;
> +
> + map = calloc(1, sizeof(*map));
> + if (map == NULL)
> + return FOREIGN_ERR;
> +
> + map->devt = devt;
> + map->udev = udev_device_ref(ud);
> + /*
> + * subsys is implicitly referenced by map->udev,
> + * no need to take a reference here.
> + */
> + map->subsys = subsys;
> + map->gen.ops = &nvme_map_ops;
> +
> + if (vector_alloc_slot(ctx->mpvec) == NULL) {
> + cleanup_nvme_map(map);
> + return FOREIGN_ERR;
> + }
> +
> + vector_set_slot(ctx->mpvec, map);
> +
> + return FOREIGN_CLAIMED;
> +}
> +
> +int add(struct context *ctx, struct udev_device *ud)
> +{
> + struct udev_device *subsys;
> + int rc;
> +
> + condlog(5, "%s called for \"%s\"", __func__, THIS);
> +
> + if (ud == NULL)
> + return FOREIGN_ERR;
> + if (strcmp("disk", udev_device_get_devtype(ud)))
> + return FOREIGN_IGNORED;
> +
> + subsys = udev_device_get_parent_with_subsystem_devtype(ud,
> + "nvme-subsystem",
> + NULL);
> + if (subsys == NULL)
> + return FOREIGN_IGNORED;
> +
> + lock(ctx);
> + pthread_cleanup_push(unlock, ctx);
> + rc = _add_map(ctx, ud, subsys);
> + pthread_cleanup_pop(1);
> +
> + if (rc == FOREIGN_CLAIMED)
> + condlog(3, "%s: %s: added map %s", __func__, THIS,
> + udev_device_get_sysname(ud));
> + else if (rc != FOREIGN_OK)
> + condlog(1, "%s: %s: retcode %d adding %s",
> + __func__, THIS, rc, udev_device_get_sysname(ud));
> +
> + return rc;
> +}
> +
> +int change(struct context *ctx, struct udev_device *ud)
> +{
> + condlog(5, "%s called for \"%s\"", __func__, THIS);
> + return FOREIGN_IGNORED;
> +}
> +
> +static int _delete_map(struct context *ctx, struct udev_device *ud)
> +{
> + int k;
> + struct nvme_map *map;
> + dev_t devt = udev_device_get_devnum(ud);
> +
> + map = _find_nvme_map_by_devt(ctx, devt);
> + if (map ==NULL)
> + return FOREIGN_IGNORED;
> +
> + k = find_slot(ctx->mpvec, map);
> + if (k == -1)
> + return FOREIGN_ERR;
> + else
> + vector_del_slot(ctx->mpvec, k);
> +
> + cleanup_nvme_map(map);
> +
> + return FOREIGN_OK;
> +}
> +
> +int delete(struct context *ctx, struct udev_device *ud)
> +{
> + int rc;
> +
> + condlog(5, "%s called for \"%s\"", __func__, THIS);
> +
> + if (ud == NULL)
> + return FOREIGN_ERR;
> +
> + lock(ctx);
> + pthread_cleanup_push(unlock, ctx);
> + rc = _delete_map(ctx, ud);
> + pthread_cleanup_pop(1);
> +
> + if (rc == FOREIGN_OK)
> + condlog(3, "%s: %s: map %s deleted", __func__, THIS,
> + udev_device_get_sysname(ud));
> + else if (rc != FOREIGN_IGNORED)
> + condlog(1, "%s: %s: retcode %d deleting map %s", __func__,
> + THIS, rc, udev_device_get_sysname(ud));
> +
> + return rc;
> +}
> +
> +void check(struct context *ctx)
> +{
> + condlog(5, "%s called for \"%s\"", __func__, THIS);
> + return;
> +}
> +
> +/*
> + * It's safe to pass our internal pointer, this is only used under the lock.
> + */
> +const struct _vector *get_multipaths(const struct context *ctx)
> +{
> + condlog(5, "%s called for \"%s\"", __func__, THIS);
> + return ctx->mpvec;
> +}
> +
> +void release_multipaths(const struct context *ctx, const struct _vector *mpvec)
> +{
> + condlog(5, "%s called for \"%s\"", __func__, THIS);
> + /* NOP */
> +}
> +
> +/*
> + * It's safe to pass our internal pointer, this is only used under the lock.
> + */
> +const struct _vector * get_paths(const struct context *ctx)
> +{
> + condlog(5, "%s called for \"%s\"", __func__, THIS);
> + return NULL;
> +}
> +
> +void release_paths(const struct context *ctx, const struct _vector *mpvec)
> +{
> + condlog(5, "%s called for \"%s\"", __func__, THIS);
> + /* NOP */
> +}
> +
> +/* compile-time check whether all methods are present and correctly typed */
> +#define _METHOD_INIT(x) .x = x
> +static struct foreign __methods __attribute__((unused)) = {
> + _METHOD_INIT(init),
> + _METHOD_INIT(cleanup),
> + _METHOD_INIT(change),
> + _METHOD_INIT(delete),
> + _METHOD_INIT(delete_all),
> + _METHOD_INIT(check),
> + _METHOD_INIT(lock),
> + _METHOD_INIT(unlock),
> + _METHOD_INIT(get_multipaths),
> + _METHOD_INIT(release_multipaths),
> + _METHOD_INIT(get_paths),
> + _METHOD_INIT(release_paths),
> +};
> --
> 2.16.1
More information about the dm-devel
mailing list