Linux Device Mapper development
 help / color / mirror / Atom feed
From: "Benjamin Marzinski" <bmarzins@redhat.com>
To: Martin Wilck <mwilck@suse.com>
Cc: dm-devel@redhat.com
Subject: Re: [RFC PATCH 17/20] libmultipath/foreign: nvme foreign library
Date: Wed, 28 Feb 2018 21:14:20 -0600	[thread overview]
Message-ID: <20180301031420.GM14513@octiron.msp.redhat.com> (raw)
In-Reply-To: <20180220132658.22295-18-mwilck@suse.com>

On Tue, Feb 20, 2018 at 02:26:55PM +0100, Martin Wilck wrote:
> This still contains stubs for path handling and checking, but it's functional
> for printing already.
> 
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  Makefile                      |   1 +
>  libmultipath/foreign/Makefile |  30 +++
>  libmultipath/foreign/nvme.c   | 444 ++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 475 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@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..4e9c3a52d03c
> --- /dev/null
> +++ b/libmultipath/foreign/nvme.c
> @@ -0,0 +1,444 @@
> +/*
> +  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);

According to the libudev reference manual, udev devices returned by
udev_device_get_parent_with_subsystem_devtype are attached to their
child and free along with them, so this unref shouldn't be necessary

> +	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)
> +{
> +	if (ctx == NULL)
> +		return;
> +
> +	(void)delete_all(ctx);
> +
> +	lock(ctx);
> +	pthread_cleanup_push(unlock, ctx);
> +	vector_free(ctx->mpvec);
> +	pthread_cleanup_pop(1);
> +	pthread_mutex_destroy(&ctx->mutex);
> +
> +	free(ctx);
> +}

Would you mind either removing the locking, or adding a comment
explaining that it's not necessary here.  If you really did need to lock
ctx in order guard against someone accessing ctx->mpvec in cleanup(),
then not setting it to NULL after its freed, and immediately freeing ctx
afterwards would clearly be broken, so seeing the locking makes it look
like this function is wrong.

> +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);
> +	map->subsys = udev_device_ref(subsys);

You shouldn't need to get a reference here, since map->udev will keep
this device around.

-Ben

> +	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

  reply	other threads:[~2018-03-01  3:14 UTC|newest]

Thread overview: 63+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-02-20 13:26 [RFC PATCH 00/20] "Foreign" NVMe support for multipath-tools Martin Wilck
2018-02-20 13:26 ` [RFC PATCH 01/20] multipath(d)/Makefile: add explicit dependency on libraries Martin Wilck
2018-03-01  5:35   ` Benjamin Marzinski
2018-02-20 13:26 ` [RFC PATCH 02/20] libmultipath: remove unused "stdout helpers" Martin Wilck
2018-03-01  5:36   ` Benjamin Marzinski
2018-02-20 13:26 ` [RFC PATCH 03/20] libmultipath: get rid of selector "hack" in print.c Martin Wilck
2018-03-01  5:36   ` Benjamin Marzinski
2018-02-20 13:26 ` [RFC PATCH 04/20] libmultipath: parser: use call-by-value for "snprint" methods Martin Wilck
2018-03-01  5:37   ` Benjamin Marzinski
2018-02-20 13:26 ` [RFC PATCH 05/20] libmultipath: don't update path groups when printing Martin Wilck
2018-02-28 23:40   ` Benjamin Marzinski
2018-03-02 13:59     ` Martin Wilck
2018-03-02 15:31       ` Benjamin Marzinski
2018-02-20 13:26 ` [RFC PATCH 06/20] libmultipath/print: use "const" where appropriate Martin Wilck
2018-03-01  5:37   ` Benjamin Marzinski
2018-02-20 13:26 ` [RFC PATCH 07/20] libmultipath: use "const" in devmapper code Martin Wilck
2018-03-01  5:39   ` Benjamin Marzinski
2018-02-20 13:26 ` [RFC PATCH 08/20] libmultipath: fix compiler warnings for -Wcast-qual Martin Wilck
2018-03-01  5:39   ` Benjamin Marzinski
2018-02-20 13:26 ` [RFC PATCH 09/20] multipath-tools: Makefile.inc: use -Werror=cast-qual Martin Wilck
2018-03-01  5:59   ` Benjamin Marzinski
2018-02-20 13:26 ` [RFC PATCH 10/20] libmultipath: add vector_free_const() Martin Wilck
2018-03-01  6:00   ` Benjamin Marzinski
2018-02-20 13:26 ` [RFC PATCH 11/20] libmultipath: add vector_convert() Martin Wilck
2018-03-01  6:02   ` Benjamin Marzinski
2018-02-20 13:26 ` [RFC PATCH 12/20] libmultipath: "generic multipath" interface Martin Wilck
2018-02-28 23:47   ` Benjamin Marzinski
2018-03-01  8:51     ` Martin Wilck
2018-02-20 13:26 ` [RFC PATCH 13/20] libmultipath: print: convert API to generic data type Martin Wilck
2018-02-28 23:55   ` Benjamin Marzinski
2018-02-20 13:26 ` [RFC PATCH 14/20] libmultipath: print: use generic API for get_x_layout() Martin Wilck
2018-03-01  6:03   ` Benjamin Marzinski
2018-02-20 13:26 ` [RFC PATCH 15/20] libmultipath: API for foreign multipath handling Martin Wilck
2018-03-01  3:01   ` Benjamin Marzinski
2018-02-20 13:26 ` [RFC PATCH 16/20] libmultipath/print: add "%G - foreign" wildcard Martin Wilck
2018-03-01  6:04   ` Benjamin Marzinski
2018-02-20 13:26 ` [RFC PATCH 17/20] libmultipath/foreign: nvme foreign library Martin Wilck
2018-03-01  3:14   ` Benjamin Marzinski [this message]
2018-03-02 16:04     ` Martin Wilck
2018-03-02 18:30       ` Benjamin Marzinski
2018-02-20 13:26 ` [RFC PATCH 18/20] multipath: use foreign API Martin Wilck
2018-03-01  3:55   ` Benjamin Marzinski
2018-03-02 16:36     ` Martin Wilck
2018-02-20 13:26 ` [RFC PATCH 19/20] multipathd: " Martin Wilck
2018-03-01  5:13   ` Benjamin Marzinski
2018-03-02 17:04     ` Martin Wilck
2018-03-02 18:42       ` Benjamin Marzinski
2018-03-02 19:19     ` Martin Wilck
2018-03-02 20:00       ` Benjamin Marzinski
2018-03-02 21:18         ` [PATCH] multipathd: fix inverted signal blocking logic Martin Wilck
2018-03-02 21:35           ` Bart Van Assche
2018-03-02 22:15             ` Martin Wilck
2018-03-02 22:23               ` Bart Van Assche
2018-03-02 23:16                 ` Martin Wilck
2018-03-02 23:27                   ` Bart Van Assche
2018-03-03  0:31                     ` Martin Wilck
2018-03-05 16:27                       ` Bart Van Assche
2018-03-05 17:28                         ` Martin Wilck
2018-03-06  0:46                           ` Benjamin Marzinski
2018-03-06  8:48                             ` Martin Wilck
2018-03-02 21:00     ` [RFC PATCH 19/20] multipathd: use foreign API Bart Van Assche
2018-02-20 13:26 ` [RFC PATCH 20/20] libmultipath: foreign/nvme: implement path display Martin Wilck
2018-03-01  5:19   ` Benjamin Marzinski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180301031420.GM14513@octiron.msp.redhat.com \
    --to=bmarzins@redhat.com \
    --cc=dm-devel@redhat.com \
    --cc=mwilck@suse.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox