From: Benjamin Marzinski <bmarzins@redhat.com>
To: Martin Wilck <martin.wilck@suse.com>
Cc: Christophe Varoqui <christophe.varoqui@opensvc.com>,
dm-devel@lists.linux.dev
Subject: Re: [PATCH 17/44] libmultipath: add libmp_mapinfo()
Date: Wed, 10 Jul 2024 18:53:13 -0400 [thread overview]
Message-ID: <Zo8Q2UZLwzkaRePo@redhat.com> (raw)
In-Reply-To: <20240709213935.177028-18-mwilck@suse.com>
On Tue, Jul 09, 2024 at 11:39:08PM +0200, Martin Wilck wrote:
This is a great idea! I just have a few minor issues.
> libmp_mapinfo() is intended as a generic abstraction for retrieving information from
> the kernel device-mapper driver. It retrieves the information that the caller
> needs, with a minimal set of DM ioctls, and never more then 2 ioctl calls.
>
> libdm's DM_DEVICE_TABLE and DM_DEVICE_STATUS calls map to the kernel's
> DM_TABLE_STATUS ioctl, with or without the DM_STATUS_TABLE_FLAG set,
> respectively. DM_TABLE_STATUS always retrieves the basic map status (struct
> dm_info) and the map UUID and name, too.
>
> Note: I'd prefer to use an unnamed struct instead of _u in
> union libmp_map_identifer. But doing using an unnamed struct and and
> initializing the union like this in a function argument:
>
> func((mapid_t) { .major = major, .minor = minor })
This is a nitpick, but instead of using _u, couldn't you just pass the
major and minor as a dev_t? If you're working with an actual major and
minor, you would need to do something like:
(mapid_t) { .dev = makedev(major, minor) }
but we are often already dealing with a dev_t, so instead of doing:
(mapid_t) { ._u = { major(dev), minor(dev) } }
we could just do:
(mapid_t) { .dev = dev }
To get the major and minor out in the libmp_mapinfo() you would need to
use major(id.dev) and minor(id.dev), instead of id._u.major and
id._u.minor, but I think first set is more readable anyway.
>
> is not part of C99, and not supported in gcc 4.8, which we still support.
>
> Likewise, the following syntax for initializing an empty struct:
>
> (mapinfo_t) { 0 }
>
> is not supported on all architectures we support (notably clang 3.5 under
> Debian Jessie).
>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
> libmultipath/devmapper.c | 181 +++++++++++++++++++++++++++++-
> libmultipath/devmapper.h | 66 +++++++++++
> libmultipath/libmultipath.version | 3 +-
> 3 files changed, 248 insertions(+), 2 deletions(-)
>
> diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c
> index 3685ef7..5c9f9d8 100644
> --- a/libmultipath/devmapper.c
> +++ b/libmultipath/devmapper.c
> @@ -14,7 +14,6 @@
> #include <errno.h>
> #include <syslog.h>
> #include <sys/sysmacros.h>
> -#include <linux/dm-ioctl.h>
>
> #include "util.h"
> #include "vector.h"
> @@ -604,6 +603,186 @@ has_dm_info(const struct multipath *mpp)
> return (mpp && mpp->dmi.exists != 0);
> }
>
> +static int libmp_set_map_identifier(int flags, mapid_t id, struct dm_task *dmt)
> +{
> + switch (flags & __DM_MAP_BY_MASK) {
> + case DM_MAP_BY_UUID:
> + if (!id.str || !(*id.str))
> + return 0;
> + return dm_task_set_uuid(dmt, id.str);
> + case DM_MAP_BY_NAME:
> + if (!id.str || !(*id.str))
> + return 0;
> + return dm_task_set_name(dmt, id.str);
> + case DM_MAP_BY_DEV:
> + if (!dm_task_set_major(dmt, id._u.major))
> + return 0;
> + return dm_task_set_minor(dmt, id._u.minor);
> + default:
> + condlog(0, "%s: invalid by_id", __func__);
> + return 0;
> + }
> +}
> +
> +static int libmp_mapinfo__(int flags, mapid_t id, mapinfo_t info, const char *map_id)
> +{
> + struct dm_task __attribute__((cleanup(cleanup_dm_task))) *dmt = NULL;
> + struct dm_info dmi;
> + int rc, ioctl_nr;
> + uint64_t start, length = 0;
> + char *target_type = NULL, *params = NULL;
> + const char *name = NULL, *uuid = NULL;
> + char __attribute__((cleanup(cleanup_charp))) *tmp_target = NULL;
> + char __attribute__((cleanup(cleanup_charp))) *tmp_status = NULL;
> + bool tgt_set = false;
> +
> + /*
> + * If both info.target and info.status are set, we need two
> + * ioctls. Call this function recursively.
> + * If successful, tmp_target will be non-NULL.
> + */
> + if (info.target && info.status) {
> + rc = libmp_mapinfo__(flags, id,
> + (mapinfo_t) {
> + .target = &tmp_target,
> + .tgt_type = info.tgt_type,
> + },
> + map_id);
> + if (rc != DMP_OK)
> + return rc;
> + tgt_set = true;
> + }
> +
> + /*
> + * The DM_DEVICE_TABLE and DM_DEVICE_STATUS ioctls both fetch the basic
> + * information from DM_DEVICE_INFO, too.
> + * Choose the most lightweight ioctl to fetch all requested info.
> + */
> + if (info.target && !info.status)
> + ioctl_nr = DM_DEVICE_TABLE;
> + else if (info.status || info.size || info.tgt_type)
> + ioctl_nr = DM_DEVICE_STATUS;
> + else
> + ioctl_nr = DM_DEVICE_INFO;
> +
> + if (!(dmt = libmp_dm_task_create(ioctl_nr)))
> + return DMP_ERR;
> +
> + if (!libmp_set_map_identifier(flags, id, dmt)) {
> + condlog(2, "%s: failed to set map identifier to %s", __func__, map_id);
> + return DMP_ERR;
> + }
> +
> + if (!libmp_dm_task_run(dmt)) {
> + dm_log_error(3, ioctl_nr, dmt);
> + if (dm_task_get_errno(dmt) == ENXIO) {
> + condlog(2, "%s: map %s not found", __func__, map_id);
> + return DMP_NOT_FOUND;
> + } else
> + return DMP_ERR;
> + }
> +
> + condlog(4, "%s: DM ioctl %d succeeded for %s",
> + __func__, ioctl_nr, map_id);
> +
> + if (!dm_task_get_info(dmt, &dmi) || !dmi.exists) {
> + condlog(2, "%s: map %s doesn't exist", __func__, map_id);
> + return DMP_NOT_FOUND;
> + }
> +
> + if (info.target || info.status || info.size || info.tgt_type) {
> + if (dm_get_next_target(dmt, NULL, &start, &length,
> + &target_type, ¶ms) != NULL) {
> + condlog(2, "%s: map %s has multiple targets", __func__, map_id);
> + return DMP_NOT_FOUND;
> + }
> + if (!params) {
> + condlog(2, "%s: map %s has no targets", __func__, map_id);
> + return DMP_NOT_FOUND;
> + }
> + if (info.tgt_type && strcmp(info.tgt_type, target_type)) {
> + condlog(3, "%s: target type mismatch: \"%s\" != \"%s\"",
> + __func__, info.tgt_type, target_type);
> + return DMP_NO_MATCH;
> + }
> + }
> +
> + /*
> + * Check possible error conditions.
> + * If error is returned, don't touch any output parameters.
> + */
> + if ((info.name && !(name = dm_task_get_name(dmt)))
> + || (info.uuid && !(uuid = dm_task_get_uuid(dmt)))
> + || (info.status && !(tmp_status = strdup(params)))
> + || (info.target && !tmp_target && !(tmp_target = strdup(params))))
> + return DMP_ERR;
> +
> + if (info.name) {
> + strlcpy(info.name, name, WWID_SIZE);
> + condlog(4, "%s: %s: name: \"%s\"", __func__, map_id, info.name);
> + }
> + if (info.uuid) {
> + strlcpy(info.uuid, uuid, DM_UUID_LEN);
> + condlog(4, "%s: %s: uuid: \"%s\"", __func__, map_id, info.uuid);
> + }
> +
> + if (info.size) {
> + *info.size = length;
> + condlog(4, "%s: %s: size: %lld", __func__, map_id, *info.size);
> + }
> +
> + if (info.dmi) {
> + memcpy(info.dmi, &dmi, sizeof(*info.dmi));
> + condlog(4, "%s: %s %d:%d, %d targets, %s table, %s, %s, %d opened, %u events",
> + __func__, map_id,
> + info.dmi->major, info.dmi->minor,
> + info.dmi->target_count,
> + info.dmi->live_table ? "live" :
> + info.dmi->inactive_table ? "inactive" : "no",
> + info.dmi->suspended ? "suspended" : "active",
> + info.dmi->read_only ? "ro" : "rw",
> + info.dmi->open_count,
> + info.dmi->event_nr);
> + }
> +
> + if (info.target) {
> + *info.target = steal_ptr(tmp_target);
> + if (!tgt_set)
> + condlog(4, "%s: %s: target: \"%s\"", __func__, map_id, *info.target);
> + }
> +
> + if (info.status) {
> + *info.status = steal_ptr(tmp_status);
> + condlog(4, "%s: %s: status: \"%s\"", __func__, map_id, *info.status);
> + }
> +
> + return DMP_OK;
> +}
> +
> +/* Helper: format a string describing the map for log messages */
> +static const char* libmp_map_identifier(int flags, mapid_t id, char buf[BLK_DEV_SIZE])
> +{
> + switch (flags & __DM_MAP_BY_MASK) {
> + case DM_MAP_BY_NAME:
> + case DM_MAP_BY_UUID:
> + return id.str;
> + case DM_MAP_BY_DEV:
> + safe_snprintf(buf, BLK_DEV_SIZE, "%d:%d", id._u.major, id._u.minor);
> + return buf;
> + default:
> + safe_snprintf(buf, BLK_DEV_SIZE, "*invalid*");
> + return buf;
> + }
> +}
> +
> +int libmp_mapinfo(int flags, mapid_t id, mapinfo_t info)
> +{
> + char idbuf[BLK_DEV_SIZE];
> +
> + return libmp_mapinfo__(flags, id, info,
> + libmp_map_identifier(flags, id, idbuf));
> +}
> +
> int
> dm_get_info(const char *name, struct dm_info *info)
> {
> diff --git a/libmultipath/devmapper.h b/libmultipath/devmapper.h
> index 9438c2d..269389b 100644
> --- a/libmultipath/devmapper.h
> +++ b/libmultipath/devmapper.h
> @@ -1,5 +1,6 @@
> #ifndef _DEVMAPPER_H
> #define _DEVMAPPER_H
> +#include <linux/dm-ioctl.h>
> #include "autoconfig.h"
> #include "structs.h"
>
> @@ -31,8 +32,73 @@ enum {
> DMP_ERR,
> DMP_OK,
> DMP_NOT_FOUND,
> + DMP_NO_MATCH,
> };
>
> +/**
> + * enum mapinfo_flags: input flags for libmp_mapinfo()
> + */
> +enum __mapinfo_flags {
> + /** DM_MAP_BY_NAME: identify map by device-mapper name from @name */
> + DM_MAP_BY_NAME = 0,
> + /** DM_MAP_BY_UUID: identify map by device-mapper UUID from @uuid */
> + DM_MAP_BY_UUID,
> + /** DM_MAP_BY_DEV: identify map by major/minor number from @dmi */
> + DM_MAP_BY_DEV,
> + __DM_MAP_BY_MASK = (1 << 3) - 1,
I assume you orignally indented these to be flags, but they aren't
really, so __DM_MAP_BY_MASK would just be 3, since the values are 0, 1,
and 2.
> +};
> +
> +typedef union libmp_map_identifier {
> + const char *str;
Like I said before, I would just use
dev_t dev;
instead of the struct. But I don't have stong feelings about this if you
prefer keeping major and minor separate.
> + struct {
> + int major;
> + int minor;
> + } _u;
> +} mapid_t;
> +
> +typedef struct libmp_map_info {
> + /** @name: name of the map.
> + * If non-NULL, it must point to an array of WWID_SIZE bytes
> + */
> + char *name;
> + /** @uuid: UUID of the map.
> + * If non-NULL it must point to an array of DM_UUID_LEN bytes
> + */
> + char *uuid;
> + /** @dmi: Basic info, must point to a valid dm_info buffer if non-NULL */
> + struct dm_info *dmi;
> + /** @target: target params, *@target will be allocated if @target is non-NULL*/
> + char **target;
> + /** @size: target size. Will be ignored if @target is NULL */
> + unsigned long long *size;
> + /** @status: target status, *@status will be allocated if @status is non-NULL */
> + char **status;
> + /** @tgt_type: (input) set to a target type, e.g. "multipath" to limit
> + * successful return to just this target type in libmp_mapinfo().
> + */
> + const char *tgt_type;
> +} mapinfo_t;
> +
> +/**
> + * libmp_mapinfo(): obtain information about a map from the kernel
> + * @param flags: see __mapinfo_flags above.
> + * Exactly one of DM_MAP_BY_NAME, DM_MAP_BY_UUID, and DM_MAP_BY_DEV must be set.
Again, you can't really set multiple values, so this comment doesn't
make sense.
-Ben
> + * @param id: string or major/minor to identify the map to query
> + * @param info: output parameters, see above. Non-NULL elements will be filled in.
> + * @returns:
> + * DMP_OK if successful.
> + * DMP_NOT_FOUND if the map wasn't found, or has no or multiple targets.
> + * DMP_NO_MATCH if the map didn't match @tgt_type (see above).
> + * DMP_ERR if some other error occurred.
> + *
> + * This function obtains the requested information for the device-mapper map
> + * identified by the input parameters.
> + * Output parameters are only filled in if the return value is DMP_OK.
> + * For target / status / size information, the map's table should contain
> + * only one target (usually multipath or linear).
> + */
> +int libmp_mapinfo(int flags, mapid_t id, mapinfo_t info);
> +
> int dm_prereq(unsigned int *v);
> void skip_libmp_dm_init(void);
> void libmp_dm_exit(void);
> diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version
> index f58cb1d..48c2b67 100644
> --- a/libmultipath/libmultipath.version
> +++ b/libmultipath/libmultipath.version
> @@ -43,7 +43,7 @@ LIBMPATHCOMMON_1.0.0 {
> put_multipath_config;
> };
>
> -LIBMULTIPATH_24.0.0 {
> +LIBMULTIPATH_25.0.0 {
> global:
> /* symbols referenced by multipath and multipathd */
> add_foreign;
> @@ -134,6 +134,7 @@ global:
> libmp_get_version;
> libmp_get_multipath_config;
> libmp_dm_task_run;
> + libmp_mapinfo;
> libmp_put_multipath_config;
> libmp_udev_set_sync_support;
> libmultipath_exit;
> --
> 2.45.2
next prev parent reply other threads:[~2024-07-10 22:53 UTC|newest]
Thread overview: 75+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-07-09 21:38 [PATCH 00/44] multipath-tools: devmapper API refactored Martin Wilck
2024-07-09 21:38 ` [PATCH 01/44] multipath-tools CI: more fixes for arm/v7 Martin Wilck
2024-07-10 7:06 ` Martin Wilck
2024-07-09 21:38 ` [PATCH 02/44] multipath-tools CI: fix dmevents test for Debian Sid, arm/v7 Martin Wilck
2024-07-09 21:38 ` [PATCH 03/44] create-config.mk: use printf instead of /bin/echo Martin Wilck
2024-07-09 21:38 ` [PATCH 04/44] multipathd.service.in: use @BINDIR@ instead of /sbin Martin Wilck
2024-07-09 21:38 ` [PATCH 05/44] Makefile.inc: replace @BINDIR@ with $(TGTDIR)/$(bindir) Martin Wilck
2024-07-09 21:38 ` [PATCH 06/44] kpartx.rules: use @BINDIR@ to locate kpartx Martin Wilck
2024-07-10 16:30 ` Benjamin Marzinski
2024-07-10 20:15 ` Martin Wilck
2024-07-09 21:38 ` [PATCH 07/44] multipath-tools: Remove hard-coded paths to executables Martin Wilck
2024-07-09 21:38 ` [PATCH 08/44] multipath-tools: compile_commands.json fixes Martin Wilck
2024-07-09 21:39 ` [PATCH 09/44] multipath-tools: .gitignore: ignore o.wrap files for CI helpers Martin Wilck
2024-07-09 21:39 ` [PATCH 10/44] libmultipath: remove unused includes in devmapper.h Martin Wilck
2024-07-09 21:39 ` [PATCH 11/44] libmultipath: use DM_DEVICE_INFO in dm_mapname() Martin Wilck
2024-07-09 21:39 ` [PATCH 12/44] multipath-tools: don't call dm_task_no_open_count() Martin Wilck
2024-07-09 21:39 ` [PATCH 13/44] libmpathutil: export cleanup_udev_device() Martin Wilck
2024-07-09 21:39 ` [PATCH 14/44] libmpathutil: add cleanup_vector() Martin Wilck
2024-07-09 21:39 ` [PATCH 15/44] libmultipath: add cleanup helpers for struct multipath Martin Wilck
2024-07-09 21:39 ` [PATCH 16/44] libmultipath: add cleanup_dm_task(), and use it in devmapper.c Martin Wilck
2024-07-10 20:12 ` Benjamin Marzinski
2024-07-10 20:18 ` Martin Wilck
2024-07-09 21:39 ` [PATCH 17/44] libmultipath: add libmp_mapinfo() Martin Wilck
2024-07-10 22:53 ` Benjamin Marzinski [this message]
2024-07-11 11:00 ` Martin Wilck
2024-07-09 21:39 ` [PATCH 18/44] libmultipath tests: add tests for libmp_mapinfo() Martin Wilck
2024-07-09 21:39 ` [PATCH 19/44] libmultipath: implement dm_get_info() and dm_map_present() with new API Martin Wilck
2024-07-09 21:39 ` [PATCH 20/44] libmultipath: remove dm_get_prefixed_uuid() Martin Wilck
2024-07-09 21:39 ` [PATCH 21/44] libmultipath: is_mpath_part(): improve parsing Martin Wilck
2024-07-10 22:54 ` Benjamin Marzinski
2024-07-11 11:08 ` Martin Wilck
2024-07-11 17:01 ` Benjamin Marzinski
2024-07-11 17:41 ` Martin Wilck
2024-07-09 21:39 ` [PATCH 22/44] libmultipath: rename dm_get_uuid() -> dm_get_wwid() Martin Wilck
2024-07-09 21:39 ` [PATCH 23/44] libmultipath: improve dm_get_wwid() return value logic Martin Wilck
2024-07-10 23:34 ` Benjamin Marzinski
2024-07-11 12:25 ` Martin Wilck
2024-07-11 16:38 ` Benjamin Marzinski
2024-07-11 22:00 ` Martin Wilck
2024-07-09 21:39 ` [PATCH 24/44] libmultipath: reimplement dm_map_name() with new API Martin Wilck
2024-07-10 23:41 ` Benjamin Marzinski
2024-07-09 21:39 ` [PATCH 25/44] libmultipath: reimplement dm_map_present_by_uuid() Martin Wilck
2024-07-09 21:39 ` [PATCH 26/44] libmultipath: reimplement dm_get_opencount() with new API Martin Wilck
2024-07-09 21:39 ` [PATCH 27/44] libmpathpersist: skip redundant dm_map_present() call Martin Wilck
2024-07-09 21:39 ` [PATCH 28/44] libmultipath: implement dm_is_mpath() with new API Martin Wilck
2024-07-11 0:21 ` Benjamin Marzinski
2024-07-11 12:32 ` Martin Wilck
2024-07-09 21:39 ` [PATCH 29/44] libmultipath: implement dm_get_multipath() " Martin Wilck
2024-07-09 21:39 ` [PATCH 30/44] libmultipath: use libmp_mapinfo() in _dm_flush_map() Martin Wilck
2024-07-09 21:39 ` [PATCH 31/44] libmultipath: add is_mpath_uuid() helper Martin Wilck
2024-07-11 3:38 ` Benjamin Marzinski
2024-07-11 12:56 ` Martin Wilck
2024-07-11 18:39 ` Benjamin Marzinski
2024-07-11 18:59 ` Martin Wilck
2024-07-09 21:39 ` [PATCH 32/44] libmultipath: add is_mpath_part_uuid() helper Martin Wilck
2024-07-09 21:39 ` [PATCH 33/44] libmultipath: add dmp_errstr() helper Martin Wilck
2024-07-09 21:39 ` [PATCH 34/44] libmultipath: use libmp_mapinfo() in do_foreach_partmaps() Martin Wilck
2024-07-09 21:39 ` [PATCH 35/44] libmultipath: use libmp_pathinfo() in update_multipath_table() Martin Wilck
2024-07-11 5:16 ` Benjamin Marzinski
2024-07-11 15:29 ` Martin Wilck
2024-07-09 21:39 ` [PATCH 36/44] libmultipath: update mpp->dmi " Martin Wilck
2024-07-09 21:39 ` [PATCH 37/44] libmultipath: drop extra call to dm_map_present() in domap() Martin Wilck
2024-07-09 21:39 ` [PATCH 38/44] libmultipath: split off update_multipath_table__() Martin Wilck
2024-07-09 21:39 ` [PATCH 39/44] multipath: implement check_usable_paths() with libmp_pathinfo() Martin Wilck
2024-07-11 5:38 ` Benjamin Marzinski
2024-07-09 21:39 ` [PATCH 40/44] multipathd: implement add_map_without_path() with libmp_mapinfo() Martin Wilck
2024-07-11 6:11 ` Benjamin Marzinski
2024-07-09 21:39 ` [PATCH 41/44] libmultipath: simplify dm_get_maps() Martin Wilck
2024-07-09 21:39 ` [PATCH 42/44] llibmultipath: fix return code check for dm_is_suspended() Martin Wilck
2024-07-11 6:27 ` Benjamin Marzinski
2024-07-11 15:35 ` Martin Wilck
2024-07-09 21:39 ` [PATCH 43/44] libmpathpersist: use libmp_mapinfo() in get_mpvec() Martin Wilck
2024-07-11 6:43 ` Benjamin Marzinski
2024-07-09 21:39 ` [PATCH 44/44] libmpathpersist: use mpp->alias in do_mpath_persistent_reserve_out() Martin Wilck
2024-07-11 6:55 ` [PATCH 00/44] multipath-tools: devmapper API refactored 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=Zo8Q2UZLwzkaRePo@redhat.com \
--to=bmarzins@redhat.com \
--cc=christophe.varoqui@opensvc.com \
--cc=dm-devel@lists.linux.dev \
--cc=martin.wilck@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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.