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 <bmarz...@redhat.com>

> Signed-off-by: Martin Wilck <mwi...@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.varo...@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

--
dm-devel mailing list
dm-devel@redhat.com
https://www.redhat.com/mailman/listinfo/dm-devel

Reply via email to