dm-devel.redhat.com archive mirror
 help / color / mirror / Atom feed
* [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul
@ 2021-09-10 11:40 mwilck
  2021-09-10 11:40 ` [dm-devel] [PATCH 01/35] libmultipath: add timespeccmp() utility function mwilck
                   ` (34 more replies)
  0 siblings, 35 replies; 89+ messages in thread
From: mwilck @ 2021-09-10 11:40 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

Hello Christophe, hello Ben,

The current multipathd unix listener code has various deficiencies.

 - client disconnects aren't handled correctly,
 - the uxsock_timeout is applied for receiving, handling, and
   responding to the client requests separately, rather than for
   the entire operation,
 - timeouts are logged, but not acted upon, causing the timeout
   to be noticed in the client rather than in the server.
 - clients may see a timeout while "reconfigure" is running,
 - unpriviledged (non-root) client connections don't work
   correctly
 - most importantly, the code busy-loops, polls, or waits in
   various places in called subroutines, which is a no-go in a
   piece of code designed as an event handler and may lead
   to spurious timeouts and delayed reaction e.g. to signals
   or client requests.

This patch set approaches all these issues. Fixing the last one,
in particular, requires a major refactoring of the uxlsnr code.
Overall, the reliability and latency of client request handling
and signal handling by multipathd should be noticeably improved
by this patch set.

The biggest problem (waiting for the vecs lock in a client handler)
can only be fixed by moving this wait into the handlers ppoll()
loop (another possible fix would have been to handle all clients
in separate threads, but that would have required even more
complexity). The patch set achieves this by adding an eventfd-based
notification mechanism to the vecs lock, which can be passed to
ppoll() to wake up when the lock is freed.

Furthermore, client requests can't be handled in a single poll
iteration any more. Therefore the client connection becomes stateful,
and is handled by a state machine using the states RECEIVE, PARSE,
WAIT FOR LOCK, WORK, and SEND.

The refactoring is done step by step for ease (hopefully) of
review. 1/35-4/35 add utility code that will be used by the uxlsnr
refactoring. 5/35-7/35 are some independent patches that
aren't directly related to uxlnsr, but fix issues that I observed
while working on this set. 14/35 too, but it deserves special mention
becaiuse it introduces a new option and changes the default behavior
of "multipathd reconfigure".

8/35-13/35 are minor fixups in the client handling code. This code is
strongly related to the uxlsnr, thus I thought I'd rather fix it
before making the other changes. In 25/35, the cli-handlers are
converted to use the strbuf API everywhere instead of separate "reply"
and "len" arguments. 15/35-18/35 are minor fixes for the
uxlsnr. 19/35-34/35 are the actual refactoring patches for the uxlsnr
code. First I move some code around unchanged, then I add the
state machine (handle_client()) and move the code into it piece
by piece. 35/35 adds a fix for the client side (multipathd -k).

CC'ing Lixiaokeng and Chongyun Wu, as they have test cases that use
the client code heavily AFAIR. Testing by 3rd parties would be
very welcome.

Comments welcome, regards,
Martin

Martin Wilck (35):
  libmultipath: add timespeccmp() utility function
  libmultipath: add trylock() helper
  libmultipath: add optional wakeup functionality to lock.c
  libmultipath: print: add __snprint_config()
  libmultipath: improve cleanup of uevent queues on exit
  multipathd: fix systemd notification when stopping while reloading
  multipathd: improve delayed reconfigure
  multipathd: cli.h: formatting improvements
  multipathd: cli_del_map: fix reply for delayed action
  multipathd: add prototype for cli_handler functions
  multipathd: make all cli_handlers static
  multipathd: add and set cli_handlers in a single step
  multipathd: cli.c: use ESRCH for "command not found"
  multipathd: add "force_reconfigure" option
  multipathd: uxlsnr: avoid stalled clients during reconfigure
  multipathd: uxlsnr: handle client HUP
  multipathd: uxlsnr: use symbolic values for pollfd indices
  multipathd: uxlsnr: avoid using fd -1 in ppoll()
  multipathd: uxlsnr: data structure for stateful client connection
  multipathd: move uxsock_trigger() to uxlsnr.c
  multipathd: move parse_cmd() to uxlsnr.c
  multipathd: uxlsnr: remove check_timeout()
  multipathd: uxlsnr: move client handling to separate function
  multipathd: uxlsnr: use main poll loop for receiving
  multipathd: use strbuf in cli_handler functions
  multipathd: uxlsnr: check root on connection startup
  multipathd: uxlsnr: pass struct client to uxsock_trigger() and
    parse_cmd()
  multipathd: uxlsnr: move handler execution to separate function
  multipathd: uxlsnr: use parser to determine non-root commands
  multipathd: uxlsnr: merge uxsock_trigger() into state machine
  multipathd: uxlsnr: add idle notification
  multipathd: uxlsnr: add timeout handling
  multipathd: uxlsnr: use poll loop for sending, too
  multipathd: uxlsnr: drop client_lock
  multipathd: uxclt: allow client mode for non-root, too

 libmultipath/config.c             |   1 +
 libmultipath/config.h             |   1 +
 libmultipath/configure.c          |  19 +-
 libmultipath/defaults.h           |   1 +
 libmultipath/dict.c               |   4 +
 libmultipath/libmultipath.version |  15 +
 libmultipath/lock.c               |  12 +-
 libmultipath/lock.h               |  11 +-
 libmultipath/print.c              |  34 +-
 libmultipath/print.h              |   2 +
 libmultipath/time-util.c          |  12 +
 libmultipath/time-util.h          |   1 +
 libmultipath/uevent.c             |  49 ++-
 multipath/multipath.8             |   6 +-
 multipath/multipath.conf.5        |  17 +
 multipathd/cli.c                  | 172 ++-------
 multipathd/cli.h                  | 101 +++---
 multipathd/cli_handlers.c         | 553 ++++++++++++++---------------
 multipathd/cli_handlers.h         |  61 +---
 multipathd/main.c                 | 238 +++++--------
 multipathd/main.h                 |   3 +-
 multipathd/multipathd.8           |   6 +-
 multipathd/uxlsnr.c               | 566 +++++++++++++++++++++++-------
 multipathd/uxlsnr.h               |   4 +-
 24 files changed, 1043 insertions(+), 846 deletions(-)

-- 
2.33.0


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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 01/35] libmultipath: add timespeccmp() utility function
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
@ 2021-09-10 11:40 ` mwilck
  2021-09-15 22:07   ` Benjamin Marzinski
  2021-09-10 11:40 ` [dm-devel] [PATCH 02/35] libmultipath: add trylock() helper mwilck
                   ` (33 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:40 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

Add a small utility that will be used in later patches.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/libmultipath.version |  5 +++++
 libmultipath/time-util.c          | 12 ++++++++++++
 libmultipath/time-util.h          |  1 +
 3 files changed, 18 insertions(+)

diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version
index eb5b5b5..c98cf7f 100644
--- a/libmultipath/libmultipath.version
+++ b/libmultipath/libmultipath.version
@@ -287,3 +287,8 @@ global:
 local:
 	*;
 };
+
+LIBMULTIPATH_9.1.0 {
+global:
+	timespeccmp;
+} LIBMULTIPATH_9.0.0;
diff --git a/libmultipath/time-util.c b/libmultipath/time-util.c
index 55f366c..2919300 100644
--- a/libmultipath/time-util.c
+++ b/libmultipath/time-util.c
@@ -49,3 +49,15 @@ void timespecsub(const struct timespec *a, const struct timespec *b,
 	res->tv_nsec = a->tv_nsec - b->tv_nsec;
 	normalize_timespec(res);
 }
+
+int timespeccmp(const struct timespec *a, const struct timespec *b)
+{
+	struct timespec tmp;
+
+	timespecsub(a, b, &tmp);
+	if (tmp.tv_sec > 0)
+		return 1;
+	if (tmp.tv_sec < 0)
+		return -1;
+	return tmp.tv_nsec > 0 ? 1 : (tmp.tv_nsec < 0 ? -1 : 0);
+}
diff --git a/libmultipath/time-util.h b/libmultipath/time-util.h
index b23d328..4a80ebd 100644
--- a/libmultipath/time-util.h
+++ b/libmultipath/time-util.h
@@ -10,5 +10,6 @@ void pthread_cond_init_mono(pthread_cond_t *cond);
 void normalize_timespec(struct timespec *ts);
 void timespecsub(const struct timespec *a, const struct timespec *b,
 		 struct timespec *res);
+int timespeccmp(const struct timespec *a, const struct timespec *b);
 
 #endif /* _TIME_UTIL_H_ */
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 02/35] libmultipath: add trylock() helper
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
  2021-09-10 11:40 ` [dm-devel] [PATCH 01/35] libmultipath: add timespeccmp() utility function mwilck
@ 2021-09-10 11:40 ` mwilck
  2021-09-15 22:07   ` Benjamin Marzinski
  2021-09-10 11:40 ` [dm-devel] [PATCH 03/35] libmultipath: add optional wakeup functionality to lock.c mwilck
                   ` (32 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:40 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

Add a small helper.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/lock.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libmultipath/lock.h b/libmultipath/lock.h
index a170efe..d99eedb 100644
--- a/libmultipath/lock.h
+++ b/libmultipath/lock.h
@@ -12,6 +12,11 @@ static inline void lock(struct mutex_lock *a)
 	pthread_mutex_lock(&a->mutex);
 }
 
+static inline int trylock(struct mutex_lock *a)
+{
+	return pthread_mutex_trylock(&a->mutex);
+}
+
 static inline int timedlock(struct mutex_lock *a, struct timespec *tmo)
 {
 	return pthread_mutex_timedlock(&a->mutex, tmo);
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 03/35] libmultipath: add optional wakeup functionality to lock.c
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
  2021-09-10 11:40 ` [dm-devel] [PATCH 01/35] libmultipath: add timespeccmp() utility function mwilck
  2021-09-10 11:40 ` [dm-devel] [PATCH 02/35] libmultipath: add trylock() helper mwilck
@ 2021-09-10 11:40 ` mwilck
  2021-09-15 22:13   ` Benjamin Marzinski
  2021-09-10 11:40 ` [dm-devel] [PATCH 04/35] libmultipath: print: add __snprint_config() mwilck
                   ` (31 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:40 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

Have struct mutex_lock take an optional wakeup function.
unlock() is renamed to __unlock() in order to prevent it from
being called by mistake.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/libmultipath.version |  5 +++++
 libmultipath/lock.c               | 12 +++++++++++-
 libmultipath/lock.h               |  6 +++++-
 3 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version
index c98cf7f..2107c51 100644
--- a/libmultipath/libmultipath.version
+++ b/libmultipath/libmultipath.version
@@ -292,3 +292,8 @@ LIBMULTIPATH_9.1.0 {
 global:
 	timespeccmp;
 } LIBMULTIPATH_9.0.0;
+
+LIBMULTIPATH_9.2.0 {
+global:
+	set_wakeup_fn;
+} LIBMULTIPATH_9.1.0;
diff --git a/libmultipath/lock.c b/libmultipath/lock.c
index 72c70e3..93b48db 100644
--- a/libmultipath/lock.c
+++ b/libmultipath/lock.c
@@ -3,6 +3,16 @@
 void cleanup_lock (void * data)
 {
 	struct mutex_lock *lock = data;
+	wakeup_fn *fn = lock->wakeup;
 
-	unlock(lock);
+	__unlock(lock);
+	if (fn)
+		fn();
+}
+
+void set_wakeup_fn(struct mutex_lock *lck, wakeup_fn *fn)
+{
+	lock(lck);
+	lck->wakeup = fn;
+	__unlock(lck);
 }
diff --git a/libmultipath/lock.h b/libmultipath/lock.h
index d99eedb..d7b779e 100644
--- a/libmultipath/lock.h
+++ b/libmultipath/lock.h
@@ -3,8 +3,11 @@
 
 #include <pthread.h>
 
+typedef void (wakeup_fn)(void);
+
 struct mutex_lock {
 	pthread_mutex_t mutex;
+	wakeup_fn *wakeup;
 };
 
 static inline void lock(struct mutex_lock *a)
@@ -22,7 +25,7 @@ static inline int timedlock(struct mutex_lock *a, struct timespec *tmo)
 	return pthread_mutex_timedlock(&a->mutex, tmo);
 }
 
-static inline void unlock(struct mutex_lock *a)
+static inline void __unlock(struct mutex_lock *a)
 {
 	pthread_mutex_unlock(&a->mutex);
 }
@@ -30,5 +33,6 @@ static inline void unlock(struct mutex_lock *a)
 #define lock_cleanup_pop(a) pthread_cleanup_pop(1)
 
 void cleanup_lock (void * data);
+void set_wakeup_fn(struct mutex_lock *lock, wakeup_fn *fn);
 
 #endif /* _LOCK_H */
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 04/35] libmultipath: print: add __snprint_config()
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (2 preceding siblings ...)
  2021-09-10 11:40 ` [dm-devel] [PATCH 03/35] libmultipath: add optional wakeup functionality to lock.c mwilck
@ 2021-09-10 11:40 ` mwilck
  2021-09-15 22:14   ` Benjamin Marzinski
  2021-09-10 11:40 ` [dm-devel] [PATCH 05/35] libmultipath: improve cleanup of uevent queues on exit mwilck
                   ` (30 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:40 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

exactly like snprint_config(), but takes a struct strbuf * as argument.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/libmultipath.version |  5 +++++
 libmultipath/print.c              | 34 +++++++++++++++++++++----------
 libmultipath/print.h              |  2 ++
 3 files changed, 30 insertions(+), 11 deletions(-)

diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version
index 2107c51..b2feee2 100644
--- a/libmultipath/libmultipath.version
+++ b/libmultipath/libmultipath.version
@@ -297,3 +297,8 @@ LIBMULTIPATH_9.2.0 {
 global:
 	set_wakeup_fn;
 } LIBMULTIPATH_9.1.0;
+
+LIBMULTIPATH_9.3.0 {
+global:
+	__snprint_config;
+} LIBMULTIPATH_9.2.0;
diff --git a/libmultipath/print.c b/libmultipath/print.c
index 2fb9f4e..d2ef010 100644
--- a/libmultipath/print.c
+++ b/libmultipath/print.c
@@ -1756,24 +1756,36 @@ static int snprint_blacklist_except(const struct config *conf,
 	return get_strbuf_len(buff) - initial_len;
 }
 
+int __snprint_config(const struct config *conf, struct strbuf *buff,
+		     const struct _vector *hwtable, const struct _vector *mpvec)
+{
+	int rc;
+
+	if ((rc = snprint_defaults(conf, buff)) < 0 ||
+	    (rc = snprint_blacklist(conf, buff)) < 0 ||
+	    (rc = snprint_blacklist_except(conf, buff)) < 0 ||
+	    (rc = snprint_hwtable(conf, buff,
+				  hwtable ? hwtable : conf->hwtable)) < 0 ||
+	    (rc = snprint_overrides(conf, buff, conf->overrides)) < 0)
+		return rc;
+
+	if (VECTOR_SIZE(conf->mptable) > 0 ||
+	    (mpvec != NULL && VECTOR_SIZE(mpvec) > 0))
+		if ((rc = snprint_mptable(conf, buff, mpvec)) < 0)
+			return rc;
+
+	return 0;
+}
+
 char *snprint_config(const struct config *conf, int *len,
 		     const struct _vector *hwtable, const struct _vector *mpvec)
 {
 	STRBUF_ON_STACK(buff);
 	char *reply;
-	int rc;
+	int rc = __snprint_config(conf, &buff, hwtable, mpvec);
 
-	if ((rc = snprint_defaults(conf, &buff)) < 0 ||
-	    (rc = snprint_blacklist(conf, &buff)) < 0 ||
-	    (rc = snprint_blacklist_except(conf, &buff)) < 0 ||
-	    (rc = snprint_hwtable(conf, &buff,
-				  hwtable ? hwtable : conf->hwtable)) < 0 ||
-	    (rc = snprint_overrides(conf, &buff, conf->overrides)) < 0)
+	if (rc < 0)
 		return NULL;
-	if (VECTOR_SIZE(conf->mptable) > 0 ||
-	    (mpvec != NULL && VECTOR_SIZE(mpvec) > 0))
-		if ((rc = snprint_mptable(conf, &buff, mpvec)) < 0)
-			return NULL;
 
 	if (len)
 		*len = get_strbuf_len(&buff);
diff --git a/libmultipath/print.h b/libmultipath/print.h
index c6674a5..b149275 100644
--- a/libmultipath/print.h
+++ b/libmultipath/print.h
@@ -54,6 +54,8 @@ int _snprint_multipath_topology (const struct gen_multipath *, struct strbuf *,
 #define snprint_multipath_topology(buf, mpp, v) \
 	_snprint_multipath_topology (dm_multipath_to_gen(mpp), buf, v)
 int snprint_multipath_topology_json(struct strbuf *, const struct vectors *vecs);
+int __snprint_config(const struct config *conf, struct strbuf *buff,
+		     const struct _vector *hwtable, const struct _vector *mpvec);
 char *snprint_config(const struct config *conf, int *len,
 		     const struct _vector *hwtable,
 		     const struct _vector *mpvec);
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 05/35] libmultipath: improve cleanup of uevent queues on exit
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (3 preceding siblings ...)
  2021-09-10 11:40 ` [dm-devel] [PATCH 04/35] libmultipath: print: add __snprint_config() mwilck
@ 2021-09-10 11:40 ` mwilck
  2021-09-15 22:20   ` Benjamin Marzinski
  2021-09-10 11:40 ` [dm-devel] [PATCH 06/35] multipathd: fix systemd notification when stopping while reloading mwilck
                   ` (29 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:40 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

uevents listed on merge_node must be cleaned up, too. uevents
cancelled while being serviced and temporary queues, likewise.
The global uevq must be cleaned out in the uevent listener thread,
because it might have added events after the dispatcher thread
had already finished.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/uevent.c | 49 ++++++++++++++++++++++++++++++++-----------
 1 file changed, 37 insertions(+), 12 deletions(-)

diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c
index 4265904..082e891 100644
--- a/libmultipath/uevent.c
+++ b/libmultipath/uevent.c
@@ -91,16 +91,25 @@ struct uevent * alloc_uevent (void)
 	return uev;
 }
 
+static void uevq_cleanup(struct list_head *tmpq);
+
+static void cleanup_uev(void *arg)
+{
+	struct uevent *uev = arg;
+
+	uevq_cleanup(&uev->merge_node);
+	if (uev->udev)
+		udev_device_unref(uev->udev);
+	FREE(uev);
+}
+
 static void uevq_cleanup(struct list_head *tmpq)
 {
 	struct uevent *uev, *tmp;
 
 	list_for_each_entry_safe(uev, tmp, tmpq, node) {
 		list_del_init(&uev->node);
-
-		if (uev->udev)
-			udev_device_unref(uev->udev);
-		FREE(uev);
+		cleanup_uev(uev);
 	}
 }
 
@@ -384,14 +393,10 @@ service_uevq(struct list_head *tmpq)
 	list_for_each_entry_safe(uev, tmp, tmpq, node) {
 		list_del_init(&uev->node);
 
+		pthread_cleanup_push(cleanup_uev, uev);
 		if (my_uev_trigger && my_uev_trigger(uev, my_trigger_data))
 			condlog(0, "uevent trigger error");
-
-		uevq_cleanup(&uev->merge_node);
-
-		if (uev->udev)
-			udev_device_unref(uev->udev);
-		FREE(uev);
+		pthread_cleanup_pop(1);
 	}
 }
 
@@ -411,6 +416,18 @@ static void monitor_cleanup(void *arg)
 	udev_monitor_unref(monitor);
 }
 
+static void cleanup_uevq(void *arg)
+{
+	uevq_cleanup(arg);
+}
+
+static void cleanup_global_uevq(void *arg __attribute__((unused)))
+{
+	pthread_mutex_lock(uevq_lockp);
+	uevq_cleanup(&uevq);
+	pthread_mutex_unlock(uevq_lockp);
+}
+
 /*
  * Service the uevent queue.
  */
@@ -425,6 +442,7 @@ int uevent_dispatch(int (*uev_trigger)(struct uevent *, void * trigger_data),
 	while (1) {
 		LIST_HEAD(uevq_tmp);
 
+		pthread_cleanup_push(cleanup_mutex, uevq_lockp);
 		pthread_mutex_lock(uevq_lockp);
 		servicing_uev = 0;
 		/*
@@ -436,14 +454,17 @@ int uevent_dispatch(int (*uev_trigger)(struct uevent *, void * trigger_data),
 		}
 		servicing_uev = 1;
 		list_splice_init(&uevq, &uevq_tmp);
-		pthread_mutex_unlock(uevq_lockp);
+		pthread_cleanup_pop(1);
+
 		if (!my_uev_trigger)
 			break;
+
+		pthread_cleanup_push(cleanup_uevq, &uevq_tmp);
 		merge_uevq(&uevq_tmp);
 		service_uevq(&uevq_tmp);
+		pthread_cleanup_pop(1);
 	}
 	condlog(3, "Terminating uev service queue");
-	uevq_cleanup(&uevq);
 	return 0;
 }
 
@@ -600,6 +621,8 @@ int uevent_listen(struct udev *udev)
 
 	events = 0;
 	gettimeofday(&start_time, NULL);
+	pthread_cleanup_push(cleanup_global_uevq, NULL);
+	pthread_cleanup_push(cleanup_uevq, &uevlisten_tmp);
 	while (1) {
 		struct uevent *uev;
 		struct udev_device *dev;
@@ -650,6 +673,8 @@ int uevent_listen(struct udev *udev)
 		gettimeofday(&start_time, NULL);
 		timeout = 30;
 	}
+	pthread_cleanup_pop(1);
+	pthread_cleanup_pop(1);
 out:
 	pthread_cleanup_pop(1);
 out_udev:
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 06/35] multipathd: fix systemd notification when stopping while reloading
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (4 preceding siblings ...)
  2021-09-10 11:40 ` [dm-devel] [PATCH 05/35] libmultipath: improve cleanup of uevent queues on exit mwilck
@ 2021-09-10 11:40 ` mwilck
  2021-09-15 22:55   ` Benjamin Marzinski
  2021-09-10 11:40 ` [dm-devel] [PATCH 07/35] multipathd: improve delayed reconfigure mwilck
                   ` (28 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:40 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

After sending "RELOADING=1" to systemd, a service must send
"READY=1" before "STOPPING=1". Otherwise systemd will be confused
and will not regard the service as stopped. Subsequent attempts
to start multipathd via socket activation fail until systemd times
out the reload operation.

The problem can be reproduced by running "multipathd shutdown"
quickly after "multipathd reconfigure".

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/main.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/multipathd/main.c b/multipathd/main.c
index 3aff241..67160b9 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -210,9 +210,12 @@ static void do_sd_notify(enum daemon_status old_state,
 	if (msg && !safe_sprintf(notify_msg, "STATUS=%s", msg))
 		sd_notify(0, notify_msg);
 
-	if (new_state == DAEMON_SHUTDOWN)
+	if (new_state == DAEMON_SHUTDOWN) {
+		/* Tell systemd that we're not RELOADING any more */
+		if (old_state == DAEMON_CONFIGURE && startup_done)
+			sd_notify(0, "READY=1");
 		sd_notify(0, "STOPPING=1");
-	else if (new_state == DAEMON_IDLE && old_state == DAEMON_CONFIGURE) {
+	} else if (new_state == DAEMON_IDLE && old_state == DAEMON_CONFIGURE) {
 		sd_notify(0, "READY=1");
 		startup_done = true;
 	} else if (new_state == DAEMON_CONFIGURE && startup_done)
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 07/35] multipathd: improve delayed reconfigure
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (5 preceding siblings ...)
  2021-09-10 11:40 ` [dm-devel] [PATCH 06/35] multipathd: fix systemd notification when stopping while reloading mwilck
@ 2021-09-10 11:40 ` mwilck
  2021-09-15 23:00   ` Benjamin Marzinski
  2021-09-10 11:40 ` [dm-devel] [PATCH 08/35] multipathd: cli.h: formatting improvements mwilck
                   ` (27 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:40 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

When a reconfigure operation is requested, either by the admin
or by some condition multipathd encounters, the current code
attempts to set DAEMON_CONFIGURE state and gives up after a second
if it doesn't succeed. Apart from shutdown, this happens only
if multipathd is either already reconfiguring, or busy in the
path checker loop.

This patch modifies the logic as follows: rather than waiting,
we set a flag that requests a reconfigure operation asap, i.e.
when the current operation is finished and the status switched
to DAEMON_IDLE. In this case, multipathd will not switch to IDLE
but start another reconfigure cycle.

This assumes that if a reconfigure is requested while one is already
running, the admin has made some (additional) changes and wants
multipathd to pull them in. As we can't be sure that the currently
running reconfigure has seen the configuration changes, we need
to start over again.

A positive side effect is less waiting in clients and multipathd.

After this change, the only caller of set_config_state() is
checkerloop(). Waking up every second just to see that DAEMON_RUNNING
couldn't be set makes no sense. Therefore set_config_state() is
changed to wait "forever", or until shutdown is requested. Unless
multipathd completely hangs, the wait will terminate sooner or
later.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/cli_handlers.c | 10 +----
 multipathd/main.c         | 92 +++++++++++++++++++++++++++++----------
 multipathd/main.h         |  3 +-
 3 files changed, 71 insertions(+), 34 deletions(-)

diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
index 6d3a0ae..44f76ee 100644
--- a/multipathd/cli_handlers.c
+++ b/multipathd/cli_handlers.c
@@ -1076,17 +1076,9 @@ cli_switch_group(void * v, char ** reply, int * len, void * data)
 int
 cli_reconfigure(void * v, char ** reply, int * len, void * data)
 {
-	int rc;
-
 	condlog(2, "reconfigure (operator)");
 
-	rc = set_config_state(DAEMON_CONFIGURE);
-	if (rc == ETIMEDOUT) {
-		condlog(2, "timeout starting reconfiguration");
-		return 1;
-	} else if (rc == EINVAL)
-		/* daemon shutting down */
-		return 1;
+	schedule_reconfigure();
 	return 0;
 }
 
diff --git a/multipathd/main.c b/multipathd/main.c
index 67160b9..5fb6989 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -221,6 +221,10 @@ static void do_sd_notify(enum daemon_status old_state,
 	} else if (new_state == DAEMON_CONFIGURE && startup_done)
 		sd_notify(0, "RELOADING=1");
 }
+#else
+static void do_sd_notify(__attribute__((unused)) enum daemon_status old_state,
+			 __attribute__((unused)) enum daemon_status new_state)
+{}
 #endif
 
 static void config_cleanup(__attribute__((unused)) void *arg)
@@ -266,19 +270,38 @@ enum daemon_status wait_for_state_change_if(enum daemon_status oldstate,
 	return st;
 }
 
+/* Don't access this variable without holding config_lock */
+static bool reconfigure_pending;
+
 /* must be called with config_lock held */
 static void __post_config_state(enum daemon_status state)
 {
 	if (state != running_state && running_state != DAEMON_SHUTDOWN) {
-#ifdef USE_SYSTEMD
 		enum daemon_status old_state = running_state;
-#endif
 
+		/*
+		 * Handle a pending reconfigure request.
+		 * DAEMON_IDLE is set from child() after reconfigure(),
+		 * or from checkerloop() after completing checkers.
+		 * In either case, child() will see DAEMON_CONFIGURE
+		 * again and start another reconfigure cycle.
+		 */
+		if (reconfigure_pending && state == DAEMON_IDLE &&
+		    (old_state == DAEMON_CONFIGURE ||
+		     old_state == DAEMON_RUNNING)) {
+			/*
+			 * notify systemd of transient idle state, lest systemd
+			 * thinks the reload lasts forever.
+			 */
+			do_sd_notify(old_state, DAEMON_IDLE);
+			old_state = DAEMON_IDLE;
+			state = DAEMON_CONFIGURE;
+		}
+		if (reconfigure_pending && state == DAEMON_CONFIGURE)
+			reconfigure_pending = false;
 		running_state = state;
 		pthread_cond_broadcast(&config_cond);
-#ifdef USE_SYSTEMD
 		do_sd_notify(old_state, state);
-#endif
 	}
 }
 
@@ -290,24 +313,48 @@ void post_config_state(enum daemon_status state)
 	pthread_cleanup_pop(1);
 }
 
-int set_config_state(enum daemon_status state)
+void schedule_reconfigure(void)
+{
+	pthread_mutex_lock(&config_lock);
+	pthread_cleanup_push(config_cleanup, NULL);
+	switch (running_state)
+	{
+	case DAEMON_SHUTDOWN:
+		break;
+	case DAEMON_IDLE:
+		__post_config_state(DAEMON_CONFIGURE);
+		break;
+	case DAEMON_CONFIGURE:
+	case DAEMON_RUNNING:
+		reconfigure_pending = true;
+		break;
+	default:
+		break;
+	}
+	pthread_cleanup_pop(1);
+}
+
+enum daemon_status set_config_state(enum daemon_status state)
 {
 	int rc = 0;
+	enum daemon_status st;
 
 	pthread_cleanup_push(config_cleanup, NULL);
 	pthread_mutex_lock(&config_lock);
-	if (running_state != state) {
 
-		if (running_state == DAEMON_SHUTDOWN)
-			rc = EINVAL;
-		else
-			rc = __wait_for_state_change(
-				running_state != DAEMON_IDLE, 1000);
-		if (!rc)
-			__post_config_state(state);
+	while (rc == 0 &&
+	       running_state != state &&
+	       running_state != DAEMON_SHUTDOWN &&
+	       running_state != DAEMON_IDLE) {
+		rc = pthread_cond_wait(&config_cond, &config_lock);
 	}
+
+	if (rc == 0 && running_state == DAEMON_IDLE && state != DAEMON_IDLE)
+		__post_config_state(state);
+	st = running_state;
+
 	pthread_cleanup_pop(1);
-	return rc;
+	return st;
 }
 
 struct config *get_multipath_config(void)
@@ -734,7 +781,7 @@ ev_add_map (char * dev, const char * alias, struct vectors * vecs)
 			if (delayed_reconfig &&
 			    !need_to_delay_reconfig(vecs)) {
 				condlog(2, "reconfigure (delayed)");
-				set_config_state(DAEMON_CONFIGURE);
+				schedule_reconfigure();
 				return 0;
 			}
 		}
@@ -1845,7 +1892,7 @@ missing_uev_wait_tick(struct vectors *vecs)
 	if (timed_out && delayed_reconfig &&
 	    !need_to_delay_reconfig(vecs)) {
 		condlog(2, "reconfigure (delayed)");
-		set_config_state(DAEMON_CONFIGURE);
+		schedule_reconfigure();
 	}
 }
 
@@ -2484,6 +2531,10 @@ checkerloop (void *ap)
 		int num_paths = 0, strict_timing, rc = 0;
 		unsigned int ticks = 0;
 
+		if (set_config_state(DAEMON_RUNNING) != DAEMON_RUNNING)
+			/* daemon shutdown */
+			break;
+
 		get_monotonic_time(&start_time);
 		if (start_time.tv_sec && last_time.tv_sec) {
 			timespecsub(&start_time, &last_time, &diff_time);
@@ -2499,13 +2550,6 @@ checkerloop (void *ap)
 		if (use_watchdog)
 			sd_notify(0, "WATCHDOG=1");
 #endif
-		rc = set_config_state(DAEMON_RUNNING);
-		if (rc == ETIMEDOUT) {
-			condlog(4, "timeout waiting for DAEMON_IDLE");
-			continue;
-		} else if (rc == EINVAL)
-			/* daemon shutdown */
-			break;
 
 		pthread_cleanup_push(cleanup_lock, &vecs->lock);
 		lock(&vecs->lock);
@@ -2833,7 +2877,7 @@ handle_signals(bool nonfatal)
 		return;
 	if (reconfig_sig) {
 		condlog(2, "reconfigure (signal)");
-		set_config_state(DAEMON_CONFIGURE);
+		schedule_reconfigure();
 	}
 	if (log_reset_sig) {
 		condlog(2, "reset log (signal)");
diff --git a/multipathd/main.h b/multipathd/main.h
index bc1f938..23ce919 100644
--- a/multipathd/main.h
+++ b/multipathd/main.h
@@ -37,6 +37,7 @@ void exit_daemon(void);
 const char * daemon_status(void);
 enum daemon_status wait_for_state_change_if(enum daemon_status oldstate,
 					    unsigned long ms);
+void schedule_reconfigure(void);
 int need_to_delay_reconfig (struct vectors *);
 int reconfigure (struct vectors *);
 int ev_add_path (struct path *, struct vectors *, int);
@@ -44,7 +45,7 @@ int ev_remove_path (struct path *, struct vectors *, int);
 int ev_add_map (char *, const char *, struct vectors *);
 int ev_remove_map (char *, char *, int, struct vectors *);
 int flush_map(struct multipath *, struct vectors *, int);
-int set_config_state(enum daemon_status);
+enum daemon_status set_config_state(enum daemon_status);
 void * mpath_alloc_prin_response(int prin_sa);
 int prin_do_scsi_ioctl(char *, int rq_servact, struct prin_resp * resp,
 		       int noisy);
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 08/35] multipathd: cli.h: formatting improvements
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (6 preceding siblings ...)
  2021-09-10 11:40 ` [dm-devel] [PATCH 07/35] multipathd: improve delayed reconfigure mwilck
@ 2021-09-10 11:40 ` mwilck
  2021-09-15 23:01   ` Benjamin Marzinski
  2021-09-10 11:40 ` [dm-devel] [PATCH 09/35] multipathd: cli_del_map: fix reply for delayed action mwilck
                   ` (26 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:40 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

No functional changes. Just make the code a little easier to read.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/cli.h | 82 ++++++++++++++++++++++++------------------------
 1 file changed, 41 insertions(+), 41 deletions(-)

diff --git a/multipathd/cli.h b/multipathd/cli.h
index fdfb9ae..1e8948a 100644
--- a/multipathd/cli.h
+++ b/multipathd/cli.h
@@ -4,83 +4,83 @@
 #include <stdint.h>
 
 enum {
-	__LIST,
+	__LIST,			/*  0 */
 	__ADD,
 	__DEL,
 	__SWITCH,
 	__SUSPEND,
-	__RESUME,
+	__RESUME,			/*  5 */
 	__REINSTATE,
 	__FAIL,
 	__RESIZE,
 	__RESET,
-	__RELOAD,
+	__RELOAD,			/* 10 */
 	__FORCEQ,
 	__DISABLEQ,
 	__RESTOREQ,
 	__PATHS,
-	__MAPS,
+	__MAPS,			/* 15 */
 	__PATH,
 	__MAP,
 	__GROUP,
 	__RECONFIGURE,
-	__DAEMON,
+	__DAEMON,			/* 20 */
 	__STATUS,
 	__STATS,
 	__TOPOLOGY,
 	__CONFIG,
-	__BLACKLIST,
+	__BLACKLIST,			/* 25 */
 	__DEVICES,
 	__RAW,
 	__WILDCARDS,
 	__QUIT,
-	__SHUTDOWN,
+	__SHUTDOWN,			/* 30 */
 	__GETPRSTATUS,
 	__SETPRSTATUS,
 	__UNSETPRSTATUS,
 	__FMT,
-	__JSON,
+	__JSON,			/* 35 */
 	__GETPRKEY,
 	__SETPRKEY,
 	__UNSETPRKEY,
 	__KEY,
-	__LOCAL,
+	__LOCAL,			/* 40 */
 	__SETMARGINAL,
 	__UNSETMARGINAL,
 };
 
-#define LIST		(1 << __LIST)
-#define ADD		(1 << __ADD)
-#define DEL		(1 << __DEL)
-#define SWITCH		(1 << __SWITCH)
-#define SUSPEND		(1 << __SUSPEND)
-#define RESUME		(1 << __RESUME)
-#define REINSTATE	(1 << __REINSTATE)
-#define FAIL		(1 << __FAIL)
-#define RESIZE		(1 << __RESIZE)
-#define RESET		(1 << __RESET)
-#define RELOAD		(1 << __RELOAD)
-#define FORCEQ		(1 << __FORCEQ)
-#define DISABLEQ	(1 << __DISABLEQ)
-#define RESTOREQ	(1 << __RESTOREQ)
-#define PATHS		(1 << __PATHS)
-#define MAPS		(1 << __MAPS)
-#define PATH		(1 << __PATH)
-#define MAP		(1 << __MAP)
-#define GROUP		(1 << __GROUP)
-#define RECONFIGURE	(1 << __RECONFIGURE)
-#define DAEMON		(1 << __DAEMON)
-#define STATUS		(1 << __STATUS)
-#define STATS		(1 << __STATS)
-#define TOPOLOGY	(1 << __TOPOLOGY)
-#define CONFIG		(1 << __CONFIG)
-#define BLACKLIST	(1 << __BLACKLIST)
-#define DEVICES		(1 << __DEVICES)
-#define RAW		(1 << __RAW)
-#define COUNT		(1 << __COUNT)
-#define WILDCARDS	(1 << __WILDCARDS)
-#define QUIT		(1 << __QUIT)
-#define SHUTDOWN	(1 << __SHUTDOWN)
+#define LIST		(1ULL << __LIST)
+#define ADD		(1ULL << __ADD)
+#define DEL		(1ULL << __DEL)
+#define SWITCH		(1ULL << __SWITCH)
+#define SUSPEND	(1ULL << __SUSPEND)
+#define RESUME		(1ULL << __RESUME)
+#define REINSTATE	(1ULL << __REINSTATE)
+#define FAIL		(1ULL << __FAIL)
+#define RESIZE		(1ULL << __RESIZE)
+#define RESET		(1ULL << __RESET)
+#define RELOAD		(1ULL << __RELOAD)
+#define FORCEQ		(1ULL << __FORCEQ)
+#define DISABLEQ	(1ULL << __DISABLEQ)
+#define RESTOREQ	(1ULL << __RESTOREQ)
+#define PATHS		(1ULL << __PATHS)
+#define MAPS		(1ULL << __MAPS)
+#define PATH		(1ULL << __PATH)
+#define MAP		(1ULL << __MAP)
+#define GROUP		(1ULL << __GROUP)
+#define RECONFIGURE	(1ULL << __RECONFIGURE)
+#define DAEMON		(1ULL << __DAEMON)
+#define STATUS		(1ULL << __STATUS)
+#define STATS		(1ULL << __STATS)
+#define TOPOLOGY	(1ULL << __TOPOLOGY)
+#define CONFIG		(1ULL << __CONFIG)
+#define BLACKLIST	(1ULL << __BLACKLIST)
+#define DEVICES	(1ULL << __DEVICES)
+#define RAW		(1ULL << __RAW)
+#define COUNT		(1ULL << __COUNT)
+#define WILDCARDS	(1ULL << __WILDCARDS)
+#define QUIT		(1ULL << __QUIT)
+#define SHUTDOWN	(1ULL << __SHUTDOWN)
 #define GETPRSTATUS	(1ULL << __GETPRSTATUS)
 #define SETPRSTATUS	(1ULL << __SETPRSTATUS)
 #define UNSETPRSTATUS	(1ULL << __UNSETPRSTATUS)
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 09/35] multipathd: cli_del_map: fix reply for delayed action
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (7 preceding siblings ...)
  2021-09-10 11:40 ` [dm-devel] [PATCH 08/35] multipathd: cli.h: formatting improvements mwilck
@ 2021-09-10 11:40 ` mwilck
  2021-09-15 23:40   ` Benjamin Marzinski
  2021-09-10 11:40 ` [dm-devel] [PATCH 10/35] multipathd: add prototype for cli_handler functions mwilck
                   ` (25 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:40 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

Return code 2 from ev_remove_map means that a delayed remove has
been started, which is not the same as failure.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/cli_handlers.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
index 44f76ee..2e4b239 100644
--- a/multipathd/cli_handlers.c
+++ b/multipathd/cli_handlers.c
@@ -783,6 +783,9 @@ cli_del_map (void * v, char ** reply, int * len, void * data)
 		return 1;
 	}
 	rc = ev_remove_map(param, alias, minor, vecs);
+	if (rc == 2)
+		*reply = strdup("delayed");
+
 	FREE(alias);
 	return rc;
 }
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 10/35] multipathd: add prototype for cli_handler functions
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (8 preceding siblings ...)
  2021-09-10 11:40 ` [dm-devel] [PATCH 09/35] multipathd: cli_del_map: fix reply for delayed action mwilck
@ 2021-09-10 11:40 ` mwilck
  2021-09-15 23:53   ` Benjamin Marzinski
  2021-09-10 11:40 ` [dm-devel] [PATCH 11/35] multipathd: make all cli_handlers static mwilck
                   ` (24 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:40 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

Use a typedef instead of spelling out the function type everywhere.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/cli.c |  6 +++---
 multipathd/cli.h | 10 ++++++----
 2 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/multipathd/cli.c b/multipathd/cli.c
index 4d6c37c..5213813 100644
--- a/multipathd/cli.c
+++ b/multipathd/cli.c
@@ -65,7 +65,7 @@ out:
 }
 
 int
-add_handler (uint64_t fp, int (*fn)(void *, char **, int *, void *))
+add_handler (uint64_t fp, cli_handler *fn)
 {
 	struct handler * h;
 
@@ -100,7 +100,7 @@ find_handler (uint64_t fp)
 }
 
 int
-set_handler_callback (uint64_t fp, int (*fn)(void *, char **, int *, void *))
+set_handler_callback (uint64_t fp, cli_handler *fn)
 {
 	struct handler * h = find_handler(fp);
 
@@ -112,7 +112,7 @@ set_handler_callback (uint64_t fp, int (*fn)(void *, char **, int *, void *))
 }
 
 int
-set_unlocked_handler_callback (uint64_t fp,int (*fn)(void *, char **, int *, void *))
+set_unlocked_handler_callback (uint64_t fp, cli_handler *fn)
 {
 	struct handler * h = find_handler(fp);
 
diff --git a/multipathd/cli.h b/multipathd/cli.h
index 1e8948a..3dac1b4 100644
--- a/multipathd/cli.h
+++ b/multipathd/cli.h
@@ -124,16 +124,18 @@ struct key {
 	int has_param;
 };
 
+typedef int (cli_handler)(void *keywords, char **reply, int *len, void *data);
+
 struct handler {
 	uint64_t fingerprint;
 	int locked;
-	int (*fn)(void *, char **, int *, void *);
+	cli_handler *fn;
 };
 
 int alloc_handlers (void);
-int add_handler (uint64_t fp, int (*fn)(void *, char **, int *, void *));
-int set_handler_callback (uint64_t fp, int (*fn)(void *, char **, int *, void *));
-int set_unlocked_handler_callback (uint64_t fp, int (*fn)(void *, char **, int *, void *));
+int add_handler (uint64_t fp, cli_handler *fn);
+int set_handler_callback (uint64_t fp, cli_handler *fn);
+int set_unlocked_handler_callback (uint64_t fp, cli_handler *fn);
 int parse_cmd (char * cmd, char ** reply, int * len, void *, int);
 int load_keys (void);
 char * get_keyparam (vector v, uint64_t code);
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 11/35] multipathd: make all cli_handlers static
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (9 preceding siblings ...)
  2021-09-10 11:40 ` [dm-devel] [PATCH 10/35] multipathd: add prototype for cli_handler functions mwilck
@ 2021-09-10 11:40 ` mwilck
  2021-09-15 23:53   ` Benjamin Marzinski
  2021-09-10 11:40 ` [dm-devel] [PATCH 12/35] multipathd: add and set cli_handlers in a single step mwilck
                   ` (23 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:40 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

The cli_handler functions are only called from the handler table and
need not be exported.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/cli_handlers.c | 214 ++++++++++++++++++++++----------------
 multipathd/cli_handlers.h |  61 ++---------
 multipathd/main.c         |  58 +----------
 3 files changed, 134 insertions(+), 199 deletions(-)

diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
index 2e4b239..1a9c822 100644
--- a/multipathd/cli_handlers.c
+++ b/multipathd/cli_handlers.c
@@ -39,7 +39,7 @@
 		*(__len) = *(__rep) ? sizeof(string_literal) : 0;	\
 	} while (0)
 
-int
+static int
 show_paths (char ** r, int * len, struct vectors * vecs, char * style,
 	    int pretty)
 {
@@ -70,7 +70,7 @@ show_paths (char ** r, int * len, struct vectors * vecs, char * style,
 	return 0;
 }
 
-int
+static int
 show_path (char ** r, int * len, struct vectors * vecs, struct path *pp,
 	   char * style)
 {
@@ -85,7 +85,7 @@ show_path (char ** r, int * len, struct vectors * vecs, struct path *pp,
 	return 0;
 }
 
-int
+static int
 show_map_topology (char ** r, int * len, struct multipath * mpp,
 		   struct vectors * vecs)
 {
@@ -102,7 +102,7 @@ show_map_topology (char ** r, int * len, struct multipath * mpp,
 	return 0;
 }
 
-int
+static int
 show_maps_topology (char ** r, int * len, struct vectors * vecs)
 {
 	STRBUF_ON_STACK(reply);
@@ -128,7 +128,7 @@ show_maps_topology (char ** r, int * len, struct vectors * vecs)
 	return 0;
 }
 
-int
+static int
 show_maps_json (char ** r, int * len, struct vectors * vecs)
 {
 	STRBUF_ON_STACK(reply);
@@ -149,7 +149,7 @@ show_maps_json (char ** r, int * len, struct vectors * vecs)
 	return 0;
 }
 
-int
+static int
 show_map_json (char ** r, int * len, struct multipath * mpp,
 		   struct vectors * vecs)
 {
@@ -194,7 +194,7 @@ reset_stats(struct multipath * mpp)
 	mpp->stat_map_failures = 0;
 }
 
-int
+static int
 cli_list_config (void * v, char ** reply, int * len, void * data)
 {
 	condlog(3, "list config (operator)");
@@ -207,7 +207,7 @@ static void v_free(void *x)
 	vector_free(x);
 }
 
-int
+static int
 cli_list_config_local (void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -223,7 +223,7 @@ cli_list_config_local (void * v, char ** reply, int * len, void * data)
 	return ret;
 }
 
-int
+static int
 cli_list_paths (void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -233,7 +233,7 @@ cli_list_paths (void * v, char ** reply, int * len, void * data)
 	return show_paths(reply, len, vecs, PRINT_PATH_CHECKER, 1);
 }
 
-int
+static int
 cli_list_paths_fmt (void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -244,7 +244,7 @@ cli_list_paths_fmt (void * v, char ** reply, int * len, void * data)
 	return show_paths(reply, len, vecs, fmt, 1);
 }
 
-int
+static int
 cli_list_paths_raw (void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -255,7 +255,7 @@ cli_list_paths_raw (void * v, char ** reply, int * len, void * data)
 	return show_paths(reply, len, vecs, fmt, 0);
 }
 
-int
+static int
 cli_list_path (void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -272,7 +272,7 @@ cli_list_path (void * v, char ** reply, int * len, void * data)
 	return show_path(reply, len, vecs, pp, "%o");
 }
 
-int
+static int
 cli_list_map_topology (void * v, char ** reply, int * len, void * data)
 {
 	struct multipath * mpp;
@@ -291,7 +291,7 @@ cli_list_map_topology (void * v, char ** reply, int * len, void * data)
 	return show_map_topology(reply, len, mpp, vecs);
 }
 
-int
+static int
 cli_list_maps_topology (void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -301,7 +301,7 @@ cli_list_maps_topology (void * v, char ** reply, int * len, void * data)
 	return show_maps_topology(reply, len, vecs);
 }
 
-int
+static int
 cli_list_map_json (void * v, char ** reply, int * len, void * data)
 {
 	struct multipath * mpp;
@@ -320,7 +320,7 @@ cli_list_map_json (void * v, char ** reply, int * len, void * data)
 	return show_map_json(reply, len, mpp, vecs);
 }
 
-int
+static int
 cli_list_maps_json (void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -330,7 +330,7 @@ cli_list_maps_json (void * v, char ** reply, int * len, void * data)
 	return show_maps_json(reply, len, vecs);
 }
 
-int
+static int
 cli_list_wildcards (void * v, char ** reply, int * len, void * data)
 {
 	STRBUF_ON_STACK(buf);
@@ -343,7 +343,7 @@ cli_list_wildcards (void * v, char ** reply, int * len, void * data)
 	return 0;
 }
 
-int
+static int
 show_status (char ** r, int *len, struct vectors * vecs)
 {
 	STRBUF_ON_STACK(reply);
@@ -356,7 +356,7 @@ show_status (char ** r, int *len, struct vectors * vecs)
 	return 0;
 }
 
-int
+static int
 show_daemon (char ** r, int *len)
 {
 	STRBUF_ON_STACK(reply);
@@ -370,7 +370,7 @@ show_daemon (char ** r, int *len)
 	return 0;
 }
 
-int
+static int
 show_map (char ** r, int *len, struct multipath * mpp, char * style,
 	  int pretty)
 {
@@ -384,7 +384,7 @@ show_map (char ** r, int *len, struct multipath * mpp, char * style,
 	return 0;
 }
 
-int
+static int
 show_maps (char ** r, int *len, struct vectors * vecs, char * style,
 	   int pretty)
 {
@@ -419,7 +419,7 @@ show_maps (char ** r, int *len, struct vectors * vecs, char * style,
 	return 0;
 }
 
-int
+static int
 cli_list_maps_fmt (void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -430,7 +430,7 @@ cli_list_maps_fmt (void * v, char ** reply, int * len, void * data)
 	return show_maps(reply, len, vecs, fmt, 1);
 }
 
-int
+static int
 cli_list_maps_raw (void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -441,7 +441,7 @@ cli_list_maps_raw (void * v, char ** reply, int * len, void * data)
 	return show_maps(reply, len, vecs, fmt, 0);
 }
 
-int
+static int
 cli_list_map_fmt (void * v, char ** reply, int * len, void * data)
 {
 	struct multipath * mpp;
@@ -461,27 +461,7 @@ cli_list_map_fmt (void * v, char ** reply, int * len, void * data)
 	return show_map(reply, len, mpp, fmt, 1);
 }
 
-int
-cli_list_map_raw (void * v, char ** reply, int * len, void * data)
-{
-	struct multipath * mpp;
-	struct vectors * vecs = (struct vectors *)data;
-	char * param = get_keyparam(v, MAP);
-	char * fmt = get_keyparam(v, FMT);
-
-	param = convert_dev(param, 0);
-	get_path_layout(vecs->pathvec, 0);
-	get_multipath_layout(vecs->mpvec, 1);
-	mpp = find_mp_by_str(vecs->mpvec, param);
-	if (!mpp)
-		return 1;
-
-	condlog(3, "list map %s fmt %s (operator)", param, fmt);
-
-	return show_map(reply, len, mpp, fmt, 0);
-}
-
-int
+static int
 cli_list_maps (void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -491,7 +471,7 @@ cli_list_maps (void * v, char ** reply, int * len, void * data)
 	return show_maps(reply, len, vecs, PRINT_MAP_NAMES, 1);
 }
 
-int
+static int
 cli_list_status (void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -501,7 +481,7 @@ cli_list_status (void * v, char ** reply, int * len, void * data)
 	return show_status(reply, len, vecs);
 }
 
-int
+static int
 cli_list_maps_status (void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -511,7 +491,7 @@ cli_list_maps_status (void * v, char ** reply, int * len, void * data)
 	return show_maps(reply, len, vecs, PRINT_MAP_STATUS, 1);
 }
 
-int
+static int
 cli_list_maps_stats (void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -521,7 +501,7 @@ cli_list_maps_stats (void * v, char ** reply, int * len, void * data)
 	return show_maps(reply, len, vecs, PRINT_MAP_STATS, 1);
 }
 
-int
+static int
 cli_list_daemon (void * v, char ** reply, int * len, void * data)
 {
 	condlog(3, "list daemon (operator)");
@@ -529,7 +509,7 @@ cli_list_daemon (void * v, char ** reply, int * len, void * data)
 	return show_daemon(reply, len);
 }
 
-int
+static int
 cli_reset_maps_stats (void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -544,7 +524,7 @@ cli_reset_maps_stats (void * v, char ** reply, int * len, void * data)
 	return 0;
 }
 
-int
+static int
 cli_reset_map_stats (void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -562,7 +542,7 @@ cli_reset_map_stats (void * v, char ** reply, int * len, void * data)
 	return 0;
 }
 
-int
+static int
 cli_add_path (void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -678,7 +658,7 @@ blacklisted:
 	return 0;
 }
 
-int
+static int
 cli_del_path (void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -701,7 +681,7 @@ cli_del_path (void * v, char ** reply, int * len, void * data)
 	return (ret == REMOVE_PATH_FAILURE);
 }
 
-int
+static int
 cli_add_map (void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -761,7 +741,7 @@ cli_add_map (void * v, char ** reply, int * len, void * data)
 	return rc;
 }
 
-int
+static int
 cli_del_map (void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -790,7 +770,7 @@ cli_del_map (void * v, char ** reply, int * len, void * data)
 	return rc;
 }
 
-int
+static int
 cli_del_maps (void *v, char **reply, int *len, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -809,7 +789,7 @@ cli_del_maps (void *v, char **reply, int *len, void *data)
 	return ret;
 }
 
-int
+static int
 cli_reload(void *v, char **reply, int *len, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -837,7 +817,7 @@ cli_reload(void *v, char **reply, int *len, void *data)
 	return reload_and_sync_map(mpp, vecs, 0);
 }
 
-int resize_map(struct multipath *mpp, unsigned long long size,
+static int resize_map(struct multipath *mpp, unsigned long long size,
 	       struct vectors * vecs)
 {
 	char *params __attribute__((cleanup(cleanup_charp))) = NULL;
@@ -862,7 +842,7 @@ int resize_map(struct multipath *mpp, unsigned long long size,
 	return 0;
 }
 
-int
+static int
 cli_resize(void *v, char **reply, int *len, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -927,7 +907,7 @@ cli_resize(void *v, char **reply, int *len, void *data)
 	return 0;
 }
 
-int
+static int
 cli_force_no_daemon_q(void * v, char ** reply, int * len, void * data)
 {
 	struct config *conf;
@@ -940,7 +920,7 @@ cli_force_no_daemon_q(void * v, char ** reply, int * len, void * data)
 	return 0;
 }
 
-int
+static int
 cli_restore_no_daemon_q(void * v, char ** reply, int * len, void * data)
 {
 	struct config *conf;
@@ -953,7 +933,7 @@ cli_restore_no_daemon_q(void * v, char ** reply, int * len, void * data)
 	return 0;
 }
 
-int
+static int
 cli_restore_queueing(void *v, char **reply, int *len, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -994,7 +974,7 @@ cli_restore_queueing(void *v, char **reply, int *len, void *data)
 	return 0;
 }
 
-int
+static int
 cli_restore_all_queueing(void *v, char **reply, int *len, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -1016,7 +996,7 @@ cli_restore_all_queueing(void *v, char **reply, int *len, void *data)
 	return 0;
 }
 
-int
+static int
 cli_disable_queueing(void *v, char **reply, int *len, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -1045,7 +1025,7 @@ cli_disable_queueing(void *v, char **reply, int *len, void *data)
 	return 0;
 }
 
-int
+static int
 cli_disable_all_queueing(void *v, char **reply, int *len, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -1064,7 +1044,7 @@ cli_disable_all_queueing(void *v, char **reply, int *len, void *data)
 	return 0;
 }
 
-int
+static int
 cli_switch_group(void * v, char ** reply, int * len, void * data)
 {
 	char * mapname = get_keyparam(v, MAP);
@@ -1076,7 +1056,7 @@ cli_switch_group(void * v, char ** reply, int * len, void * data)
 	return dm_switchgroup(mapname, groupnum);
 }
 
-int
+static int
 cli_reconfigure(void * v, char ** reply, int * len, void * data)
 {
 	condlog(2, "reconfigure (operator)");
@@ -1085,7 +1065,7 @@ cli_reconfigure(void * v, char ** reply, int * len, void * data)
 	return 0;
 }
 
-int
+static int
 cli_suspend(void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -1115,7 +1095,7 @@ cli_suspend(void * v, char ** reply, int * len, void * data)
 	return 0;
 }
 
-int
+static int
 cli_resume(void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -1147,7 +1127,7 @@ cli_resume(void * v, char ** reply, int * len, void * data)
 	return 0;
 }
 
-int
+static int
 cli_reinstate(void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -1170,7 +1150,7 @@ cli_reinstate(void * v, char ** reply, int * len, void * data)
 	return dm_reinstate_path(pp->mpp->alias, pp->dev_t);
 }
 
-int
+static int
 cli_reassign (void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -1194,7 +1174,7 @@ cli_reassign (void * v, char ** reply, int * len, void * data)
 	return 0;
 }
 
-int
+static int
 cli_fail(void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -1223,7 +1203,7 @@ cli_fail(void * v, char ** reply, int * len, void * data)
 	return r;
 }
 
-int
+static int
 show_blacklist (char ** r, int * len)
 {
 	STRBUF_ON_STACK(reply);
@@ -1243,7 +1223,7 @@ show_blacklist (char ** r, int * len)
 	return 0;
 }
 
-int
+static int
 cli_list_blacklist (void * v, char ** reply, int * len, void * data)
 {
 	condlog(3, "list blacklist (operator)");
@@ -1251,7 +1231,7 @@ cli_list_blacklist (void * v, char ** reply, int * len, void * data)
 	return show_blacklist(reply, len);
 }
 
-int
+static int
 show_devices (char ** r, int * len, struct vectors *vecs)
 {
 	STRBUF_ON_STACK(reply);
@@ -1272,7 +1252,7 @@ show_devices (char ** r, int * len, struct vectors *vecs)
 	return 0;
 }
 
-int
+static int
 cli_list_devices (void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
@@ -1282,13 +1262,13 @@ cli_list_devices (void * v, char ** reply, int * len, void * data)
 	return show_devices(reply, len, vecs);
 }
 
-int
+static int
 cli_quit (void * v, char ** reply, int * len, void * data)
 {
 	return 0;
 }
 
-int
+static int
 cli_shutdown (void * v, char ** reply, int * len, void * data)
 {
 	condlog(3, "shutdown (operator)");
@@ -1296,7 +1276,7 @@ cli_shutdown (void * v, char ** reply, int * len, void * data)
 	return 0;
 }
 
-int
+static int
 cli_getprstatus (void * v, char ** reply, int * len, void * data)
 {
 	struct multipath * mpp;
@@ -1321,7 +1301,7 @@ cli_getprstatus (void * v, char ** reply, int * len, void * data)
 	return 0;
 }
 
-int
+static int
 cli_setprstatus(void * v, char ** reply, int * len, void * data)
 {
 	struct multipath * mpp;
@@ -1344,7 +1324,7 @@ cli_setprstatus(void * v, char ** reply, int * len, void * data)
 	return 0;
 }
 
-int
+static int
 cli_unsetprstatus(void * v, char ** reply, int * len, void * data)
 {
 	struct multipath * mpp;
@@ -1366,7 +1346,7 @@ cli_unsetprstatus(void * v, char ** reply, int * len, void * data)
 	return 0;
 }
 
-int
+static int
 cli_getprkey(void * v, char ** reply, int * len, void * data)
 {
 	struct multipath * mpp;
@@ -1398,7 +1378,7 @@ cli_getprkey(void * v, char ** reply, int * len, void * data)
 	return 0;
 }
 
-int
+static int
 cli_unsetprkey(void * v, char ** reply, int * len, void * data)
 {
 	struct multipath * mpp;
@@ -1422,7 +1402,7 @@ cli_unsetprkey(void * v, char ** reply, int * len, void * data)
 	return ret;
 }
 
-int
+static int
 cli_setprkey(void * v, char ** reply, int * len, void * data)
 {
 	struct multipath * mpp;
@@ -1454,7 +1434,7 @@ cli_setprkey(void * v, char ** reply, int * len, void * data)
 	return ret;
 }
 
-int cli_set_marginal(void * v, char ** reply, int * len, void * data)
+static int cli_set_marginal(void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	char * param = get_keyparam(v, PATH);
@@ -1481,7 +1461,7 @@ int cli_set_marginal(void * v, char ** reply, int * len, void * data)
 	return reload_and_sync_map(pp->mpp, vecs, 0);
 }
 
-int cli_unset_marginal(void * v, char ** reply, int * len, void * data)
+static int cli_unset_marginal(void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	char * param = get_keyparam(v, PATH);
@@ -1508,7 +1488,7 @@ int cli_unset_marginal(void * v, char ** reply, int * len, void * data)
 	return reload_and_sync_map(pp->mpp, vecs, 0);
 }
 
-int cli_unset_all_marginal(void * v, char ** reply, int * len, void * data)
+static int cli_unset_all_marginal(void * v, char ** reply, int * len, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	char * mapname = get_keyparam(v, MAP);
@@ -1544,3 +1524,63 @@ int cli_unset_all_marginal(void * v, char ** reply, int * len, void * data)
 
 	return reload_and_sync_map(mpp, vecs, 0);
 }
+
+void init_handler_callbacks(void)
+{
+	set_handler_callback(LIST+PATHS, cli_list_paths);
+	set_handler_callback(LIST+PATHS+FMT, cli_list_paths_fmt);
+	set_handler_callback(LIST+PATHS+RAW+FMT, cli_list_paths_raw);
+	set_handler_callback(LIST+PATH, cli_list_path);
+	set_handler_callback(LIST+MAPS, cli_list_maps);
+	set_handler_callback(LIST+STATUS, cli_list_status);
+	set_unlocked_handler_callback(LIST+DAEMON, cli_list_daemon);
+	set_handler_callback(LIST+MAPS+STATUS, cli_list_maps_status);
+	set_handler_callback(LIST+MAPS+STATS, cli_list_maps_stats);
+	set_handler_callback(LIST+MAPS+FMT, cli_list_maps_fmt);
+	set_handler_callback(LIST+MAPS+RAW+FMT, cli_list_maps_raw);
+	set_handler_callback(LIST+MAPS+TOPOLOGY, cli_list_maps_topology);
+	set_handler_callback(LIST+TOPOLOGY, cli_list_maps_topology);
+	set_handler_callback(LIST+MAPS+JSON, cli_list_maps_json);
+	set_handler_callback(LIST+MAP+TOPOLOGY, cli_list_map_topology);
+	set_handler_callback(LIST+MAP+FMT, cli_list_map_fmt);
+	set_handler_callback(LIST+MAP+RAW+FMT, cli_list_map_fmt);
+	set_handler_callback(LIST+MAP+JSON, cli_list_map_json);
+	set_handler_callback(LIST+CONFIG+LOCAL, cli_list_config_local);
+	set_handler_callback(LIST+CONFIG, cli_list_config);
+	set_handler_callback(LIST+BLACKLIST, cli_list_blacklist);
+	set_handler_callback(LIST+DEVICES, cli_list_devices);
+	set_handler_callback(LIST+WILDCARDS, cli_list_wildcards);
+	set_handler_callback(RESET+MAPS+STATS, cli_reset_maps_stats);
+	set_handler_callback(RESET+MAP+STATS, cli_reset_map_stats);
+	set_handler_callback(ADD+PATH, cli_add_path);
+	set_handler_callback(DEL+PATH, cli_del_path);
+	set_handler_callback(ADD+MAP, cli_add_map);
+	set_handler_callback(DEL+MAP, cli_del_map);
+	set_handler_callback(DEL+MAPS, cli_del_maps);
+	set_handler_callback(SWITCH+MAP+GROUP, cli_switch_group);
+	set_unlocked_handler_callback(RECONFIGURE, cli_reconfigure);
+	set_handler_callback(SUSPEND+MAP, cli_suspend);
+	set_handler_callback(RESUME+MAP, cli_resume);
+	set_handler_callback(RESIZE+MAP, cli_resize);
+	set_handler_callback(RELOAD+MAP, cli_reload);
+	set_handler_callback(RESET+MAP, cli_reassign);
+	set_handler_callback(REINSTATE+PATH, cli_reinstate);
+	set_handler_callback(FAIL+PATH, cli_fail);
+	set_handler_callback(DISABLEQ+MAP, cli_disable_queueing);
+	set_handler_callback(RESTOREQ+MAP, cli_restore_queueing);
+	set_handler_callback(DISABLEQ+MAPS, cli_disable_all_queueing);
+	set_handler_callback(RESTOREQ+MAPS, cli_restore_all_queueing);
+	set_unlocked_handler_callback(QUIT, cli_quit);
+	set_unlocked_handler_callback(SHUTDOWN, cli_shutdown);
+	set_handler_callback(GETPRSTATUS+MAP, cli_getprstatus);
+	set_handler_callback(SETPRSTATUS+MAP, cli_setprstatus);
+	set_handler_callback(UNSETPRSTATUS+MAP, cli_unsetprstatus);
+	set_handler_callback(FORCEQ+DAEMON, cli_force_no_daemon_q);
+	set_handler_callback(RESTOREQ+DAEMON, cli_restore_no_daemon_q);
+	set_handler_callback(GETPRKEY+MAP, cli_getprkey);
+	set_handler_callback(SETPRKEY+MAP+KEY, cli_setprkey);
+	set_handler_callback(UNSETPRKEY+MAP, cli_unsetprkey);
+	set_handler_callback(SETMARGINAL+PATH, cli_set_marginal);
+	set_handler_callback(UNSETMARGINAL+PATH, cli_unset_marginal);
+	set_handler_callback(UNSETMARGINAL+MAP, cli_unset_all_marginal);
+}
diff --git a/multipathd/cli_handlers.h b/multipathd/cli_handlers.h
index 6f57b42..7eaf847 100644
--- a/multipathd/cli_handlers.h
+++ b/multipathd/cli_handlers.h
@@ -1,55 +1,6 @@
-int cli_list_paths (void * v, char ** reply, int * len, void * data);
-int cli_list_paths_fmt (void * v, char ** reply, int * len, void * data);
-int cli_list_paths_raw (void * v, char ** reply, int * len, void * data);
-int cli_list_path (void * v, char ** reply, int * len, void * data);
-int cli_list_status (void * v, char ** reply, int * len, void * data);
-int cli_list_daemon (void * v, char ** reply, int * len, void * data);
-int cli_list_maps (void * v, char ** reply, int * len, void * data);
-int cli_list_maps_fmt (void * v, char ** reply, int * len, void * data);
-int cli_list_maps_raw (void * v, char ** reply, int * len, void * data);
-int cli_list_map_fmt (void * v, char ** reply, int * len, void * data);
-int cli_list_map_raw (void * v, char ** reply, int * len, void * data);
-int cli_list_maps_status (void * v, char ** reply, int * len, void * data);
-int cli_list_maps_stats (void * v, char ** reply, int * len, void * data);
-int cli_list_map_topology (void * v, char ** reply, int * len, void * data);
-int cli_list_maps_topology (void * v, char ** reply, int * len, void * data);
-int cli_list_map_json (void * v, char ** reply, int * len, void * data);
-int cli_list_maps_json (void * v, char ** reply, int * len, void * data);
-int cli_list_config (void * v, char ** reply, int * len, void * data);
-int cli_list_config_local (void * v, char ** reply, int * len, void * data);
-int cli_list_blacklist (void * v, char ** reply, int * len, void * data);
-int cli_list_devices (void * v, char ** reply, int * len, void * data);
-int cli_list_wildcards (void * v, char ** reply, int * len, void * data);
-int cli_reset_maps_stats (void * v, char ** reply, int * len, void * data);
-int cli_reset_map_stats (void * v, char ** reply, int * len, void * data);
-int cli_add_path (void * v, char ** reply, int * len, void * data);
-int cli_del_path (void * v, char ** reply, int * len, void * data);
-int cli_add_map (void * v, char ** reply, int * len, void * data);
-int cli_del_map (void * v, char ** reply, int * len, void * data);
-int cli_del_maps (void * v, char ** reply, int * len, void * data);
-int cli_switch_group(void * v, char ** reply, int * len, void * data);
-int cli_reconfigure(void * v, char ** reply, int * len, void * data);
-int cli_resize(void * v, char ** reply, int * len, void * data);
-int cli_reload(void * v, char ** reply, int * len, void * data);
-int cli_disable_queueing(void * v, char ** reply, int * len, void * data);
-int cli_disable_all_queueing(void * v, char ** reply, int * len, void * data);
-int cli_restore_queueing(void * v, char ** reply, int * len, void * data);
-int cli_restore_all_queueing(void * v, char ** reply, int * len, void * data);
-int cli_suspend(void * v, char ** reply, int * len, void * data);
-int cli_resume(void * v, char ** reply, int * len, void * data);
-int cli_reinstate(void * v, char ** reply, int * len, void * data);
-int cli_fail(void * v, char ** reply, int * len, void * data);
-int cli_force_no_daemon_q(void * v, char ** reply, int * len, void * data);
-int cli_restore_no_daemon_q(void * v, char ** reply, int * len, void * data);
-int cli_quit(void * v, char ** reply, int * len, void * data);
-int cli_shutdown(void * v, char ** reply, int * len, void * data);
-int cli_reassign (void * v, char ** reply, int * len, void * data);
-int cli_getprstatus(void * v, char ** reply, int * len, void * data);
-int cli_setprstatus(void * v, char ** reply, int * len, void * data);
-int cli_unsetprstatus(void * v, char ** reply, int * len, void * data);
-int cli_getprkey(void * v, char ** reply, int * len, void * data);
-int cli_setprkey(void * v, char ** reply, int * len, void * data);
-int cli_unsetprkey(void * v, char ** reply, int * len, void * data);
-int cli_set_marginal(void * v, char ** reply, int * len, void * data);
-int cli_unset_marginal(void * v, char ** reply, int * len, void * data);
-int cli_unset_all_marginal(void * v, char ** reply, int * len, void * data);
+#ifndef _CLI_HANDLERS_H
+#define _CLI_HANDLERS_H
+
+void init_handler_callbacks(void);
+
+#endif
diff --git a/multipathd/main.c b/multipathd/main.c
index 5fb6989..bda51c9 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -1693,63 +1693,7 @@ uxlsnrloop (void * ap)
 	/* Tell main thread that thread has started */
 	post_config_state(DAEMON_CONFIGURE);
 
-	set_handler_callback(LIST+PATHS, cli_list_paths);
-	set_handler_callback(LIST+PATHS+FMT, cli_list_paths_fmt);
-	set_handler_callback(LIST+PATHS+RAW+FMT, cli_list_paths_raw);
-	set_handler_callback(LIST+PATH, cli_list_path);
-	set_handler_callback(LIST+MAPS, cli_list_maps);
-	set_handler_callback(LIST+STATUS, cli_list_status);
-	set_unlocked_handler_callback(LIST+DAEMON, cli_list_daemon);
-	set_handler_callback(LIST+MAPS+STATUS, cli_list_maps_status);
-	set_handler_callback(LIST+MAPS+STATS, cli_list_maps_stats);
-	set_handler_callback(LIST+MAPS+FMT, cli_list_maps_fmt);
-	set_handler_callback(LIST+MAPS+RAW+FMT, cli_list_maps_raw);
-	set_handler_callback(LIST+MAPS+TOPOLOGY, cli_list_maps_topology);
-	set_handler_callback(LIST+TOPOLOGY, cli_list_maps_topology);
-	set_handler_callback(LIST+MAPS+JSON, cli_list_maps_json);
-	set_handler_callback(LIST+MAP+TOPOLOGY, cli_list_map_topology);
-	set_handler_callback(LIST+MAP+FMT, cli_list_map_fmt);
-	set_handler_callback(LIST+MAP+RAW+FMT, cli_list_map_fmt);
-	set_handler_callback(LIST+MAP+JSON, cli_list_map_json);
-	set_handler_callback(LIST+CONFIG+LOCAL, cli_list_config_local);
-	set_handler_callback(LIST+CONFIG, cli_list_config);
-	set_handler_callback(LIST+BLACKLIST, cli_list_blacklist);
-	set_handler_callback(LIST+DEVICES, cli_list_devices);
-	set_handler_callback(LIST+WILDCARDS, cli_list_wildcards);
-	set_handler_callback(RESET+MAPS+STATS, cli_reset_maps_stats);
-	set_handler_callback(RESET+MAP+STATS, cli_reset_map_stats);
-	set_handler_callback(ADD+PATH, cli_add_path);
-	set_handler_callback(DEL+PATH, cli_del_path);
-	set_handler_callback(ADD+MAP, cli_add_map);
-	set_handler_callback(DEL+MAP, cli_del_map);
-	set_handler_callback(DEL+MAPS, cli_del_maps);
-	set_handler_callback(SWITCH+MAP+GROUP, cli_switch_group);
-	set_unlocked_handler_callback(RECONFIGURE, cli_reconfigure);
-	set_handler_callback(SUSPEND+MAP, cli_suspend);
-	set_handler_callback(RESUME+MAP, cli_resume);
-	set_handler_callback(RESIZE+MAP, cli_resize);
-	set_handler_callback(RELOAD+MAP, cli_reload);
-	set_handler_callback(RESET+MAP, cli_reassign);
-	set_handler_callback(REINSTATE+PATH, cli_reinstate);
-	set_handler_callback(FAIL+PATH, cli_fail);
-	set_handler_callback(DISABLEQ+MAP, cli_disable_queueing);
-	set_handler_callback(RESTOREQ+MAP, cli_restore_queueing);
-	set_handler_callback(DISABLEQ+MAPS, cli_disable_all_queueing);
-	set_handler_callback(RESTOREQ+MAPS, cli_restore_all_queueing);
-	set_unlocked_handler_callback(QUIT, cli_quit);
-	set_unlocked_handler_callback(SHUTDOWN, cli_shutdown);
-	set_handler_callback(GETPRSTATUS+MAP, cli_getprstatus);
-	set_handler_callback(SETPRSTATUS+MAP, cli_setprstatus);
-	set_handler_callback(UNSETPRSTATUS+MAP, cli_unsetprstatus);
-	set_handler_callback(FORCEQ+DAEMON, cli_force_no_daemon_q);
-	set_handler_callback(RESTOREQ+DAEMON, cli_restore_no_daemon_q);
-	set_handler_callback(GETPRKEY+MAP, cli_getprkey);
-	set_handler_callback(SETPRKEY+MAP+KEY, cli_setprkey);
-	set_handler_callback(UNSETPRKEY+MAP, cli_unsetprkey);
-	set_handler_callback(SETMARGINAL+PATH, cli_set_marginal);
-	set_handler_callback(UNSETMARGINAL+PATH, cli_unset_marginal);
-	set_handler_callback(UNSETMARGINAL+MAP, cli_unset_all_marginal);
-
+	init_handler_callbacks();
 	umask(077);
 	uxsock_listen(&uxsock_trigger, ux_sock, ap);
 
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 12/35] multipathd: add and set cli_handlers in a single step
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (10 preceding siblings ...)
  2021-09-10 11:40 ` [dm-devel] [PATCH 11/35] multipathd: make all cli_handlers static mwilck
@ 2021-09-10 11:40 ` mwilck
  2021-09-16  0:01   ` Benjamin Marzinski
  2021-09-10 11:40 ` [dm-devel] [PATCH 13/35] multipathd: cli.c: use ESRCH for "command not found" mwilck
                   ` (22 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:40 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

Modify set_handler_callback() such that a missing slot is created
if no matching slot is found. This way, we can skip the initialization
with NULL handlers on startup.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/cli.c | 85 +++++++-----------------------------------------
 multipathd/cli.h |  6 ++--
 2 files changed, 15 insertions(+), 76 deletions(-)

diff --git a/multipathd/cli.c b/multipathd/cli.c
index 5213813..7020d2b 100644
--- a/multipathd/cli.c
+++ b/multipathd/cli.c
@@ -100,26 +100,20 @@ find_handler (uint64_t fp)
 }
 
 int
-set_handler_callback (uint64_t fp, cli_handler *fn)
+__set_handler_callback (uint64_t fp, cli_handler *fn, bool locked)
 {
-	struct handler * h = find_handler(fp);
+	struct handler *h = find_handler(fp);
 
-	if (!h)
+	if (!h) {
+		add_handler(fp, fn);
+		h = find_handler(fp);
+	}
+	if (!h) {
+		condlog(0, "%s: failed to set handler for code %"PRIu64,
+			__func__, fp);
 		return 1;
-	h->fn = fn;
-	h->locked = 1;
-	return 0;
-}
-
-int
-set_unlocked_handler_callback (uint64_t fp, cli_handler *fn)
-{
-	struct handler * h = find_handler(fp);
-
-	if (!h)
-		return 1;
-	h->fn = fn;
-	h->locked = 0;
+	}
+	h->locked = locked;
 	return 0;
 }
 
@@ -513,63 +507,6 @@ cli_init (void) {
 	if (alloc_handlers())
 		return 1;
 
-	add_handler(LIST+PATHS, NULL);
-	add_handler(LIST+PATHS+FMT, NULL);
-	add_handler(LIST+PATHS+RAW+FMT, NULL);
-	add_handler(LIST+PATH, NULL);
-	add_handler(LIST+STATUS, NULL);
-	add_handler(LIST+DAEMON, NULL);
-	add_handler(LIST+MAPS, NULL);
-	add_handler(LIST+MAPS+STATUS, NULL);
-	add_handler(LIST+MAPS+STATS, NULL);
-	add_handler(LIST+MAPS+FMT, NULL);
-	add_handler(LIST+MAPS+RAW+FMT, NULL);
-	add_handler(LIST+MAPS+TOPOLOGY, NULL);
-	add_handler(LIST+MAPS+JSON, NULL);
-	add_handler(LIST+TOPOLOGY, NULL);
-	add_handler(LIST+MAP+TOPOLOGY, NULL);
-	add_handler(LIST+MAP+JSON, NULL);
-	add_handler(LIST+MAP+FMT, NULL);
-	add_handler(LIST+MAP+RAW+FMT, NULL);
-	add_handler(LIST+CONFIG, NULL);
-	add_handler(LIST+CONFIG+LOCAL, NULL);
-	add_handler(LIST+BLACKLIST, NULL);
-	add_handler(LIST+DEVICES, NULL);
-	add_handler(LIST+WILDCARDS, NULL);
-	add_handler(RESET+MAPS+STATS, NULL);
-	add_handler(RESET+MAP+STATS, NULL);
-	add_handler(ADD+PATH, NULL);
-	add_handler(DEL+PATH, NULL);
-	add_handler(ADD+MAP, NULL);
-	add_handler(DEL+MAP, NULL);
-	add_handler(DEL+MAPS, NULL);
-	add_handler(SWITCH+MAP+GROUP, NULL);
-	add_handler(RECONFIGURE, NULL);
-	add_handler(SUSPEND+MAP, NULL);
-	add_handler(RESUME+MAP, NULL);
-	add_handler(RESIZE+MAP, NULL);
-	add_handler(RESET+MAP, NULL);
-	add_handler(RELOAD+MAP, NULL);
-	add_handler(DISABLEQ+MAP, NULL);
-	add_handler(RESTOREQ+MAP, NULL);
-	add_handler(DISABLEQ+MAPS, NULL);
-	add_handler(RESTOREQ+MAPS, NULL);
-	add_handler(REINSTATE+PATH, NULL);
-	add_handler(FAIL+PATH, NULL);
-	add_handler(QUIT, NULL);
-	add_handler(SHUTDOWN, NULL);
-	add_handler(GETPRSTATUS+MAP, NULL);
-	add_handler(SETPRSTATUS+MAP, NULL);
-	add_handler(UNSETPRSTATUS+MAP, NULL);
-	add_handler(GETPRKEY+MAP, NULL);
-	add_handler(SETPRKEY+MAP+KEY, NULL);
-	add_handler(UNSETPRKEY+MAP, NULL);
-	add_handler(FORCEQ+DAEMON, NULL);
-	add_handler(RESTOREQ+DAEMON, NULL);
-	add_handler(SETMARGINAL+PATH, NULL);
-	add_handler(UNSETMARGINAL+PATH, NULL);
-	add_handler(UNSETMARGINAL+MAP, NULL);
-
 	return 0;
 }
 
diff --git a/multipathd/cli.h b/multipathd/cli.h
index 3dac1b4..dbb75be 100644
--- a/multipathd/cli.h
+++ b/multipathd/cli.h
@@ -134,8 +134,10 @@ struct handler {
 
 int alloc_handlers (void);
 int add_handler (uint64_t fp, cli_handler *fn);
-int set_handler_callback (uint64_t fp, cli_handler *fn);
-int set_unlocked_handler_callback (uint64_t fp, cli_handler *fn);
+int __set_handler_callback (uint64_t fp, cli_handler *fn, bool locked);
+#define set_handler_callback(fp, fn) __set_handler_callback(fp, fn, true)
+#define set_unlocked_handler_callback(fp, fn) __set_handler_callback(fp, fn, false)
+
 int parse_cmd (char * cmd, char ** reply, int * len, void *, int);
 int load_keys (void);
 char * get_keyparam (vector v, uint64_t code);
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 13/35] multipathd: cli.c: use ESRCH for "command not found"
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (11 preceding siblings ...)
  2021-09-10 11:40 ` [dm-devel] [PATCH 12/35] multipathd: add and set cli_handlers in a single step mwilck
@ 2021-09-10 11:40 ` mwilck
  2021-09-16  0:02   ` Benjamin Marzinski
  2021-09-10 11:40 ` [dm-devel] [PATCH 14/35] multipathd: add "force_reconfigure" option mwilck
                   ` (21 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:40 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

EAGAIN is too generic, and doesn't fit semantically either.
ESRCH in't used anywhere else in our code.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/cli.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/multipathd/cli.c b/multipathd/cli.c
index 7020d2b..f8c1dbd 100644
--- a/multipathd/cli.c
+++ b/multipathd/cli.c
@@ -253,7 +253,7 @@ find_key (const char * str)
  *
  * returns:
  * ENOMEM: not enough memory to allocate command
- * EAGAIN: command not found
+ * ESRCH: command not found
  * EINVAL: argument missing for command
  */
 static int
@@ -288,7 +288,7 @@ get_cmdvec (char * cmd, vector *v)
 		}
 		kw = find_key(buff);
 		if (!kw) {
-			r = EAGAIN;
+			r = ESRCH;
 			goto out;
 		}
 		cmdkw = alloc_key();
@@ -378,7 +378,7 @@ do_genhelp(struct strbuf *reply, const char *cmd, int error) {
 	case ENOMEM:
 		rc = print_strbuf(reply, "%s: Not enough memory\n", cmd);
 		break;
-	case EAGAIN:
+	case ESRCH:
 		rc = print_strbuf(reply, "%s: not found\n", cmd);
 		break;
 	case EINVAL:
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 14/35] multipathd: add "force_reconfigure" option
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (12 preceding siblings ...)
  2021-09-10 11:40 ` [dm-devel] [PATCH 13/35] multipathd: cli.c: use ESRCH for "command not found" mwilck
@ 2021-09-10 11:40 ` mwilck
  2021-09-16  0:13   ` Benjamin Marzinski
  2021-09-10 11:41 ` [dm-devel] [PATCH 15/35] multipathd: uxlsnr: avoid stalled clients during reconfigure mwilck
                   ` (20 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:40 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

Since e3270f7 ("multipathd: use weaker "force_reload" at startup"),
(multipath-tools 0.7.0), we only reload those maps that must be
reloaded during startup. "multipath reconfigure", OTOH, reloads
every map, which may take a long time on systems with lots of
storage devices, as every DM_DEVICE_RELOAD ioctl involves a
suspend/resume cycle.

The logic we use during startup is actually very robust and catches
all cases in which a reload is necessary. "reconfigure" operations
are often done because of configuration changes, and usually don't
require a full reload of every map.

This patch changes the default behavior of "multipath reconfigure"
to "weak" reload, like we do on startup since e3270f7. The behavior
can be changed by setting the configuration option
"force_reconfigure yes" before starting the reconfigure operation.
"multipath -r" is also affected, but "multipath -D -r" is not.

It would have been nice to have introduced a new cli command
"reconfigure force" instead, but the way "reconfigure" is
implemented, that would have required a major rewrite of the code.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/config.c      |  1 +
 libmultipath/config.h      |  1 +
 libmultipath/configure.c   | 19 ++++++++++++++++++-
 libmultipath/defaults.h    |  1 +
 libmultipath/dict.c        |  4 ++++
 multipath/multipath.8      |  6 ++++--
 multipath/multipath.conf.5 | 17 +++++++++++++++++
 multipathd/main.c          | 18 +++++-------------
 multipathd/multipathd.8    |  6 ++++--
 9 files changed, 55 insertions(+), 18 deletions(-)

diff --git a/libmultipath/config.c b/libmultipath/config.c
index 30046a1..a1ef4c3 100644
--- a/libmultipath/config.c
+++ b/libmultipath/config.c
@@ -869,6 +869,7 @@ int _init_config (const char *file, struct config *conf)
 	conf->ghost_delay = DEFAULT_GHOST_DELAY;
 	conf->all_tg_pt = DEFAULT_ALL_TG_PT;
 	conf->recheck_wwid = DEFAULT_RECHECK_WWID;
+	conf->force_reconfigure = DEFAULT_FORCE_RECONFIGURE;
 	/*
 	 * preload default hwtable
 	 */
diff --git a/libmultipath/config.h b/libmultipath/config.h
index 933fe0d..4617177 100644
--- a/libmultipath/config.h
+++ b/libmultipath/config.h
@@ -189,6 +189,7 @@ struct config {
 	int skip_delegate;
 	unsigned int sequence_nr;
 	int recheck_wwid;
+	int force_reconfigure;
 
 	char * multipath_dir;
 	char * selector;
diff --git a/libmultipath/configure.c b/libmultipath/configure.c
index 7edb355..262657e 100644
--- a/libmultipath/configure.c
+++ b/libmultipath/configure.c
@@ -1093,12 +1093,27 @@ out:
 	return ret;
 }
 
+static const char* reconfigure_str(int force)
+{
+	switch(force) {
+	case FORCE_RELOAD_NONE:
+		return "new";
+	case FORCE_RELOAD_WEAK:
+		return "changed";
+	case FORCE_RELOAD_YES:
+		return "all";
+	default:
+		return "<undefined>";
+	}
+}
+
 /*
  * The force_reload parameter determines how coalesce_paths treats existing maps.
  * FORCE_RELOAD_NONE: existing maps aren't touched at all
  * FORCE_RELOAD_YES: all maps are rebuilt from scratch and (re)loaded in DM
  * FORCE_RELOAD_WEAK: existing maps are compared to the current conf and only
- * reloaded in DM if there's a difference. This is useful during startup.
+ * reloaded in DM if there's a difference. This is normally sufficient.
+ * This is controlled by the "force_reconfigure" config option.
  */
 int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid,
 		    int force_reload, enum mpath_cmds cmd)
@@ -1117,6 +1132,8 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid,
 	int allow_queueing;
 	struct bitfield *size_mismatch_seen;
 
+	condlog(2, "%s: reloading %s maps", __func__,
+		reconfigure_str(force_reload));
 	/* ignore refwwid if it's empty */
 	if (refwwid && !strlen(refwwid))
 		refwwid = NULL;
diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h
index c27946c..eab08a0 100644
--- a/libmultipath/defaults.h
+++ b/libmultipath/defaults.h
@@ -56,6 +56,7 @@
 #define DEFAULT_RECHECK_WWID RECHECK_WWID_OFF
 /* Enable no foreign libraries by default */
 #define DEFAULT_ENABLE_FOREIGN "NONE"
+#define DEFAULT_FORCE_RECONFIGURE YN_NO
 
 #define CHECKINT_UNDEF		UINT_MAX
 #define DEFAULT_CHECKINT	5
diff --git a/libmultipath/dict.c b/libmultipath/dict.c
index 7a72738..fee08cf 100644
--- a/libmultipath/dict.c
+++ b/libmultipath/dict.c
@@ -299,6 +299,9 @@ declare_def_snprint(verbosity, print_int)
 declare_def_handler(reassign_maps, set_yes_no)
 declare_def_snprint(reassign_maps, print_yes_no)
 
+declare_def_handler(force_reconfigure, set_yes_no)
+declare_def_snprint(force_reconfigure, print_yes_no)
+
 declare_def_handler(multipath_dir, set_str)
 declare_def_snprint(multipath_dir, print_str)
 
@@ -1713,6 +1716,7 @@ init_keywords(vector keywords)
 	install_keyword("polling_interval", &checkint_handler, &snprint_def_checkint);
 	install_keyword("max_polling_interval", &def_max_checkint_handler, &snprint_def_max_checkint);
 	install_keyword("reassign_maps", &def_reassign_maps_handler, &snprint_def_reassign_maps);
+	install_keyword("force_reconfigure", &def_force_reconfigure_handler, &snprint_def_force_reconfigure);
 	install_keyword("multipath_dir", &def_multipath_dir_handler, &snprint_def_multipath_dir);
 	install_keyword("path_selector", &def_selector_handler, &snprint_def_selector);
 	install_keyword("path_grouping_policy", &def_pgpolicy_handler, &snprint_def_pgpolicy);
diff --git a/multipath/multipath.8 b/multipath/multipath.8
index 17df59f..b3980c0 100644
--- a/multipath/multipath.8
+++ b/multipath/multipath.8
@@ -264,9 +264,11 @@ Force new maps to use the specified policy, overriding the configuration in
 .
 .TP
 .B \-r
-Force a reload of all existing multipath maps. This command is delegated to
+Reload existing multipath maps. This command is delegated to
 the multipathd daemon if it's running. In this case, other command line
-switches of the \fImultipath\fR command have no effect.
+switches of the \fImultipath\fR command have no effect, and multipathd
+executes a \fIreconfigure\fR command. See the \fIforce_reconfigure\fR option
+in \fBmultipath.conf(5)\fR.
 .
 .TP
 .BI \-R " retries"
diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5
index 42a15ff..814de66 100644
--- a/multipath/multipath.conf.5
+++ b/multipath/multipath.conf.5
@@ -34,6 +34,7 @@ or \fBmultipathd show config\fR command.
 .SH SYNTAX
 .\" ----------------------------------------------------------------------------
 .
+.
 The configuration file contains entries of the form:
 .RS
 .nf
@@ -165,6 +166,22 @@ The default is: \fB4 * polling_interval\fR
 .
 .
 .TP
+.B force_reconfigure
+This controls what happens when \fBmultipathd reconfigure\fR or
+\fBmultipath -r\fR is executed. If set to \fIyes\fR, all multipath
+maps will be reloaded, regardless if this is necessary or not, which
+may take a lot of time on large systems. This used to be the default
+on previous versions of multipath-tools. If set to \fIno\fR,
+only those maps will be reloaded for which some parameters
+have changed that are relevant for the device-mapper configuration of the map.
+This is the behavior during \fImultipathd\fR startup.
+.RS
+.TP
+The default is: \fBno\fR
+.RE
+.
+.
+.TP
 .B reassign_maps
 Enable reassigning of device-mapper maps. With this option multipathd
 will remap existing device-mapper maps to always point to multipath
diff --git a/multipathd/main.c b/multipathd/main.c
index bda51c9..6d7c8c9 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -2594,14 +2594,13 @@ checkerloop (void *ap)
 }
 
 int
-configure (struct vectors * vecs)
+configure (struct vectors *vecs, bool force)
 {
 	struct multipath * mpp;
 	struct path * pp;
 	vector mpvec;
 	int i, ret;
 	struct config *conf;
-	static int force_reload = FORCE_RELOAD_WEAK;
 
 	if (!vecs->pathvec && !(vecs->pathvec = vector_alloc())) {
 		condlog(0, "couldn't allocate path vec in configure");
@@ -2649,15 +2648,9 @@ configure (struct vectors * vecs)
 	if (should_exit())
 		goto fail;
 
-	/*
-	 * create new set of maps & push changed ones into dm
-	 * In the first call, use FORCE_RELOAD_WEAK to avoid making
-	 * superfluous ACT_RELOAD ioctls. Later calls are done
-	 * with FORCE_RELOAD_YES.
-	 */
-	ret = coalesce_paths(vecs, mpvec, NULL, force_reload, CMD_NONE);
-	if (force_reload == FORCE_RELOAD_WEAK)
-		force_reload = FORCE_RELOAD_YES;
+	ret = coalesce_paths(vecs, mpvec, NULL,
+			     force ? FORCE_RELOAD_YES : FORCE_RELOAD_WEAK,
+			     CMD_NONE);
 	if (ret != CP_OK) {
 		condlog(0, "configure failed while coalescing paths");
 		goto fail;
@@ -2769,8 +2762,7 @@ reconfigure (struct vectors * vecs)
 	rcu_assign_pointer(multipath_conf, conf);
 	call_rcu(&old->rcu, rcu_free_config);
 
-	configure(vecs);
-
+	configure(vecs, conf->force_reconfigure == YN_YES);
 
 	return 0;
 }
diff --git a/multipathd/multipathd.8 b/multipathd/multipathd.8
index 048a838..b52a617 100644
--- a/multipathd/multipathd.8
+++ b/multipathd/multipathd.8
@@ -195,8 +195,10 @@ group index, starting with 1.
 .
 .TP
 .B reconfigure
-Reconfigures the multipaths. This should be triggered automatically after anyi
-hotplug event.
+Reconfigure the multipaths. This is only necessary after applying configuration
+changes, as multipathd sets up new devices automatically. See the
+\fIforce_reconfigure\fR option in \fBmultipath.conf(5)\fR. Note that multipathd
+can't process most commands while reconfiguring.
 .
 .TP
 .B suspend map|multipath $map
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 15/35] multipathd: uxlsnr: avoid stalled clients during reconfigure
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (13 preceding siblings ...)
  2021-09-10 11:40 ` [dm-devel] [PATCH 14/35] multipathd: add "force_reconfigure" option mwilck
@ 2021-09-10 11:41 ` mwilck
  2021-09-16  2:17   ` Benjamin Marzinski
  2021-09-10 11:41 ` [dm-devel] [PATCH 16/35] multipathd: uxlsnr: handle client HUP mwilck
                   ` (19 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:41 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

Since 47cc1d3 ("multipathd: fix client response for socket
activation"), we hold back clients while reconfigure is running.
The idea of 47cc1d3 was to fix the behavior during initial
start up. When multipathd reconfigures itself during runtime,
and the reconfiguration takes a long time (a minute or more is
not unusual in big configurations), clients will time out with
no response ("timeout receiving packet"). Waiting for reconfigure
to finish breaks our timeout handling.

Therefore we should only apply the logic of 47cc1d3 during initial
configuration. In this case, the client that triggered socket
activation may still encounter a timeout, but there's not much we can
do about that.

Fixes: 47cc1d3 ("multipathd: fix client response for socket activation")
Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/main.c   |  9 +++++++++
 multipathd/uxlsnr.c | 12 ------------
 2 files changed, 9 insertions(+), 12 deletions(-)

diff --git a/multipathd/main.c b/multipathd/main.c
index 6d7c8c9..c6357ef 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -1695,6 +1695,15 @@ uxlsnrloop (void * ap)
 
 	init_handler_callbacks();
 	umask(077);
+
+	/*
+	 * Wait for initial reconfiguration to finish, while
+	 * hadling signals
+	 */
+	while (wait_for_state_change_if(DAEMON_CONFIGURE, 50)
+	       == DAEMON_CONFIGURE)
+		handle_signals(false);
+
 	uxsock_listen(&uxsock_trigger, ux_sock, ap);
 
 out_sock:
diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
index dbee0d6..20efbd3 100644
--- a/multipathd/uxlsnr.c
+++ b/multipathd/uxlsnr.c
@@ -391,18 +391,6 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
 			continue;
 		}
 
-		/*
-		 * Client connection. We shouldn't answer while we're
-		 * configuring - nothing may be configured yet.
-		 * But we can't wait forever either, because this thread
-		 * must handle signals. So wait a short while only.
-		 */
-		if (wait_for_state_change_if(DAEMON_CONFIGURE, 10)
-		    == DAEMON_CONFIGURE) {
-			handle_signals(false);
-			continue;
-		}
-
 		/* see if a client wants to speak to us */
 		for (i = POLLFDS_BASE; i < n_pfds; i++) {
 			if (polls[i].revents & POLLIN) {
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 16/35] multipathd: uxlsnr: handle client HUP
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (14 preceding siblings ...)
  2021-09-10 11:41 ` [dm-devel] [PATCH 15/35] multipathd: uxlsnr: avoid stalled clients during reconfigure mwilck
@ 2021-09-10 11:41 ` mwilck
  2021-09-16  2:17   ` Benjamin Marzinski
  2021-09-10 11:41 ` [dm-devel] [PATCH 17/35] multipathd: uxlsnr: use symbolic values for pollfd indices mwilck
                   ` (18 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:41 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

The unix socket listener thread doesn't even look at the revents
returned by poll() while the daemon is configuring. This may cause a
closed client socket to be kept open for a long time by the server,
while the listener basically performs a busy loop, as ppoll() always
returns immediately as long as the POLLHUP condition exists.

Worse, it can happen that multipathd reads data from such a closed
client socket after the client has disconnected. See the description
of POLLHUP in poll(2).

Close connections immediately if HUP is received.

Also, use the fd in log messages to identify the client rather
than the random index.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/uxlsnr.c | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
index 20efbd3..7e88538 100644
--- a/multipathd/uxlsnr.c
+++ b/multipathd/uxlsnr.c
@@ -393,7 +393,7 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
 
 		/* see if a client wants to speak to us */
 		for (i = POLLFDS_BASE; i < n_pfds; i++) {
-			if (polls[i].revents & POLLIN) {
+			if (polls[i].revents & (POLLIN|POLLHUP|POLLERR)) {
 				struct timespec start_time;
 
 				c = NULL;
@@ -410,6 +410,12 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
 						i, polls[i].fd);
 					continue;
 				}
+				if (polls[i].revents & (POLLHUP|POLLERR)) {
+					condlog(4, "cli[%d]: Disconnected",
+						c->fd);
+					dead_client(c);
+					continue;
+				}
 				get_monotonic_time(&start_time);
 				if (recv_packet_from_client(c->fd, &inbuf,
 							    uxsock_timeout)
@@ -423,7 +429,7 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
 					continue;
 				}
 				condlog(4, "cli[%d]: Got request [%s]",
-					i, inbuf);
+					polls[i].fd, inbuf);
 				uxsock_trigger(inbuf, &reply, &rlen,
 					       _socket_client_is_root(c->fd),
 					       trigger_data);
@@ -434,7 +440,7 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
 					} else {
 						condlog(4, "cli[%d]: "
 							"Reply [%d bytes]",
-							i, rlen);
+							polls[i].fd, rlen);
 					}
 					FREE(reply);
 					reply = NULL;
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 17/35] multipathd: uxlsnr: use symbolic values for pollfd indices
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (15 preceding siblings ...)
  2021-09-10 11:41 ` [dm-devel] [PATCH 16/35] multipathd: uxlsnr: handle client HUP mwilck
@ 2021-09-10 11:41 ` mwilck
  2021-09-16  2:18   ` Benjamin Marzinski
  2021-09-10 11:41 ` [dm-devel] [PATCH 18/35] multipathd: uxlsnr: avoid using fd -1 in ppoll() mwilck
                   ` (17 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:41 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

Avoid hardcoding the indices as 0, 1, 2...

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/uxlsnr.c | 25 +++++++++++++++----------
 1 file changed, 15 insertions(+), 10 deletions(-)

diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
index 7e88538..6506109 100644
--- a/multipathd/uxlsnr.c
+++ b/multipathd/uxlsnr.c
@@ -46,8 +46,13 @@ struct client {
 	int fd;
 };
 
-/* The number of fds we poll on, other than individual client connections */
-#define POLLFDS_BASE 2
+/* Indices for array of poll fds */
+enum {
+	POLLFD_UX = 0,
+	POLLFD_NOTIFY,
+	POLLFDS_BASE,
+};
+
 #define POLLFD_CHUNK (4096 / sizeof(struct pollfd))
 /* Minimum mumber of pollfds to reserve for clients */
 #define MIN_POLLS (POLLFD_CHUNK - POLLFDS_BASE)
@@ -339,8 +344,8 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
 			}
 		}
 		if (num_clients < MAX_CLIENTS) {
-			polls[0].fd = ux_sock;
-			polls[0].events = POLLIN;
+			polls[POLLFD_UX].fd = ux_sock;
+			polls[POLLFD_UX].events = POLLIN;
 		} else {
 			/*
 			 * New clients can't connect, num_clients won't grow
@@ -348,15 +353,15 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
 			 */
 			condlog(1, "%s: max client connections reached, pausing polling",
 				__func__);
-			polls[0].fd = -1;
+			polls[POLLFD_UX].fd = -1;
 		}
 
 		reset_watch(notify_fd, &wds, &sequence_nr);
 		if (notify_fd == -1 || (wds.conf_wd == -1 && wds.dir_wd == -1))
-			polls[1].fd = -1;
+			polls[POLLFD_NOTIFY].fd = -1;
 		else
-			polls[1].fd = notify_fd;
-		polls[1].events = POLLIN;
+			polls[POLLFD_NOTIFY].fd = notify_fd;
+		polls[POLLFD_NOTIFY].events = POLLIN;
 
 		/* setup the clients */
 		i = POLLFDS_BASE;
@@ -454,12 +459,12 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
 		handle_signals(true);
 
 		/* see if we got a new client */
-		if (polls[0].revents & POLLIN) {
+		if (polls[POLLFD_UX].revents & POLLIN) {
 			new_client(ux_sock);
 		}
 
 		/* handle inotify events on config files */
-		if (polls[1].revents & POLLIN)
+		if (polls[POLLFD_NOTIFY].revents & POLLIN)
 			handle_inotify(notify_fd, &wds);
 	}
 
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 18/35] multipathd: uxlsnr: avoid using fd -1 in ppoll()
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (16 preceding siblings ...)
  2021-09-10 11:41 ` [dm-devel] [PATCH 17/35] multipathd: uxlsnr: use symbolic values for pollfd indices mwilck
@ 2021-09-10 11:41 ` mwilck
  2021-09-16  2:18   ` Benjamin Marzinski
  2021-09-10 11:41 ` [dm-devel] [PATCH 19/35] multipathd: uxlsnr: data structure for stateful client connection mwilck
                   ` (16 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:41 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

Minor edit: if notifications are off, we set the poll fd to
-1 but still use the POLLIN mask. It looks nicer if to poll
the correct fd, but reset the event mask to 0 if we're not
actually interested in it.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/uxlsnr.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
index 6506109..98a9f71 100644
--- a/multipathd/uxlsnr.c
+++ b/multipathd/uxlsnr.c
@@ -357,11 +357,11 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
 		}
 
 		reset_watch(notify_fd, &wds, &sequence_nr);
+		polls[POLLFD_NOTIFY].fd = notify_fd;
 		if (notify_fd == -1 || (wds.conf_wd == -1 && wds.dir_wd == -1))
-			polls[POLLFD_NOTIFY].fd = -1;
+			polls[POLLFD_NOTIFY].events = 0;
 		else
-			polls[POLLFD_NOTIFY].fd = notify_fd;
-		polls[POLLFD_NOTIFY].events = POLLIN;
+			polls[POLLFD_NOTIFY].events = POLLIN;
 
 		/* setup the clients */
 		i = POLLFDS_BASE;
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 19/35] multipathd: uxlsnr: data structure for stateful client connection
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (17 preceding siblings ...)
  2021-09-10 11:41 ` [dm-devel] [PATCH 18/35] multipathd: uxlsnr: avoid using fd -1 in ppoll() mwilck
@ 2021-09-10 11:41 ` mwilck
  2021-09-16  2:19   ` Benjamin Marzinski
  2021-09-10 11:41 ` [dm-devel] [PATCH 20/35] multipathd: move uxsock_trigger() to uxlsnr.c mwilck
                   ` (15 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:41 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

Currently the uxlsnr handles each client request (receive requset -
handle request - respond) in a single loop iteration. This has
severe disadvantages. In particular, the code may wait in poll()
called from read_all(), or wait for the vecs lock, while other
clients are ready to be serviced or signals to be handled.

This patch adds some fields to "struct client" which will be used
by later patches to change this into a state machine that basically
waits only in place, the ppoll() call in uxsock_listen().

For now, we just introduce and initialize the fields.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/uxlsnr.c | 27 +++++++++++++++++++++++++--
 1 file changed, 25 insertions(+), 2 deletions(-)

diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
index 98a9f71..e701a1c 100644
--- a/multipathd/uxlsnr.c
+++ b/multipathd/uxlsnr.c
@@ -40,10 +40,30 @@
 #include "main.h"
 #include "cli.h"
 #include "uxlsnr.h"
+#include "strbuf.h"
+
+/* state of client connection */
+enum {
+	CLT_RECV,
+	CLT_PARSE,
+	CLT_WAIT_LOCK,
+	CLT_WORK,
+	CLT_SEND,
+};
 
 struct client {
 	struct list_head node;
+	struct timespec expires;
+	int state;
 	int fd;
+	vector cmdvec;
+	/* NUL byte at end */
+	char cmd[_MAX_CMD_LEN + 1];
+	struct strbuf reply;
+	struct handler *handler;
+	size_t cmd_len, len;
+	int error;
+	bool is_root;
 };
 
 /* Indices for array of poll fds */
@@ -104,14 +124,14 @@ static void new_client(int ux_sock)
 	if (fd == -1)
 		return;
 
-	c = (struct client *)MALLOC(sizeof(*c));
+	c = calloc(1, sizeof(*c));
 	if (!c) {
 		close(fd);
 		return;
 	}
-	memset(c, 0, sizeof(*c));
 	INIT_LIST_HEAD(&c->node);
 	c->fd = fd;
+	c->state = CLT_RECV;
 
 	/* put it in our linked list */
 	pthread_mutex_lock(&client_lock);
@@ -127,6 +147,9 @@ static void _dead_client(struct client *c)
 	int fd = c->fd;
 	list_del_init(&c->node);
 	c->fd = -1;
+	reset_strbuf(&c->reply);
+	if (c->cmdvec)
+		free_keys(c->cmdvec);
 	FREE(c);
 	close(fd);
 }
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 20/35] multipathd: move uxsock_trigger() to uxlsnr.c
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (18 preceding siblings ...)
  2021-09-10 11:41 ` [dm-devel] [PATCH 19/35] multipathd: uxlsnr: data structure for stateful client connection mwilck
@ 2021-09-10 11:41 ` mwilck
  2021-09-16  2:19   ` Benjamin Marzinski
  2021-09-10 11:41 ` [dm-devel] [PATCH 21/35] multipathd: move parse_cmd() " mwilck
                   ` (14 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:41 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

uxsock_trigger() really belongs into cli.c. I suppose that way back in
the past there were strong reasons to call this function via a
pointer. I don't think these reasons are valid any more. Moving
the function to cli.c allows restructuring the code.

No functional changes.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/main.c   | 44 +-------------------------------------------
 multipathd/uxlsnr.c | 44 ++++++++++++++++++++++++++++++++++++++++++--
 multipathd/uxlsnr.h |  4 +---
 3 files changed, 44 insertions(+), 48 deletions(-)

diff --git a/multipathd/main.c b/multipathd/main.c
index c6357ef..ec4bcc3 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -1526,48 +1526,6 @@ map_discovery (struct vectors * vecs)
 	return 0;
 }
 
-int
-uxsock_trigger (char * str, char ** reply, int * len, bool is_root,
-		void * trigger_data)
-{
-	struct vectors * vecs;
-	int r;
-
-	*reply = NULL;
-	*len = 0;
-	vecs = (struct vectors *)trigger_data;
-
-	if ((str != NULL) && (is_root == false) &&
-	    (strncmp(str, "list", strlen("list")) != 0) &&
-	    (strncmp(str, "show", strlen("show")) != 0)) {
-		*reply = STRDUP("permission deny: need to be root");
-		if (*reply)
-			*len = strlen(*reply) + 1;
-		return 1;
-	}
-
-	r = parse_cmd(str, reply, len, vecs, uxsock_timeout / 1000);
-
-	if (r > 0) {
-		if (r == ETIMEDOUT)
-			*reply = STRDUP("timeout\n");
-		else
-			*reply = STRDUP("fail\n");
-		if (*reply)
-			*len = strlen(*reply) + 1;
-		r = 1;
-	}
-	else if (!r && *len == 0) {
-		*reply = STRDUP("ok\n");
-		if (*reply)
-			*len = strlen(*reply) + 1;
-		r = 0;
-	}
-	/* else if (r < 0) leave *reply alone */
-
-	return r;
-}
-
 int
 uev_trigger (struct uevent * uev, void * trigger_data)
 {
@@ -1704,7 +1662,7 @@ uxlsnrloop (void * ap)
 	       == DAEMON_CONFIGURE)
 		handle_signals(false);
 
-	uxsock_listen(&uxsock_trigger, ux_sock, ap);
+	uxsock_listen(ux_sock, ap);
 
 out_sock:
 	pthread_cleanup_pop(1); /* uxsock_cleanup */
diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
index e701a1c..622aac1 100644
--- a/multipathd/uxlsnr.c
+++ b/multipathd/uxlsnr.c
@@ -311,11 +311,51 @@ static void handle_inotify(int fd, struct watch_descriptors *wds)
 		condlog(1, "Multipath configuration updated.\nReload multipathd for changes to take effect");
 }
 
+static int uxsock_trigger(char *str, char **reply, int *len,
+			  bool is_root, void *trigger_data)
+{
+	struct vectors * vecs;
+	int r;
+
+	*reply = NULL;
+	*len = 0;
+	vecs = (struct vectors *)trigger_data;
+
+	if ((str != NULL) && (is_root == false) &&
+	    (strncmp(str, "list", strlen("list")) != 0) &&
+	    (strncmp(str, "show", strlen("show")) != 0)) {
+		*reply = STRDUP("permission deny: need to be root");
+		if (*reply)
+			*len = strlen(*reply) + 1;
+		return 1;
+	}
+
+	r = parse_cmd(str, reply, len, vecs, uxsock_timeout / 1000);
+
+	if (r > 0) {
+		if (r == ETIMEDOUT)
+			*reply = STRDUP("timeout\n");
+		else
+			*reply = STRDUP("fail\n");
+		if (*reply)
+			*len = strlen(*reply) + 1;
+		r = 1;
+	}
+	else if (!r && *len == 0) {
+		*reply = STRDUP("ok\n");
+		if (*reply)
+			*len = strlen(*reply) + 1;
+		r = 0;
+	}
+	/* else if (r < 0) leave *reply alone */
+
+	return r;
+}
+
 /*
  * entry point
  */
-void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
-		     void * trigger_data)
+void *uxsock_listen(long ux_sock, void *trigger_data)
 {
 	int rlen;
 	char *inbuf;
diff --git a/multipathd/uxlsnr.h b/multipathd/uxlsnr.h
index 18f008d..60c3a2c 100644
--- a/multipathd/uxlsnr.h
+++ b/multipathd/uxlsnr.h
@@ -3,10 +3,8 @@
 
 #include <stdbool.h>
 
-typedef int (uxsock_trigger_fn)(char *, char **, int *, bool, void *);
-
 void uxsock_cleanup(void *arg);
-void *uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
+void *uxsock_listen(long ux_sock,
 		    void * trigger_data);
 
 #endif
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 21/35] multipathd: move parse_cmd() to uxlsnr.c
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (19 preceding siblings ...)
  2021-09-10 11:41 ` [dm-devel] [PATCH 20/35] multipathd: move uxsock_trigger() to uxlsnr.c mwilck
@ 2021-09-10 11:41 ` mwilck
  2021-09-16  2:19   ` Benjamin Marzinski
  2021-09-10 11:41 ` [dm-devel] [PATCH 22/35] multipathd: uxlsnr: remove check_timeout() mwilck
                   ` (13 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:41 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

parse_cmd() does more than the name says - it parses, executes
handlers, and even provides reply strings for some cases. This doesn't
work well with the state machine idea. Thus move it to uxlsnr.c,
where later patches will move some functionality elsewhere.

No functional changes.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/cli.c    | 74 +++++----------------------------------------
 multipathd/cli.h    |  5 ++-
 multipathd/uxlsnr.c | 61 +++++++++++++++++++++++++++++++++++++
 3 files changed, 73 insertions(+), 67 deletions(-)

diff --git a/multipathd/cli.c b/multipathd/cli.c
index f8c1dbd..29d6a6e 100644
--- a/multipathd/cli.c
+++ b/multipathd/cli.c
@@ -256,8 +256,7 @@ find_key (const char * str)
  * ESRCH: command not found
  * EINVAL: argument missing for command
  */
-static int
-get_cmdvec (char * cmd, vector *v)
+int get_cmdvec (char *cmd, vector *v)
 {
 	int i;
 	int r = 0;
@@ -322,7 +321,7 @@ out:
 }
 
 static uint64_t
-fingerprint(vector vec)
+fingerprint(const struct _vector *vec)
 {
 	int i;
 	uint64_t fp = 0;
@@ -337,6 +336,11 @@ fingerprint(vector vec)
 	return fp;
 }
 
+struct handler *find_handler_for_cmdvec(const struct _vector *v)
+{
+	return find_handler(fingerprint(v));
+}
+
 int
 alloc_handlers (void)
 {
@@ -415,8 +419,7 @@ do_genhelp(struct strbuf *reply, const char *cmd, int error) {
 }
 
 
-static char *
-genhelp_handler (const char *cmd, int error)
+char *genhelp_handler(const char *cmd, int error)
 {
 	STRBUF_ON_STACK(reply);
 
@@ -425,67 +428,6 @@ genhelp_handler (const char *cmd, int error)
 	return steal_strbuf_str(&reply);
 }
 
-int
-parse_cmd (char * cmd, char ** reply, int * len, void * data, int timeout )
-{
-	int r;
-	struct handler * h;
-	vector cmdvec = NULL;
-	struct timespec tmo;
-
-	r = get_cmdvec(cmd, &cmdvec);
-
-	if (r) {
-		*reply = genhelp_handler(cmd, r);
-		if (*reply == NULL)
-			return EINVAL;
-		*len = strlen(*reply) + 1;
-		return 0;
-	}
-
-	h = find_handler(fingerprint(cmdvec));
-
-	if (!h || !h->fn) {
-		free_keys(cmdvec);
-		*reply = genhelp_handler(cmd, EINVAL);
-		if (*reply == NULL)
-			return EINVAL;
-		*len = strlen(*reply) + 1;
-		return 0;
-	}
-
-	/*
-	 * execute handler
-	 */
-	if (clock_gettime(CLOCK_REALTIME, &tmo) == 0) {
-		tmo.tv_sec += timeout;
-	} else {
-		tmo.tv_sec = 0;
-	}
-	if (h->locked) {
-		int locked = 0;
-		struct vectors * vecs = (struct vectors *)data;
-
-		pthread_cleanup_push(cleanup_lock, &vecs->lock);
-		if (tmo.tv_sec) {
-			r = timedlock(&vecs->lock, &tmo);
-		} else {
-			lock(&vecs->lock);
-			r = 0;
-		}
-		if (r == 0) {
-			locked = 1;
-			pthread_testcancel();
-			r = h->fn(cmdvec, reply, len, data);
-		}
-		pthread_cleanup_pop(locked);
-	} else
-		r = h->fn(cmdvec, reply, len, data);
-	free_keys(cmdvec);
-
-	return r;
-}
-
 char *
 get_keyparam (vector v, uint64_t code)
 {
diff --git a/multipathd/cli.h b/multipathd/cli.h
index dbb75be..eed606a 100644
--- a/multipathd/cli.h
+++ b/multipathd/cli.h
@@ -138,7 +138,10 @@ int __set_handler_callback (uint64_t fp, cli_handler *fn, bool locked);
 #define set_handler_callback(fp, fn) __set_handler_callback(fp, fn, true)
 #define set_unlocked_handler_callback(fp, fn) __set_handler_callback(fp, fn, false)
 
-int parse_cmd (char * cmd, char ** reply, int * len, void *, int);
+int get_cmdvec (char *cmd, vector *v);
+struct handler *find_handler_for_cmdvec(const struct _vector *v);
+char *genhelp_handler (const char *cmd, int error);
+
 int load_keys (void);
 char * get_keyparam (vector v, uint64_t code);
 void free_keys (vector vec);
diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
index 622aac1..cbbcb2c 100644
--- a/multipathd/uxlsnr.c
+++ b/multipathd/uxlsnr.c
@@ -311,6 +311,67 @@ static void handle_inotify(int fd, struct watch_descriptors *wds)
 		condlog(1, "Multipath configuration updated.\nReload multipathd for changes to take effect");
 }
 
+static int parse_cmd (char *cmd, char **reply, int *len, void *data,
+		      int timeout)
+{
+	int r;
+	struct handler * h;
+	vector cmdvec = NULL;
+	struct timespec tmo;
+
+	r = get_cmdvec(cmd, &cmdvec);
+
+	if (r) {
+		*reply = genhelp_handler(cmd, r);
+		if (*reply == NULL)
+			return EINVAL;
+		*len = strlen(*reply) + 1;
+		return 0;
+	}
+
+	h = find_handler_for_cmdvec(cmdvec);
+
+	if (!h || !h->fn) {
+		free_keys(cmdvec);
+		*reply = genhelp_handler(cmd, EINVAL);
+		if (*reply == NULL)
+			return EINVAL;
+		*len = strlen(*reply) + 1;
+		return 0;
+	}
+
+	/*
+	 * execute handler
+	 */
+	if (clock_gettime(CLOCK_REALTIME, &tmo) == 0) {
+		tmo.tv_sec += timeout;
+	} else {
+		tmo.tv_sec = 0;
+	}
+	if (h->locked) {
+		int locked = 0;
+		struct vectors * vecs = (struct vectors *)data;
+
+		pthread_cleanup_push(cleanup_lock, &vecs->lock);
+		if (tmo.tv_sec) {
+			r = timedlock(&vecs->lock, &tmo);
+		} else {
+			lock(&vecs->lock);
+			r = 0;
+		}
+		if (r == 0) {
+			locked = 1;
+			pthread_testcancel();
+			r = h->fn(cmdvec, reply, len, data);
+		}
+		pthread_cleanup_pop(locked);
+	} else
+		r = h->fn(cmdvec, reply, len, data);
+	free_keys(cmdvec);
+
+	return r;
+}
+
 static int uxsock_trigger(char *str, char **reply, int *len,
 			  bool is_root, void *trigger_data)
 {
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 22/35] multipathd: uxlsnr: remove check_timeout()
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (20 preceding siblings ...)
  2021-09-10 11:41 ` [dm-devel] [PATCH 21/35] multipathd: move parse_cmd() " mwilck
@ 2021-09-10 11:41 ` mwilck
  2021-09-16  2:21   ` Benjamin Marzinski
  2021-09-10 11:41 ` [dm-devel] [PATCH 23/35] multipathd: uxlsnr: move client handling to separate function mwilck
                   ` (12 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:41 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

This function just prints a warning, anyway. If this warning
is printed, the client will see a timeout and print a warning, too.
A later patch will re-introduce this function with real functionality.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/uxlsnr.c | 21 ---------------------
 1 file changed, 21 deletions(-)

diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
index cbbcb2c..147f81a 100644
--- a/multipathd/uxlsnr.c
+++ b/multipathd/uxlsnr.c
@@ -168,25 +168,6 @@ static void free_polls (void)
 		FREE(polls);
 }
 
-static void check_timeout(struct timespec start_time, char *inbuf,
-		   unsigned int timeout)
-{
-	struct timespec diff_time, end_time;
-
-	if (start_time.tv_sec) {
-		unsigned long msecs;
-
-		get_monotonic_time(&end_time);
-		timespecsub(&end_time, &start_time, &diff_time);
-		msecs = diff_time.tv_sec * 1000 +
-			diff_time.tv_nsec / (1000 * 1000);
-		if (msecs > timeout)
-			condlog(2, "cli cmd '%s' timeout reached "
-				"after %ld.%06lu secs", inbuf,
-				(long)diff_time.tv_sec, diff_time.tv_nsec / 1000);
-	}
-}
-
 void uxsock_cleanup(void *arg)
 {
 	struct client *client_loop;
@@ -574,8 +555,6 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
 					FREE(reply);
 					reply = NULL;
 				}
-				check_timeout(start_time, inbuf,
-					      uxsock_timeout);
 				FREE(inbuf);
 			}
 		}
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 23/35] multipathd: uxlsnr: move client handling to separate function
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (21 preceding siblings ...)
  2021-09-10 11:41 ` [dm-devel] [PATCH 22/35] multipathd: uxlsnr: remove check_timeout() mwilck
@ 2021-09-10 11:41 ` mwilck
  2021-09-16  2:21   ` Benjamin Marzinski
  2021-09-10 11:41 ` [dm-devel] [PATCH 24/35] multipathd: uxlsnr: use main poll loop for receiving mwilck
                   ` (11 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:41 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

No functional changes at this point. handle_client() will become
the state machine for handling client requests.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/uxlsnr.c | 67 ++++++++++++++++++++++-----------------------
 1 file changed, 32 insertions(+), 35 deletions(-)

diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
index 147f81a..2fb23c8 100644
--- a/multipathd/uxlsnr.c
+++ b/multipathd/uxlsnr.c
@@ -394,14 +394,42 @@ static int uxsock_trigger(char *str, char **reply, int *len,
 	return r;
 }
 
+static void handle_client(struct client *c, void *trigger_data)
+{
+	int rlen;
+	char *inbuf, *reply;
+
+	if (recv_packet_from_client(c->fd, &inbuf, uxsock_timeout) != 0) {
+		dead_client(c);
+		return;
+	}
+
+	if (!inbuf) {
+		condlog(4, "recv_packet_from_client get null request");
+		return;
+	}
+
+	condlog(4, "cli[%d]: Got request [%s]", c->fd, inbuf);
+	uxsock_trigger(inbuf, &reply, &rlen,
+		       _socket_client_is_root(c->fd),
+		       trigger_data);
+
+	if (reply) {
+		if (send_packet(c->fd, reply) != 0)
+			dead_client(c);
+		else
+			condlog(4, "cli[%d]: Reply [%d bytes]", c->fd, rlen);
+		FREE(reply);
+		reply = NULL;
+	}
+	FREE(inbuf);
+}
+
 /*
  * entry point
  */
 void *uxsock_listen(long ux_sock, void *trigger_data)
 {
-	int rlen;
-	char *inbuf;
-	char *reply;
 	sigset_t mask;
 	int max_pfds = MIN_POLLS + POLLFDS_BASE;
 	/* conf->sequence_nr will be 1 when uxsock_listen is first called */
@@ -504,8 +532,6 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
 		/* see if a client wants to speak to us */
 		for (i = POLLFDS_BASE; i < n_pfds; i++) {
 			if (polls[i].revents & (POLLIN|POLLHUP|POLLERR)) {
-				struct timespec start_time;
-
 				c = NULL;
 				pthread_mutex_lock(&client_lock);
 				list_for_each_entry(tmp, &clients, node) {
@@ -526,36 +552,7 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
 					dead_client(c);
 					continue;
 				}
-				get_monotonic_time(&start_time);
-				if (recv_packet_from_client(c->fd, &inbuf,
-							    uxsock_timeout)
-				    != 0) {
-					dead_client(c);
-					continue;
-				}
-				if (!inbuf) {
-					condlog(4, "recv_packet_from_client "
-						"get null request");
-					continue;
-				}
-				condlog(4, "cli[%d]: Got request [%s]",
-					polls[i].fd, inbuf);
-				uxsock_trigger(inbuf, &reply, &rlen,
-					       _socket_client_is_root(c->fd),
-					       trigger_data);
-				if (reply) {
-					if (send_packet(c->fd,
-							reply) != 0) {
-						dead_client(c);
-					} else {
-						condlog(4, "cli[%d]: "
-							"Reply [%d bytes]",
-							polls[i].fd, rlen);
-					}
-					FREE(reply);
-					reply = NULL;
-				}
-				FREE(inbuf);
+				handle_client(c, trigger_data);
 			}
 		}
 		/* see if we got a non-fatal signal */
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 24/35] multipathd: uxlsnr: use main poll loop for receiving
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (22 preceding siblings ...)
  2021-09-10 11:41 ` [dm-devel] [PATCH 23/35] multipathd: uxlsnr: move client handling to separate function mwilck
@ 2021-09-10 11:41 ` mwilck
  2021-09-16  2:22   ` Benjamin Marzinski
  2021-09-10 11:41 ` [dm-devel] [PATCH 25/35] multipathd: use strbuf in cli_handler functions mwilck
                   ` (10 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:41 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

As a first step towards our state machine, avoid the call to
read_all() via recv_packet_from_client(). handle_client() is now
invoked twice for the same connection. The first time it reads
the command length, and later on it reads the command itself
piece-wise, as sent by the client. This will be just a single
read in most cases, but not always.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/uxlsnr.c | 90 +++++++++++++++++++++++++++++++++++++--------
 1 file changed, 75 insertions(+), 15 deletions(-)

diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
index 2fb23c8..eff4f7b 100644
--- a/multipathd/uxlsnr.c
+++ b/multipathd/uxlsnr.c
@@ -292,6 +292,8 @@ static void handle_inotify(int fd, struct watch_descriptors *wds)
 		condlog(1, "Multipath configuration updated.\nReload multipathd for changes to take effect");
 }
 
+static const struct timespec ts_zero = { .tv_sec = 0, };
+
 static int parse_cmd (char *cmd, char **reply, int *len, void *data,
 		      int timeout)
 {
@@ -394,23 +396,78 @@ static int uxsock_trigger(char *str, char **reply, int *len,
 	return r;
 }
 
+static void set_client_state(struct client *c, int state)
+{
+	switch(state)
+	{
+	case CLT_RECV:
+		reset_strbuf(&c->reply);
+		memset(c->cmd, '\0', sizeof(c->cmd));
+		c->expires = ts_zero;
+		/* fallthrough */
+	case CLT_SEND:
+		/* reuse these fields for next data transfer */
+		c->len = c->cmd_len = 0;
+		break;
+	default:
+		break;
+	}
+	c->state = state;
+}
+
 static void handle_client(struct client *c, void *trigger_data)
 {
 	int rlen;
-	char *inbuf, *reply;
+	char *reply;
+	ssize_t n;
 
-	if (recv_packet_from_client(c->fd, &inbuf, uxsock_timeout) != 0) {
-		dead_client(c);
-		return;
+	switch (c->state) {
+	case CLT_RECV:
+		if (c->cmd_len == 0) {
+			/*
+			 * We got POLLIN; assume that at least the length can
+			 * be read immediately.
+			 */
+			get_monotonic_time(&c->expires);
+			c->expires.tv_sec += uxsock_timeout / 1000;
+			c->expires.tv_nsec += (uxsock_timeout % 1000) * 1000000;
+			normalize_timespec(&c->expires);
+			n = mpath_recv_reply_len(c->fd, 0);
+			if (n == -1) {
+				condlog(1, "%s: cli[%d]: failed to receive reply len",
+					__func__, c->fd);
+				c->error = -ECONNRESET;
+			} else if (n > _MAX_CMD_LEN) {
+				condlog(1, "%s: cli[%d]: overlong command (%zd bytes)",
+					__func__, c->fd, n);
+				c->error = -ECONNRESET;
+			} else {
+				c->cmd_len = n;
+				condlog(4, "%s: cli[%d]: connected", __func__, c->fd);
+			}
+			/* poll for data */
+			return;
+		} else if (c->len < c->cmd_len) {
+			n = recv(c->fd, c->cmd + c->len, c->cmd_len - c->len, 0);
+			if (n <= 0 && errno != EINTR && errno != EAGAIN) {
+				condlog(1, "%s: cli[%d]: error in recv: %m",
+					__func__, c->fd);
+				c->error = -ECONNRESET;
+				return;
+			}
+			c->len += n;
+			if (c->len < c->cmd_len)
+				/* continue polling */
+				return;
+			set_client_state(c, CLT_PARSE);
+		}
+		break;
+	default:
+		break;
 	}
 
-	if (!inbuf) {
-		condlog(4, "recv_packet_from_client get null request");
-		return;
-	}
-
-	condlog(4, "cli[%d]: Got request [%s]", c->fd, inbuf);
-	uxsock_trigger(inbuf, &reply, &rlen,
+	condlog(4, "cli[%d]: Got request [%s]", c->fd, c->cmd);
+	uxsock_trigger(c->cmd, &reply, &rlen,
 		       _socket_client_is_root(c->fd),
 		       trigger_data);
 
@@ -418,11 +475,12 @@ static void handle_client(struct client *c, void *trigger_data)
 		if (send_packet(c->fd, reply) != 0)
 			dead_client(c);
 		else
-			condlog(4, "cli[%d]: Reply [%d bytes]", c->fd, rlen);
-		FREE(reply);
-		reply = NULL;
+			condlog(4, "cli[%d]: Reply [%zu bytes]", c->fd,
+				get_strbuf_len(&c->reply) + 1);
+		reset_strbuf(&c->reply);
 	}
-	FREE(inbuf);
+
+	set_client_state(c, CLT_RECV);
 }
 
 /*
@@ -553,6 +611,8 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
 					continue;
 				}
 				handle_client(c, trigger_data);
+				if (c->error == -ECONNRESET)
+					dead_client(c);
 			}
 		}
 		/* see if we got a non-fatal signal */
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 25/35] multipathd: use strbuf in cli_handler functions
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (23 preceding siblings ...)
  2021-09-10 11:41 ` [dm-devel] [PATCH 24/35] multipathd: uxlsnr: use main poll loop for receiving mwilck
@ 2021-09-10 11:41 ` mwilck
  2021-09-16  2:23   ` Benjamin Marzinski
  2021-09-10 11:41 ` [dm-devel] [PATCH 26/35] multipathd: uxlsnr: check root on connection startup mwilck
                   ` (9 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:41 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

This allows us to simplify callers by not having to track the
reply length separately.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/cli.c          |   7 +-
 multipathd/cli.h          |   6 +-
 multipathd/cli_handlers.c | 316 +++++++++++++++-----------------------
 multipathd/uxlsnr.c       |  49 +++---
 4 files changed, 152 insertions(+), 226 deletions(-)

diff --git a/multipathd/cli.c b/multipathd/cli.c
index 29d6a6e..414f660 100644
--- a/multipathd/cli.c
+++ b/multipathd/cli.c
@@ -419,13 +419,10 @@ do_genhelp(struct strbuf *reply, const char *cmd, int error) {
 }
 
 
-char *genhelp_handler(const char *cmd, int error)
+void genhelp_handler(const char *cmd, int error, struct strbuf *reply)
 {
-	STRBUF_ON_STACK(reply);
-
-	if (do_genhelp(&reply, cmd, error) == -1)
+	if (do_genhelp(reply, cmd, error) == -1)
 		condlog(0, "genhelp_handler: out of memory");
-	return steal_strbuf_str(&reply);
 }
 
 char *
diff --git a/multipathd/cli.h b/multipathd/cli.h
index eed606a..fcb6af0 100644
--- a/multipathd/cli.h
+++ b/multipathd/cli.h
@@ -124,7 +124,9 @@ struct key {
 	int has_param;
 };
 
-typedef int (cli_handler)(void *keywords, char **reply, int *len, void *data);
+struct strbuf;
+
+typedef int (cli_handler)(void *keywords, struct strbuf *reply, void *data);
 
 struct handler {
 	uint64_t fingerprint;
@@ -140,7 +142,7 @@ int __set_handler_callback (uint64_t fp, cli_handler *fn, bool locked);
 
 int get_cmdvec (char *cmd, vector *v);
 struct handler *find_handler_for_cmdvec(const struct _vector *v);
-char *genhelp_handler (const char *cmd, int error);
+void genhelp_handler (const char *cmd, int error, struct strbuf *reply);
 
 int load_keys (void);
 char * get_keyparam (vector v, uint64_t code);
diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
index 1a9c822..f59db3a 100644
--- a/multipathd/cli_handlers.c
+++ b/multipathd/cli_handlers.c
@@ -33,17 +33,9 @@
 #include "strbuf.h"
 #include "cli_handlers.h"
 
-#define SET_REPLY_AND_LEN(__rep, __len, string_literal)			\
-	do {								\
-		*(__rep) = strdup(string_literal);			\
-		*(__len) = *(__rep) ? sizeof(string_literal) : 0;	\
-	} while (0)
-
 static int
-show_paths (char ** r, int * len, struct vectors * vecs, char * style,
-	    int pretty)
+show_paths (struct strbuf *reply, struct vectors *vecs, char *style, int pretty)
 {
-	STRBUF_ON_STACK(reply);
 	int i;
 	struct path * pp;
 	int hdr_len = 0;
@@ -51,61 +43,49 @@ show_paths (char ** r, int * len, struct vectors * vecs, char * style,
 	get_path_layout(vecs->pathvec, 1);
 	foreign_path_layout();
 
-	if (pretty && (hdr_len = snprint_path_header(&reply, style)) < 0)
+	if (pretty && (hdr_len = snprint_path_header(reply, style)) < 0)
 		return 1;
 
 	vector_foreach_slot(vecs->pathvec, pp, i) {
-		if (snprint_path(&reply, style, pp, pretty) < 0)
+		if (snprint_path(reply, style, pp, pretty) < 0)
 			return 1;
 	}
-	if (snprint_foreign_paths(&reply, style, pretty) < 0)
+	if (snprint_foreign_paths(reply, style, pretty) < 0)
 		return 1;
 
-	if (pretty && get_strbuf_len(&reply) == (size_t)hdr_len)
+	if (pretty && get_strbuf_len(reply) == (size_t)hdr_len)
 		/* No output - clear header */
-		truncate_strbuf(&reply, 0);
+		truncate_strbuf(reply, 0);
 
-	*len = (int)get_strbuf_len(&reply) + 1;
-	*r = steal_strbuf_str(&reply);
 	return 0;
 }
 
 static int
-show_path (char ** r, int * len, struct vectors * vecs, struct path *pp,
-	   char * style)
+show_path (struct strbuf *reply, struct vectors *vecs, struct path *pp,
+	   char *style)
 {
-	STRBUF_ON_STACK(reply);
-
 	get_path_layout(vecs->pathvec, 1);
-	if (snprint_path(&reply, style, pp, 0) < 0)
+	if (snprint_path(reply, style, pp, 0) < 0)
 		return 1;
-	*len = (int)get_strbuf_len(&reply) + 1;
-	*r = steal_strbuf_str(&reply);
-
 	return 0;
 }
 
 static int
-show_map_topology (char ** r, int * len, struct multipath * mpp,
-		   struct vectors * vecs)
+show_map_topology (struct strbuf *reply, struct multipath *mpp,
+		   struct vectors *vecs)
 {
-	STRBUF_ON_STACK(reply);
-
 	if (update_multipath(vecs, mpp->alias, 0))
 		return 1;
 
-	if (snprint_multipath_topology(&reply, mpp, 2) < 0)
+	if (snprint_multipath_topology(reply, mpp, 2) < 0)
 		return 1;
-	*len = (int)get_strbuf_len(&reply) + 1;
-	*r = steal_strbuf_str(&reply);
 
 	return 0;
 }
 
 static int
-show_maps_topology (char ** r, int * len, struct vectors * vecs)
+show_maps_topology (struct strbuf *reply, struct vectors * vecs)
 {
-	STRBUF_ON_STACK(reply);
 	int i;
 	struct multipath * mpp;
 
@@ -117,21 +97,18 @@ show_maps_topology (char ** r, int * len, struct vectors * vecs)
 			i--;
 			continue;
 		}
-		if (snprint_multipath_topology(&reply, mpp, 2) < 0)
+		if (snprint_multipath_topology(reply, mpp, 2) < 0)
 			return 1;
 	}
-	if (snprint_foreign_topology(&reply, 2) < 0)
+	if (snprint_foreign_topology(reply, 2) < 0)
 		return 1;
 
-	*len = (int)get_strbuf_len(&reply) + 1;
-	*r = steal_strbuf_str(&reply);
 	return 0;
 }
 
 static int
-show_maps_json (char ** r, int * len, struct vectors * vecs)
+show_maps_json (struct strbuf *reply, struct vectors * vecs)
 {
-	STRBUF_ON_STACK(reply);
 	int i;
 	struct multipath * mpp;
 
@@ -141,45 +118,38 @@ show_maps_json (char ** r, int * len, struct vectors * vecs)
 		}
 	}
 
-	if (snprint_multipath_topology_json(&reply, vecs) < 0)
+	if (snprint_multipath_topology_json(reply, vecs) < 0)
 		return 1;
 
-	*len = (int)get_strbuf_len(&reply) + 1;
-	*r = steal_strbuf_str(&reply);
 	return 0;
 }
 
 static int
-show_map_json (char ** r, int * len, struct multipath * mpp,
-		   struct vectors * vecs)
+show_map_json (struct strbuf *reply, struct multipath * mpp,
+	       struct vectors * vecs)
 {
-	STRBUF_ON_STACK(reply);
-
 	if (update_multipath(vecs, mpp->alias, 0))
 		return 1;
 
-	if (snprint_multipath_map_json(&reply, mpp) < 0)
+	if (snprint_multipath_map_json(reply, mpp) < 0)
 		return 1;
 
-	*len = (int)get_strbuf_len(&reply) + 1;
-	*r = steal_strbuf_str(&reply);
 	return 0;
 }
 
 static int
-show_config (char ** r, int * len, const struct _vector *hwtable,
+show_config (struct strbuf *reply, const struct _vector *hwtable,
 	     const struct _vector *mpvec)
 {
 	struct config *conf;
-	char *reply;
+	int rc;
 
 	conf = get_multipath_config();
 	pthread_cleanup_push(put_multipath_config, conf);
-	reply = snprint_config(conf, len, hwtable, mpvec);
+	rc = __snprint_config(conf, reply, hwtable, mpvec);
 	pthread_cleanup_pop(1);
-	if (reply == NULL)
+	if (rc < 0)
 		return 1;
-	*r = reply;
 	return 0;
 }
 
@@ -195,11 +165,11 @@ reset_stats(struct multipath * mpp)
 }
 
 static int
-cli_list_config (void * v, char ** reply, int * len, void * data)
+cli_list_config (void *v, struct strbuf *reply, void *data)
 {
 	condlog(3, "list config (operator)");
 
-	return show_config(reply, len, NULL, NULL);
+	return show_config(reply, NULL, NULL);
 }
 
 static void v_free(void *x)
@@ -208,9 +178,9 @@ static void v_free(void *x)
 }
 
 static int
-cli_list_config_local (void * v, char ** reply, int * len, void * data)
+cli_list_config_local (void *v, struct strbuf *reply, void *data)
 {
-	struct vectors * vecs = (struct vectors *)data;
+	struct vectors *vecs = (struct vectors *)data;
 	vector hwes;
 	int ret;
 
@@ -218,45 +188,45 @@ cli_list_config_local (void * v, char ** reply, int * len, void * data)
 
 	hwes = get_used_hwes(vecs->pathvec);
 	pthread_cleanup_push(v_free, hwes);
-	ret = show_config(reply, len, hwes, vecs->mpvec);
+	ret = show_config(reply, hwes, vecs->mpvec);
 	pthread_cleanup_pop(1);
 	return ret;
 }
 
 static int
-cli_list_paths (void * v, char ** reply, int * len, void * data)
+cli_list_paths (void *v, struct strbuf *reply, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 
 	condlog(3, "list paths (operator)");
 
-	return show_paths(reply, len, vecs, PRINT_PATH_CHECKER, 1);
+	return show_paths(reply, vecs, PRINT_PATH_CHECKER, 1);
 }
 
 static int
-cli_list_paths_fmt (void * v, char ** reply, int * len, void * data)
+cli_list_paths_fmt (void *v, struct strbuf *reply, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	char * fmt = get_keyparam(v, FMT);
 
 	condlog(3, "list paths (operator)");
 
-	return show_paths(reply, len, vecs, fmt, 1);
+	return show_paths(reply, vecs, fmt, 1);
 }
 
 static int
-cli_list_paths_raw (void * v, char ** reply, int * len, void * data)
+cli_list_paths_raw (void *v, struct strbuf *reply, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	char * fmt = get_keyparam(v, FMT);
 
 	condlog(3, "list paths (operator)");
 
-	return show_paths(reply, len, vecs, fmt, 0);
+	return show_paths(reply, vecs, fmt, 0);
 }
 
 static int
-cli_list_path (void * v, char ** reply, int * len, void * data)
+cli_list_path (void *v, struct strbuf *reply, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	char * param = get_keyparam(v, PATH);
@@ -269,11 +239,11 @@ cli_list_path (void * v, char ** reply, int * len, void * data)
 	if (!pp)
 		return 1;
 
-	return show_path(reply, len, vecs, pp, "%o");
+	return show_path(reply, vecs, pp, "%o");
 }
 
 static int
-cli_list_map_topology (void * v, char ** reply, int * len, void * data)
+cli_list_map_topology (void *v, struct strbuf *reply, void *data)
 {
 	struct multipath * mpp;
 	struct vectors * vecs = (struct vectors *)data;
@@ -288,21 +258,21 @@ cli_list_map_topology (void * v, char ** reply, int * len, void * data)
 
 	condlog(3, "list multipath %s (operator)", param);
 
-	return show_map_topology(reply, len, mpp, vecs);
+	return show_map_topology(reply, mpp, vecs);
 }
 
 static int
-cli_list_maps_topology (void * v, char ** reply, int * len, void * data)
+cli_list_maps_topology (void *v, struct strbuf *reply, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 
 	condlog(3, "list multipaths (operator)");
 
-	return show_maps_topology(reply, len, vecs);
+	return show_maps_topology(reply, vecs);
 }
 
 static int
-cli_list_map_json (void * v, char ** reply, int * len, void * data)
+cli_list_map_json (void *v, struct strbuf *reply, void *data)
 {
 	struct multipath * mpp;
 	struct vectors * vecs = (struct vectors *)data;
@@ -317,78 +287,61 @@ cli_list_map_json (void * v, char ** reply, int * len, void * data)
 
 	condlog(3, "list multipath json %s (operator)", param);
 
-	return show_map_json(reply, len, mpp, vecs);
+	return show_map_json(reply, mpp, vecs);
 }
 
 static int
-cli_list_maps_json (void * v, char ** reply, int * len, void * data)
+cli_list_maps_json (void *v, struct strbuf *reply, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 
 	condlog(3, "list multipaths json (operator)");
 
-	return show_maps_json(reply, len, vecs);
+	return show_maps_json(reply, vecs);
 }
 
 static int
-cli_list_wildcards (void * v, char ** reply, int * len, void * data)
+cli_list_wildcards (void *v, struct strbuf *reply, void *data)
 {
-	STRBUF_ON_STACK(buf);
-
-	if (snprint_wildcards(&buf) < 0)
+	if (snprint_wildcards(reply) < 0)
 		return 1;
 
-	*len = get_strbuf_len(&buf) + 1;
-	*reply = steal_strbuf_str(&buf);
 	return 0;
 }
 
 static int
-show_status (char ** r, int *len, struct vectors * vecs)
+show_status (struct strbuf *reply, struct vectors *vecs)
 {
-	STRBUF_ON_STACK(reply);
-
-	if (snprint_status(&reply, vecs) < 0)
+	if (snprint_status(reply, vecs) < 0)
 		return 1;
 
-	*len = get_strbuf_len(&reply) + 1;
-	*r = steal_strbuf_str(&reply);
 	return 0;
 }
 
 static int
-show_daemon (char ** r, int *len)
+show_daemon (struct strbuf *reply)
 {
-	STRBUF_ON_STACK(reply);
-
-	if (print_strbuf(&reply, "pid %d %s\n",
+	if (print_strbuf(reply, "pid %d %s\n",
 			 daemon_pid, daemon_status()) < 0)
 		return 1;
 
-	*len = get_strbuf_len(&reply) + 1;
-	*r = steal_strbuf_str(&reply);
 	return 0;
 }
 
 static int
-show_map (char ** r, int *len, struct multipath * mpp, char * style,
+show_map (struct strbuf *reply, struct multipath *mpp, char *style,
 	  int pretty)
 {
-	STRBUF_ON_STACK(reply);
-
-	if (snprint_multipath(&reply, style, mpp, pretty) < 0)
+	if (snprint_multipath(reply, style, mpp, pretty) < 0)
 		return 1;
 
-	*len = get_strbuf_len(&reply) + 1;
-	*r = steal_strbuf_str(&reply);
 	return 0;
 }
 
 static int
-show_maps (char ** r, int *len, struct vectors * vecs, char * style,
+show_maps (struct strbuf *reply, struct vectors *vecs, char *style,
 	   int pretty)
 {
-	STRBUF_ON_STACK(reply);
 	int i;
 	struct multipath * mpp;
 	int hdr_len = 0;
@@ -396,7 +349,7 @@ show_maps (char ** r, int *len, struct vectors * vecs, char * style,
 	get_multipath_layout(vecs->mpvec, 1);
 	foreign_multipath_layout();
 
-	if (pretty && (hdr_len = snprint_multipath_header(&reply, style)) < 0)
+	if (pretty && (hdr_len = snprint_multipath_header(reply, style)) < 0)
 		return 1;
 
 	vector_foreach_slot(vecs->mpvec, mpp, i) {
@@ -404,45 +357,43 @@ show_maps (char ** r, int *len, struct vectors * vecs, char * style,
 			i--;
 			continue;
 		}
-		if (snprint_multipath(&reply, style, mpp, pretty) < 0)
+		if (snprint_multipath(reply, style, mpp, pretty) < 0)
 			return 1;
 	}
-	if (snprint_foreign_multipaths(&reply, style, pretty) < 0)
+	if (snprint_foreign_multipaths(reply, style, pretty) < 0)
 		return 1;
 
-	if (pretty && get_strbuf_len(&reply) == (size_t)hdr_len)
+	if (pretty && get_strbuf_len(reply) == (size_t)hdr_len)
 		/* No output - clear header */
-		truncate_strbuf(&reply, 0);
+		truncate_strbuf(reply, 0);
 
-	*len = (int)get_strbuf_len(&reply) + 1;
-	*r = steal_strbuf_str(&reply);
 	return 0;
 }
 
 static int
-cli_list_maps_fmt (void * v, char ** reply, int * len, void * data)
+cli_list_maps_fmt (void *v, struct strbuf *reply, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	char * fmt = get_keyparam(v, FMT);
 
 	condlog(3, "list maps (operator)");
 
-	return show_maps(reply, len, vecs, fmt, 1);
+	return show_maps(reply, vecs, fmt, 1);
 }
 
 static int
-cli_list_maps_raw (void * v, char ** reply, int * len, void * data)
+cli_list_maps_raw (void *v, struct strbuf *reply, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	char * fmt = get_keyparam(v, FMT);
 
 	condlog(3, "list maps (operator)");
 
-	return show_maps(reply, len, vecs, fmt, 0);
+	return show_maps(reply, vecs, fmt, 0);
 }
 
 static int
-cli_list_map_fmt (void * v, char ** reply, int * len, void * data)
+cli_list_map_fmt (void *v, struct strbuf *reply, void *data)
 {
 	struct multipath * mpp;
 	struct vectors * vecs = (struct vectors *)data;
@@ -458,59 +409,59 @@ cli_list_map_fmt (void * v, char ** reply, int * len, void * data)
 
 	condlog(3, "list map %s fmt %s (operator)", param, fmt);
 
-	return show_map(reply, len, mpp, fmt, 1);
+	return show_map(reply, mpp, fmt, 1);
 }
 
 static int
-cli_list_maps (void * v, char ** reply, int * len, void * data)
+cli_list_maps (void *v, struct strbuf *reply, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 
 	condlog(3, "list maps (operator)");
 
-	return show_maps(reply, len, vecs, PRINT_MAP_NAMES, 1);
+	return show_maps(reply, vecs, PRINT_MAP_NAMES, 1);
 }
 
 static int
-cli_list_status (void * v, char ** reply, int * len, void * data)
+cli_list_status (void *v, struct strbuf *reply, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 
 	condlog(3, "list status (operator)");
 
-	return show_status(reply, len, vecs);
+	return show_status(reply, vecs);
 }
 
 static int
-cli_list_maps_status (void * v, char ** reply, int * len, void * data)
+cli_list_maps_status (void *v, struct strbuf *reply, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 
 	condlog(3, "list maps status (operator)");
 
-	return show_maps(reply, len, vecs, PRINT_MAP_STATUS, 1);
+	return show_maps(reply, vecs, PRINT_MAP_STATUS, 1);
 }
 
 static int
-cli_list_maps_stats (void * v, char ** reply, int * len, void * data)
+cli_list_maps_stats (void *v, struct strbuf *reply, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 
 	condlog(3, "list maps stats (operator)");
 
-	return show_maps(reply, len, vecs, PRINT_MAP_STATS, 1);
+	return show_maps(reply, vecs, PRINT_MAP_STATS, 1);
 }
 
 static int
-cli_list_daemon (void * v, char ** reply, int * len, void * data)
+cli_list_daemon (void *v, struct strbuf *reply, void *data)
 {
 	condlog(3, "list daemon (operator)");
 
-	return show_daemon(reply, len);
+	return show_daemon(reply);
 }
 
 static int
-cli_reset_maps_stats (void * v, char ** reply, int * len, void * data)
+cli_reset_maps_stats (void *v, struct strbuf *reply, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	int i;
@@ -525,7 +476,7 @@ cli_reset_maps_stats (void * v, char ** reply, int * len, void * data)
 }
 
 static int
-cli_reset_map_stats (void * v, char ** reply, int * len, void * data)
+cli_reset_map_stats (void *v, struct strbuf *reply, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	struct multipath * mpp;
@@ -543,7 +494,7 @@ cli_reset_map_stats (void * v, char ** reply, int * len, void * data)
 }
 
 static int
-cli_add_path (void * v, char ** reply, int * len, void * data)
+cli_add_path (void *v, struct strbuf *reply, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	char * param = get_keyparam(v, PATH);
@@ -653,13 +604,13 @@ cli_add_path (void * v, char ** reply, int * len, void * data)
 	}
 	return ev_add_path(pp, vecs, 1);
 blacklisted:
-	SET_REPLY_AND_LEN(reply, len, "blacklisted\n");
+	append_strbuf_str(reply, "blacklisted\n");
 	condlog(2, "%s: path blacklisted", param);
 	return 0;
 }
 
 static int
-cli_del_path (void * v, char ** reply, int * len, void * data)
+cli_del_path (void * v, struct strbuf *reply, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	char * param = get_keyparam(v, PATH);
@@ -675,14 +626,14 @@ cli_del_path (void * v, char ** reply, int * len, void * data)
 	}
 	ret = ev_remove_path(pp, vecs, 1);
 	if (ret == REMOVE_PATH_DELAY)
-		SET_REPLY_AND_LEN(reply, len, "delayed\n");
+		append_strbuf_str(reply, "delayed\n");
 	else if (ret == REMOVE_PATH_MAP_ERROR)
-		SET_REPLY_AND_LEN(reply, len, "map reload error. removed\n");
+		append_strbuf_str(reply, "map reload error. removed\n");
 	return (ret == REMOVE_PATH_FAILURE);
 }
 
 static int
-cli_add_map (void * v, char ** reply, int * len, void * data)
+cli_add_map (void * v, struct strbuf *reply, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	char * param = get_keyparam(v, MAP);
@@ -702,7 +653,7 @@ cli_add_map (void * v, char ** reply, int * len, void * data)
 		invalid = 1;
 	pthread_cleanup_pop(1);
 	if (invalid) {
-		SET_REPLY_AND_LEN(reply, len, "blacklisted\n");
+		append_strbuf_str(reply, "blacklisted\n");
 		condlog(2, "%s: map blacklisted", param);
 		return 1;
 	}
@@ -742,7 +693,7 @@ cli_add_map (void * v, char ** reply, int * len, void * data)
 }
 
 static int
-cli_del_map (void * v, char ** reply, int * len, void * data)
+cli_del_map (void * v, struct strbuf *reply, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	char * param = get_keyparam(v, MAP);
@@ -764,14 +715,14 @@ cli_del_map (void * v, char ** reply, int * len, void * data)
 	}
 	rc = ev_remove_map(param, alias, minor, vecs);
 	if (rc == 2)
-		*reply = strdup("delayed");
+		append_strbuf_str(reply, "delayed");
 
 	FREE(alias);
 	return rc;
 }
 
 static int
-cli_del_maps (void *v, char **reply, int *len, void *data)
+cli_del_maps (void *v, struct strbuf *reply, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	struct multipath *mpp;
@@ -790,7 +741,7 @@ cli_del_maps (void *v, char **reply, int *len, void *data)
 }
 
 static int
-cli_reload(void *v, char **reply, int *len, void *data)
+cli_reload(void *v, struct strbuf *reply, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	char * mapname = get_keyparam(v, MAP);
@@ -843,7 +794,7 @@ static int resize_map(struct multipath *mpp, unsigned long long size,
 }
 
 static int
-cli_resize(void *v, char **reply, int *len, void *data)
+cli_resize(void *v, struct strbuf *reply, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	char * mapname = get_keyparam(v, MAP);
@@ -908,7 +859,7 @@ cli_resize(void *v, char **reply, int *len, void *data)
 }
 
 static int
-cli_force_no_daemon_q(void * v, char ** reply, int * len, void * data)
+cli_force_no_daemon_q(void * v, struct strbuf *reply, void * data)
 {
 	struct config *conf;
 
@@ -921,7 +872,7 @@ cli_force_no_daemon_q(void * v, char ** reply, int * len, void * data)
 }
 
 static int
-cli_restore_no_daemon_q(void * v, char ** reply, int * len, void * data)
+cli_restore_no_daemon_q(void * v, struct strbuf *reply, void * data)
 {
 	struct config *conf;
 
@@ -934,7 +885,7 @@ cli_restore_no_daemon_q(void * v, char ** reply, int * len, void * data)
 }
 
 static int
-cli_restore_queueing(void *v, char **reply, int *len, void *data)
+cli_restore_queueing(void *v, struct strbuf *reply, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	char * mapname = get_keyparam(v, MAP);
@@ -975,7 +926,7 @@ cli_restore_queueing(void *v, char **reply, int *len, void *data)
 }
 
 static int
-cli_restore_all_queueing(void *v, char **reply, int *len, void *data)
+cli_restore_all_queueing(void *v, struct strbuf *reply, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	struct multipath *mpp;
@@ -997,7 +948,7 @@ cli_restore_all_queueing(void *v, char **reply, int *len, void *data)
 }
 
 static int
-cli_disable_queueing(void *v, char **reply, int *len, void *data)
+cli_disable_queueing(void *v, struct strbuf *reply, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	char * mapname = get_keyparam(v, MAP);
@@ -1026,7 +977,7 @@ cli_disable_queueing(void *v, char **reply, int *len, void *data)
 }
 
 static int
-cli_disable_all_queueing(void *v, char **reply, int *len, void *data)
+cli_disable_all_queueing(void *v, struct strbuf *reply, void *data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	struct multipath *mpp;
@@ -1045,7 +996,7 @@ cli_disable_all_queueing(void *v, char **reply, int *len, void *data)
 }
 
 static int
-cli_switch_group(void * v, char ** reply, int * len, void * data)
+cli_switch_group(void * v, struct strbuf *reply, void * data)
 {
 	char * mapname = get_keyparam(v, MAP);
 	int groupnum = atoi(get_keyparam(v, GROUP));
@@ -1057,7 +1008,7 @@ cli_switch_group(void * v, char ** reply, int * len, void * data)
 }
 
 static int
-cli_reconfigure(void * v, char ** reply, int * len, void * data)
+cli_reconfigure(void * v, struct strbuf *reply, void * data)
 {
 	condlog(2, "reconfigure (operator)");
 
@@ -1066,7 +1017,7 @@ cli_reconfigure(void * v, char ** reply, int * len, void * data)
 }
 
 static int
-cli_suspend(void * v, char ** reply, int * len, void * data)
+cli_suspend(void * v, struct strbuf *reply, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	char * param = get_keyparam(v, MAP);
@@ -1096,7 +1047,7 @@ cli_suspend(void * v, char ** reply, int * len, void * data)
 }
 
 static int
-cli_resume(void * v, char ** reply, int * len, void * data)
+cli_resume(void * v, struct strbuf *reply, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	char * param = get_keyparam(v, MAP);
@@ -1128,7 +1079,7 @@ cli_resume(void * v, char ** reply, int * len, void * data)
 }
 
 static int
-cli_reinstate(void * v, char ** reply, int * len, void * data)
+cli_reinstate(void * v, struct strbuf *reply, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	char * param = get_keyparam(v, PATH);
@@ -1151,7 +1102,7 @@ cli_reinstate(void * v, char ** reply, int * len, void * data)
 }
 
 static int
-cli_reassign (void * v, char ** reply, int * len, void * data)
+cli_reassign (void * v, struct strbuf *reply, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	char * param = get_keyparam(v, MAP);
@@ -1175,7 +1126,7 @@ cli_reassign (void * v, char ** reply, int * len, void * data)
 }
 
 static int
-cli_fail(void * v, char ** reply, int * len, void * data)
+cli_fail(void * v, struct strbuf *reply, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	char * param = get_keyparam(v, PATH);
@@ -1204,72 +1155,65 @@ cli_fail(void * v, char ** reply, int * len, void * data)
 }
 
 static int
-show_blacklist (char ** r, int * len)
+show_blacklist (struct strbuf *reply)
 {
-	STRBUF_ON_STACK(reply);
 	struct config *conf;
 	bool fail;
 
 	conf = get_multipath_config();
 	pthread_cleanup_push(put_multipath_config, conf);
-	fail = snprint_blacklist_report(conf, &reply) < 0;
+	fail = snprint_blacklist_report(conf, reply) < 0;
 	pthread_cleanup_pop(1);
 
 	if (fail)
 		return 1;
 
-	*len = (int)get_strbuf_len(&reply) + 1;
-	*r = steal_strbuf_str(&reply);
 	return 0;
 }
 
 static int
-cli_list_blacklist (void * v, char ** reply, int * len, void * data)
+cli_list_blacklist (void * v, struct strbuf *reply, void * data)
 {
 	condlog(3, "list blacklist (operator)");
 
-	return show_blacklist(reply, len);
+	return show_blacklist(reply);
 }
 
 static int
-show_devices (char ** r, int * len, struct vectors *vecs)
+show_devices (struct strbuf *reply, struct vectors *vecs)
 {
-	STRBUF_ON_STACK(reply);
 	struct config *conf;
 	bool fail;
 
 	conf = get_multipath_config();
 	pthread_cleanup_push(put_multipath_config, conf);
-	fail = snprint_devices(conf, &reply, vecs) < 0;
+	fail = snprint_devices(conf, reply, vecs) < 0;
 	pthread_cleanup_pop(1);
 
 	if (fail)
 		return 1;
 
-	*len = (int)get_strbuf_len(&reply) + 1;
-	*r = steal_strbuf_str(&reply);
-
 	return 0;
 }
 
 static int
-cli_list_devices (void * v, char ** reply, int * len, void * data)
+cli_list_devices (void * v, struct strbuf *reply, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 
 	condlog(3, "list devices (operator)");
 
-	return show_devices(reply, len, vecs);
+	return show_devices(reply, vecs);
 }
 
 static int
-cli_quit (void * v, char ** reply, int * len, void * data)
+cli_quit (void * v, struct strbuf *reply, void * data)
 {
 	return 0;
 }
 
 static int
-cli_shutdown (void * v, char ** reply, int * len, void * data)
+cli_shutdown (void * v, struct strbuf *reply, void * data)
 {
 	condlog(3, "shutdown (operator)");
 	exit_daemon();
@@ -1277,7 +1221,7 @@ cli_shutdown (void * v, char ** reply, int * len, void * data)
 }
 
 static int
-cli_getprstatus (void * v, char ** reply, int * len, void * data)
+cli_getprstatus (void * v, struct strbuf *reply, void * data)
 {
 	struct multipath * mpp;
 	struct vectors * vecs = (struct vectors *)data;
@@ -1292,17 +1236,16 @@ cli_getprstatus (void * v, char ** reply, int * len, void * data)
 
 	condlog(3, "%s: prflag = %u", param, (unsigned int)mpp->prflag);
 
-	*len = asprintf(reply, "%d", mpp->prflag);
-	if (*len < 0)
+	if (print_strbuf(reply, "%d", mpp->prflag) < 0)
 		return 1;
 
-	condlog(3, "%s: reply = %s", param, *reply);
+	condlog(3, "%s: reply = %s", param, get_strbuf_str(reply));
 
 	return 0;
 }
 
 static int
-cli_setprstatus(void * v, char ** reply, int * len, void * data)
+cli_setprstatus(void * v, struct strbuf *reply, void * data)
 {
 	struct multipath * mpp;
 	struct vectors * vecs = (struct vectors *)data;
@@ -1325,7 +1268,7 @@ cli_setprstatus(void * v, char ** reply, int * len, void * data)
 }
 
 static int
-cli_unsetprstatus(void * v, char ** reply, int * len, void * data)
+cli_unsetprstatus(void * v, struct strbuf *reply, void * data)
 {
 	struct multipath * mpp;
 	struct vectors * vecs = (struct vectors *)data;
@@ -1347,7 +1290,7 @@ cli_unsetprstatus(void * v, char ** reply, int * len, void * data)
 }
 
 static int
-cli_getprkey(void * v, char ** reply, int * len, void * data)
+cli_getprkey(void * v, struct strbuf *reply, void * data)
 {
 	struct multipath * mpp;
 	struct vectors * vecs = (struct vectors *)data;
@@ -1361,25 +1304,20 @@ cli_getprkey(void * v, char ** reply, int * len, void * data)
 	if (!mpp)
 		return 1;
 
-	*reply = malloc(26);
-	if (!*reply)
-		return 1;
-
 	key = get_be64(mpp->reservation_key);
 	if (!key) {
-		sprintf(*reply, "none\n");
-		*len = sizeof("none\n");
+		append_strbuf_str(reply, "none\n");
 		return 0;
 	}
 
-	/* This snprintf() can't overflow - PRIx64 needs max 16 chars */
-	*len = snprintf(*reply, 26, "0x%" PRIx64 "%s\n", key,
-			mpp->sa_flags & MPATH_F_APTPL_MASK ? ":aptpl" : "") + 1;
+	if (print_strbuf(reply, "0x%" PRIx64 "%s\n", key,
+			 mpp->sa_flags & MPATH_F_APTPL_MASK ? ":aptpl" : "") < 0)
+		return 1;
 	return 0;
 }
 
 static int
-cli_unsetprkey(void * v, char ** reply, int * len, void * data)
+cli_unsetprkey(void * v, struct strbuf *reply, void * data)
 {
 	struct multipath * mpp;
 	struct vectors * vecs = (struct vectors *)data;
@@ -1403,7 +1341,7 @@ cli_unsetprkey(void * v, char ** reply, int * len, void * data)
 }
 
 static int
-cli_setprkey(void * v, char ** reply, int * len, void * data)
+cli_setprkey(void * v, struct strbuf *reply, void * data)
 {
 	struct multipath * mpp;
 	struct vectors * vecs = (struct vectors *)data;
@@ -1434,7 +1372,7 @@ cli_setprkey(void * v, char ** reply, int * len, void * data)
 	return ret;
 }
 
-static int cli_set_marginal(void * v, char ** reply, int * len, void * data)
+static int cli_set_marginal(void * v, struct strbuf *reply, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	char * param = get_keyparam(v, PATH);
@@ -1461,7 +1399,7 @@ static int cli_set_marginal(void * v, char ** reply, int * len, void * data)
 	return reload_and_sync_map(pp->mpp, vecs, 0);
 }
 
-static int cli_unset_marginal(void * v, char ** reply, int * len, void * data)
+static int cli_unset_marginal(void * v, struct strbuf *reply, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	char * param = get_keyparam(v, PATH);
@@ -1488,7 +1426,7 @@ static int cli_unset_marginal(void * v, char ** reply, int * len, void * data)
 	return reload_and_sync_map(pp->mpp, vecs, 0);
 }
 
-static int cli_unset_all_marginal(void * v, char ** reply, int * len, void * data)
+static int cli_unset_all_marginal(void * v, struct strbuf *reply, void * data)
 {
 	struct vectors * vecs = (struct vectors *)data;
 	char * mapname = get_keyparam(v, MAP);
diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
index eff4f7b..9a945ea 100644
--- a/multipathd/uxlsnr.c
+++ b/multipathd/uxlsnr.c
@@ -294,7 +294,7 @@ static void handle_inotify(int fd, struct watch_descriptors *wds)
 
 static const struct timespec ts_zero = { .tv_sec = 0, };
 
-static int parse_cmd (char *cmd, char **reply, int *len, void *data,
+static int parse_cmd (char *cmd, struct strbuf *reply, void *data,
 		      int timeout)
 {
 	int r;
@@ -305,10 +305,9 @@ static int parse_cmd (char *cmd, char **reply, int *len, void *data,
 	r = get_cmdvec(cmd, &cmdvec);
 
 	if (r) {
-		*reply = genhelp_handler(cmd, r);
-		if (*reply == NULL)
+		genhelp_handler(cmd, r, reply);
+		if (get_strbuf_len(reply) == 0)
 			return EINVAL;
-		*len = strlen(*reply) + 1;
 		return 0;
 	}
 
@@ -316,10 +315,9 @@ static int parse_cmd (char *cmd, char **reply, int *len, void *data,
 
 	if (!h || !h->fn) {
 		free_keys(cmdvec);
-		*reply = genhelp_handler(cmd, EINVAL);
-		if (*reply == NULL)
+		genhelp_handler(cmd, EINVAL, reply);
+		if (get_strbuf_len(reply) == 0)
 			return EINVAL;
-		*len = strlen(*reply) + 1;
 		return 0;
 	}
 
@@ -345,50 +343,42 @@ static int parse_cmd (char *cmd, char **reply, int *len, void *data,
 		if (r == 0) {
 			locked = 1;
 			pthread_testcancel();
-			r = h->fn(cmdvec, reply, len, data);
+			r = h->fn(cmdvec, reply, data);
 		}
 		pthread_cleanup_pop(locked);
 	} else
-		r = h->fn(cmdvec, reply, len, data);
+		r = h->fn(cmdvec, reply, data);
 	free_keys(cmdvec);
 
 	return r;
 }
 
-static int uxsock_trigger(char *str, char **reply, int *len,
+static int uxsock_trigger(char *str, struct strbuf *reply,
 			  bool is_root, void *trigger_data)
 {
 	struct vectors * vecs;
 	int r;
 
-	*reply = NULL;
-	*len = 0;
 	vecs = (struct vectors *)trigger_data;
 
 	if ((str != NULL) && (is_root == false) &&
 	    (strncmp(str, "list", strlen("list")) != 0) &&
 	    (strncmp(str, "show", strlen("show")) != 0)) {
-		*reply = STRDUP("permission deny: need to be root");
-		if (*reply)
-			*len = strlen(*reply) + 1;
+		append_strbuf_str(reply, "permission deny: need to be root");
 		return 1;
 	}
 
-	r = parse_cmd(str, reply, len, vecs, uxsock_timeout / 1000);
+	r = parse_cmd(str, reply, vecs, uxsock_timeout / 1000);
 
 	if (r > 0) {
 		if (r == ETIMEDOUT)
-			*reply = STRDUP("timeout\n");
+			append_strbuf_str(reply, "timeout\n");
 		else
-			*reply = STRDUP("fail\n");
-		if (*reply)
-			*len = strlen(*reply) + 1;
+			append_strbuf_str(reply, "fail\n");
 		r = 1;
 	}
-	else if (!r && *len == 0) {
-		*reply = STRDUP("ok\n");
-		if (*reply)
-			*len = strlen(*reply) + 1;
+	else if (!r && get_strbuf_len(reply) == 0) {
+		append_strbuf_str(reply, "ok\n");
 		r = 0;
 	}
 	/* else if (r < 0) leave *reply alone */
@@ -417,8 +407,6 @@ static void set_client_state(struct client *c, int state)
 
 static void handle_client(struct client *c, void *trigger_data)
 {
-	int rlen;
-	char *reply;
 	ssize_t n;
 
 	switch (c->state) {
@@ -467,12 +455,13 @@ static void handle_client(struct client *c, void *trigger_data)
 	}
 
 	condlog(4, "cli[%d]: Got request [%s]", c->fd, c->cmd);
-	uxsock_trigger(c->cmd, &reply, &rlen,
-		       _socket_client_is_root(c->fd),
+	uxsock_trigger(c->cmd, &c->reply, _socket_client_is_root(c->fd),
 		       trigger_data);
 
-	if (reply) {
-		if (send_packet(c->fd, reply) != 0)
+	if (get_strbuf_len(&c->reply) > 0) {
+		const char *buf = get_strbuf_str(&c->reply);
+
+		if (send_packet(c->fd, buf) != 0)
 			dead_client(c);
 		else
 			condlog(4, "cli[%d]: Reply [%zu bytes]", c->fd,
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 26/35] multipathd: uxlsnr: check root on connection startup
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (24 preceding siblings ...)
  2021-09-10 11:41 ` [dm-devel] [PATCH 25/35] multipathd: use strbuf in cli_handler functions mwilck
@ 2021-09-10 11:41 ` mwilck
  2021-09-16  2:23   ` Benjamin Marzinski
  2021-09-10 11:41 ` [dm-devel] [PATCH 27/35] multipathd: uxlsnr: pass struct client to uxsock_trigger() and parse_cmd() mwilck
                   ` (8 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:41 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

The SO_PEERCRED socket option returns "the credentials that were
in effect at the time of the call to connect(2)" (see unix(7)).
So we might as well fetch these credentials at that time.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/uxlsnr.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
index 9a945ea..9cf6964 100644
--- a/multipathd/uxlsnr.c
+++ b/multipathd/uxlsnr.c
@@ -92,8 +92,6 @@ static struct pollfd *polls;
 static int notify_fd = -1;
 static char *watch_config_dir;
 
-static bool _socket_client_is_root(int fd);
-
 static bool _socket_client_is_root(int fd)
 {
 	socklen_t len = 0;
@@ -132,6 +130,7 @@ static void new_client(int ux_sock)
 	INIT_LIST_HEAD(&c->node);
 	c->fd = fd;
 	c->state = CLT_RECV;
+	c->is_root = _socket_client_is_root(c->fd);
 
 	/* put it in our linked list */
 	pthread_mutex_lock(&client_lock);
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 27/35] multipathd: uxlsnr: pass struct client to uxsock_trigger() and parse_cmd()
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (25 preceding siblings ...)
  2021-09-10 11:41 ` [dm-devel] [PATCH 26/35] multipathd: uxlsnr: check root on connection startup mwilck
@ 2021-09-10 11:41 ` mwilck
  2021-09-16  2:28   ` Benjamin Marzinski
  2021-09-10 11:41 ` [dm-devel] [PATCH 28/35] multipathd: uxlsnr: move handler execution to separate function mwilck
                   ` (7 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:41 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

As a next step towards the state machine, give the handler functions
access to the state of the client connection.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/uxlsnr.c | 61 +++++++++++++++++++++------------------------
 1 file changed, 29 insertions(+), 32 deletions(-)

diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
index 9cf6964..65cb5ca 100644
--- a/multipathd/uxlsnr.c
+++ b/multipathd/uxlsnr.c
@@ -293,31 +293,28 @@ static void handle_inotify(int fd, struct watch_descriptors *wds)
 
 static const struct timespec ts_zero = { .tv_sec = 0, };
 
-static int parse_cmd (char *cmd, struct strbuf *reply, void *data,
-		      int timeout)
+static int parse_cmd (struct client *c, void *data, int timeout)
 {
 	int r;
 	struct handler * h;
-	vector cmdvec = NULL;
 	struct timespec tmo;
 
-	r = get_cmdvec(cmd, &cmdvec);
+	r = get_cmdvec(c->cmd, &c->cmdvec);
 
 	if (r) {
-		genhelp_handler(cmd, r, reply);
-		if (get_strbuf_len(reply) == 0)
+		genhelp_handler(c->cmd, r, &c->reply);
+		if (get_strbuf_len(&c->reply) == 0)
 			return EINVAL;
 		return 0;
 	}
 
-	h = find_handler_for_cmdvec(cmdvec);
+	h = find_handler_for_cmdvec(c->cmdvec);
 
 	if (!h || !h->fn) {
-		free_keys(cmdvec);
-		genhelp_handler(cmd, EINVAL, reply);
-		if (get_strbuf_len(reply) == 0)
-			return EINVAL;
-		return 0;
+		genhelp_handler(c->cmd, EINVAL, &c->reply);
+		if (get_strbuf_len(&c->reply) == 0)
+			r = EINVAL;
+		goto free_cmdvec;
 	}
 
 	/*
@@ -342,46 +339,47 @@ static int parse_cmd (char *cmd, struct strbuf *reply, void *data,
 		if (r == 0) {
 			locked = 1;
 			pthread_testcancel();
-			r = h->fn(cmdvec, reply, data);
+			r = h->fn(c->cmdvec, &c->reply, data);
 		}
 		pthread_cleanup_pop(locked);
 	} else
-		r = h->fn(cmdvec, reply, data);
-	free_keys(cmdvec);
+		r = h->fn(c->cmdvec, &c->reply, data);
+
+free_cmdvec:
+	free_keys(c->cmdvec);
+	c->cmdvec = NULL;
 
 	return r;
 }
 
-static int uxsock_trigger(char *str, struct strbuf *reply,
-			  bool is_root, void *trigger_data)
+static int uxsock_trigger(struct client *c, void *trigger_data)
 {
 	struct vectors * vecs;
-	int r;
+	int r = 1;
 
 	vecs = (struct vectors *)trigger_data;
 
-	if ((str != NULL) && (is_root == false) &&
-	    (strncmp(str, "list", strlen("list")) != 0) &&
-	    (strncmp(str, "show", strlen("show")) != 0)) {
-		append_strbuf_str(reply, "permission deny: need to be root");
-		return 1;
+
+	if (!c->is_root &&
+	    (strncmp(c->cmd, "list", strlen("list")) != 0) &&
+	    (strncmp(c->cmd, "show", strlen("show")) != 0)) {
+		append_strbuf_str(&c->reply, "permission deny: need to be root");
+		return r;
 	}
 
-	r = parse_cmd(str, reply, vecs, uxsock_timeout / 1000);
+	r = parse_cmd(c, vecs, uxsock_timeout / 1000);
 
 	if (r > 0) {
 		if (r == ETIMEDOUT)
-			append_strbuf_str(reply, "timeout\n");
+			append_strbuf_str(&c->reply, "timeout\n");
 		else
-			append_strbuf_str(reply, "fail\n");
-		r = 1;
+			append_strbuf_str(&c->reply, "fail\n");
 	}
-	else if (!r && get_strbuf_len(reply) == 0) {
-		append_strbuf_str(reply, "ok\n");
+	else if (!r && get_strbuf_len(&c->reply) == 0) {
+		append_strbuf_str(&c->reply, "ok\n");
 		r = 0;
 	}
 	/* else if (r < 0) leave *reply alone */
-
 	return r;
 }
 
@@ -454,8 +452,7 @@ static void handle_client(struct client *c, void *trigger_data)
 	}
 
 	condlog(4, "cli[%d]: Got request [%s]", c->fd, c->cmd);
-	uxsock_trigger(c->cmd, &c->reply, _socket_client_is_root(c->fd),
-		       trigger_data);
+	uxsock_trigger(c, trigger_data);
 
 	if (get_strbuf_len(&c->reply) > 0) {
 		const char *buf = get_strbuf_str(&c->reply);
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 28/35] multipathd: uxlsnr: move handler execution to separate function
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (26 preceding siblings ...)
  2021-09-10 11:41 ` [dm-devel] [PATCH 27/35] multipathd: uxlsnr: pass struct client to uxsock_trigger() and parse_cmd() mwilck
@ 2021-09-10 11:41 ` mwilck
  2021-09-16  2:28   ` Benjamin Marzinski
  2021-09-10 11:41 ` [dm-devel] [PATCH 29/35] multipathd: uxlsnr: use parser to determine non-root commands mwilck
                   ` (6 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:41 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

Move the actual execution of the handler out of parse_cmd(). For now,
we do it in uxsock_trigger().

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/uxlsnr.c | 47 ++++++++++++++++++++++++++++-----------------
 1 file changed, 29 insertions(+), 18 deletions(-)

diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
index 65cb5ca..cfff0ae 100644
--- a/multipathd/uxlsnr.c
+++ b/multipathd/uxlsnr.c
@@ -293,11 +293,9 @@ static void handle_inotify(int fd, struct watch_descriptors *wds)
 
 static const struct timespec ts_zero = { .tv_sec = 0, };
 
-static int parse_cmd (struct client *c, void *data, int timeout)
+static int parse_cmd(struct client *c)
 {
 	int r;
-	struct handler * h;
-	struct timespec tmo;
 
 	r = get_cmdvec(c->cmd, &c->cmdvec);
 
@@ -308,26 +306,35 @@ static int parse_cmd (struct client *c, void *data, int timeout)
 		return 0;
 	}
 
-	h = find_handler_for_cmdvec(c->cmdvec);
+	c->handler = find_handler_for_cmdvec(c->cmdvec);
 
-	if (!h || !h->fn) {
+	if (!c->handler || !c->handler->fn) {
 		genhelp_handler(c->cmd, EINVAL, &c->reply);
 		if (get_strbuf_len(&c->reply) == 0)
 			r = EINVAL;
-		goto free_cmdvec;
+		else
+			r = 0;
 	}
 
-	/*
-	 * execute handler
-	 */
+	return r;
+}
+
+static int execute_handler(struct client *c, struct vectors *vecs, int timeout)
+{
+	int r;
+	struct timespec tmo;
+
+	if (!c->handler)
+		return EINVAL;
+
 	if (clock_gettime(CLOCK_REALTIME, &tmo) == 0) {
 		tmo.tv_sec += timeout;
 	} else {
 		tmo.tv_sec = 0;
 	}
-	if (h->locked) {
+
+	if (c->handler->locked) {
 		int locked = 0;
-		struct vectors * vecs = (struct vectors *)data;
 
 		pthread_cleanup_push(cleanup_lock, &vecs->lock);
 		if (tmo.tv_sec) {
@@ -339,15 +346,11 @@ static int parse_cmd (struct client *c, void *data, int timeout)
 		if (r == 0) {
 			locked = 1;
 			pthread_testcancel();
-			r = h->fn(c->cmdvec, &c->reply, data);
+			r = c->handler->fn(c->cmdvec, &c->reply, vecs);
 		}
 		pthread_cleanup_pop(locked);
 	} else
-		r = h->fn(c->cmdvec, &c->reply, data);
-
-free_cmdvec:
-	free_keys(c->cmdvec);
-	c->cmdvec = NULL;
+		r = c->handler->fn(c->cmdvec, &c->reply, vecs);
 
 	return r;
 }
@@ -367,7 +370,15 @@ static int uxsock_trigger(struct client *c, void *trigger_data)
 		return r;
 	}
 
-	r = parse_cmd(c, vecs, uxsock_timeout / 1000);
+	r = parse_cmd(c);
+
+	if (r == 0 && c->handler)
+		r = execute_handler(c, vecs, uxsock_timeout / 1000);
+
+	if (c->cmdvec) {
+		free_keys(c->cmdvec);
+		c->cmdvec = NULL;
+	}
 
 	if (r > 0) {
 		if (r == ETIMEDOUT)
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 29/35] multipathd: uxlsnr: use parser to determine non-root commands
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (27 preceding siblings ...)
  2021-09-10 11:41 ` [dm-devel] [PATCH 28/35] multipathd: uxlsnr: move handler execution to separate function mwilck
@ 2021-09-10 11:41 ` mwilck
  2021-09-16  2:29   ` Benjamin Marzinski
  2021-09-10 11:41 ` [dm-devel] [PATCH 30/35] multipathd: uxlsnr: merge uxsock_trigger() into state machine mwilck
                   ` (5 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:41 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

Rather than using a separate poor-man's parser for checking root
commands, use the real parser. It will return "LIST" as first verb
for the read-only commands that non-root users may execute.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/uxlsnr.c | 27 +++++++++++++++++----------
 1 file changed, 17 insertions(+), 10 deletions(-)

diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
index cfff0ae..ff9604f 100644
--- a/multipathd/uxlsnr.c
+++ b/multipathd/uxlsnr.c
@@ -362,16 +362,15 @@ static int uxsock_trigger(struct client *c, void *trigger_data)
 
 	vecs = (struct vectors *)trigger_data;
 
-
-	if (!c->is_root &&
-	    (strncmp(c->cmd, "list", strlen("list")) != 0) &&
-	    (strncmp(c->cmd, "show", strlen("show")) != 0)) {
-		append_strbuf_str(&c->reply, "permission deny: need to be root");
-		return r;
-	}
-
 	r = parse_cmd(c);
 
+	if (r == 0 && c->cmdvec && VECTOR_SIZE(c->cmdvec) > 0) {
+		struct key *kw = VECTOR_SLOT(c->cmdvec, 0);
+
+		if (!c->is_root && kw->code != LIST)
+			r = EPERM;
+	}
+
 	if (r == 0 && c->handler)
 		r = execute_handler(c, vecs, uxsock_timeout / 1000);
 
@@ -381,10 +380,18 @@ static int uxsock_trigger(struct client *c, void *trigger_data)
 	}
 
 	if (r > 0) {
-		if (r == ETIMEDOUT)
+		switch(r) {
+		case ETIMEDOUT:
 			append_strbuf_str(&c->reply, "timeout\n");
-		else
+			break;
+		case EPERM:
+			append_strbuf_str(&c->reply,
+					  "permission deny: need to be root\n");
+			break;
+		default:
 			append_strbuf_str(&c->reply, "fail\n");
+			break;
+		}
 	}
 	else if (!r && get_strbuf_len(&c->reply) == 0) {
 		append_strbuf_str(&c->reply, "ok\n");
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 30/35] multipathd: uxlsnr: merge uxsock_trigger() into state machine
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (28 preceding siblings ...)
  2021-09-10 11:41 ` [dm-devel] [PATCH 29/35] multipathd: uxlsnr: use parser to determine non-root commands mwilck
@ 2021-09-10 11:41 ` mwilck
  2021-09-16  3:32   ` Benjamin Marzinski
  2021-09-10 11:41 ` [dm-devel] [PATCH 31/35] multipathd: uxlsnr: add idle notification mwilck
                   ` (4 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:41 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

This patch sets up the bulk of the state machine. The idea is to
fall through the case labels as long as possible (when steps succeed)
and return to the caller if either an error occurs, or it becomes
necessary to wait for some pollable condition.

While doing this, switch to negative error codes for the functions
in uxlsnr.c (e.g. parse_cmd()). Positive return codes are reserved
for the cli_handler functions themselves. This way we can clearly
distinguish the error source, and avoid confusion and misleading
error messages. No cli_handler returns negative values.

Note: with this patch applied, clients may hang and time out if
the handler fails to acquire the vecs lock. This will be fixed in the
follow-up patch "multipathd: uxlsnr: add idle notification".

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/uxlsnr.c | 145 ++++++++++++++++++++++++--------------------
 1 file changed, 80 insertions(+), 65 deletions(-)

diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
index ff9604f..553274b 100644
--- a/multipathd/uxlsnr.c
+++ b/multipathd/uxlsnr.c
@@ -299,22 +299,13 @@ static int parse_cmd(struct client *c)
 
 	r = get_cmdvec(c->cmd, &c->cmdvec);
 
-	if (r) {
-		genhelp_handler(c->cmd, r, &c->reply);
-		if (get_strbuf_len(&c->reply) == 0)
-			return EINVAL;
-		return 0;
-	}
+	if (r)
+		return -r;
 
 	c->handler = find_handler_for_cmdvec(c->cmdvec);
 
-	if (!c->handler || !c->handler->fn) {
-		genhelp_handler(c->cmd, EINVAL, &c->reply);
-		if (get_strbuf_len(&c->reply) == 0)
-			r = EINVAL;
-		else
-			r = 0;
-	}
+	if (!c->handler || !c->handler->fn)
+		return -EINVAL;
 
 	return r;
 }
@@ -325,7 +316,7 @@ static int execute_handler(struct client *c, struct vectors *vecs, int timeout)
 	struct timespec tmo;
 
 	if (!c->handler)
-		return EINVAL;
+		return -EINVAL;
 
 	if (clock_gettime(CLOCK_REALTIME, &tmo) == 0) {
 		tmo.tv_sec += timeout;
@@ -355,50 +346,29 @@ static int execute_handler(struct client *c, struct vectors *vecs, int timeout)
 	return r;
 }
 
-static int uxsock_trigger(struct client *c, void *trigger_data)
+void default_reply(struct client *c, int r)
 {
-	struct vectors * vecs;
-	int r = 1;
-
-	vecs = (struct vectors *)trigger_data;
-
-	r = parse_cmd(c);
-
-	if (r == 0 && c->cmdvec && VECTOR_SIZE(c->cmdvec) > 0) {
-		struct key *kw = VECTOR_SLOT(c->cmdvec, 0);
-
-		if (!c->is_root && kw->code != LIST)
-			r = EPERM;
-	}
-
-	if (r == 0 && c->handler)
-		r = execute_handler(c, vecs, uxsock_timeout / 1000);
-
-	if (c->cmdvec) {
-		free_keys(c->cmdvec);
-		c->cmdvec = NULL;
-	}
-
-	if (r > 0) {
-		switch(r) {
-		case ETIMEDOUT:
-			append_strbuf_str(&c->reply, "timeout\n");
-			break;
-		case EPERM:
-			append_strbuf_str(&c->reply,
-					  "permission deny: need to be root\n");
-			break;
-		default:
-			append_strbuf_str(&c->reply, "fail\n");
-			break;
-		}
-	}
-	else if (!r && get_strbuf_len(&c->reply) == 0) {
+	switch(r) {
+	case -EINVAL:
+	case -ESRCH:
+	case -ENOMEM:
+		/* return codes from get_cmdvec() */
+		genhelp_handler(c->cmd, r, &c->reply);
+		break;
+	case -EPERM:
+		append_strbuf_str(&c->reply,
+				  "permission deny: need to be root\n");
+		break;
+	case -ETIMEDOUT:
+		append_strbuf_str(&c->reply, "timeout\n");
+		break;
+	case 0:
 		append_strbuf_str(&c->reply, "ok\n");
-		r = 0;
+		break;
+	default:
+		append_strbuf_str(&c->reply, "fail\n");
+		break;
 	}
-	/* else if (r < 0) leave *reply alone */
-	return r;
 }
 
 static void set_client_state(struct client *c, int state)
@@ -409,6 +379,7 @@ static void set_client_state(struct client *c, int state)
 		reset_strbuf(&c->reply);
 		memset(c->cmd, '\0', sizeof(c->cmd));
 		c->expires = ts_zero;
+		c->error = 0;
 		/* fallthrough */
 	case CLT_SEND:
 		/* reuse these fields for next data transfer */
@@ -420,10 +391,13 @@ static void set_client_state(struct client *c, int state)
 	c->state = state;
 }
 
-static void handle_client(struct client *c, void *trigger_data)
+static void handle_client(struct client *c, struct vectors *vecs)
 {
 	ssize_t n;
 
+	condlog(4, "%s: cli[%d] state=%d cmd=\"%s\" repl \"%s\"", __func__,
+		c->fd, c->state, c->cmd, get_strbuf_str(&c->reply));
+
 	switch (c->state) {
 	case CLT_RECV:
 		if (c->cmd_len == 0) {
@@ -464,15 +438,52 @@ static void handle_client(struct client *c, void *trigger_data)
 				return;
 			set_client_state(c, CLT_PARSE);
 		}
-		break;
-	default:
-		break;
-	}
+		condlog(4, "cli[%d]: Got request [%s]", c->fd, c->cmd);
+		/* fallthrough */
 
-	condlog(4, "cli[%d]: Got request [%s]", c->fd, c->cmd);
-	uxsock_trigger(c, trigger_data);
+	case CLT_PARSE:
+		condlog(4, "%s: cli[%d] state=%d cmd=\"%s\" repl \"%s\"", __func__,
+			c->fd, c->state, c->cmd,  get_strbuf_str(&c->reply));
+		c->error = parse_cmd(c);
+
+		/* Permission check */
+		if (c->error == 0 && c->cmdvec && VECTOR_SIZE(c->cmdvec) > 0) {
+			struct key *kw = VECTOR_SLOT(c->cmdvec, 0);
+
+			if (!c->is_root && kw->code != LIST) {
+				/* this will fall through to CLT_SEND */
+				c->error = -EPERM;
+				condlog(0, "%s: cli[%d]: unauthorized cmd \"%s\"",
+					__func__, c->fd, c->cmd);
+			}
+		}
+		set_client_state(c, CLT_WAIT_LOCK);
+		/* fallthrough */
+
+	case CLT_WAIT_LOCK:
+		condlog(4, "%s: cli[%d] state=%d cmd=\"%s\" repl \"%s\"", __func__,
+			c->fd, c->state, c->cmd, get_strbuf_str(&c->reply));
+		/* tbd */
+		set_client_state(c, CLT_WORK);
+		/* fallthrough */
+
+	case CLT_WORK:
+		condlog(4, "%s: cli[%d] state=%d cmd=\"%s\" repl \"%s\"", __func__,
+			c->fd, c->state, c->cmd, get_strbuf_str(&c->reply));
+		if (c->error == 0 && c->handler)
+			c->error = execute_handler(c, vecs, uxsock_timeout / 1000);
+
+		if (c->cmdvec) {
+			free_keys(c->cmdvec);
+			c->cmdvec = NULL;
+		}
+		set_client_state(c, CLT_SEND);
+		/* fallthrough */
+
+	case CLT_SEND:
+		if (get_strbuf_len(&c->reply) == 0)
+			default_reply(c, c->error);
 
-	if (get_strbuf_len(&c->reply) > 0) {
 		const char *buf = get_strbuf_str(&c->reply);
 
 		if (send_packet(c->fd, buf) != 0)
@@ -481,9 +492,13 @@ static void handle_client(struct client *c, void *trigger_data)
 			condlog(4, "cli[%d]: Reply [%zu bytes]", c->fd,
 				get_strbuf_len(&c->reply) + 1);
 		reset_strbuf(&c->reply);
-	}
 
-	set_client_state(c, CLT_RECV);
+		set_client_state(c, CLT_RECV);
+		break;
+
+	default:
+		break;
+	}
 }
 
 /*
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 31/35] multipathd: uxlsnr: add idle notification
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (29 preceding siblings ...)
  2021-09-10 11:41 ` [dm-devel] [PATCH 30/35] multipathd: uxlsnr: merge uxsock_trigger() into state machine mwilck
@ 2021-09-10 11:41 ` mwilck
  2021-09-16  4:14   ` Benjamin Marzinski
  2021-09-10 11:41 ` [dm-devel] [PATCH 32/35] multipathd: uxlsnr: add timeout handling mwilck
                   ` (3 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:41 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

The previous patches added the state machine and the timeout handling,
but there was no wakeup mechanism for the uxlsnr for cases where
client connections were waiting for the vecs lock.

This patch uses the previously introduced wakeup mechanism of
struct mutex_lock for this purpose. Processes which unlock the
"global" vecs lock send an event in an eventfd which the uxlsnr
loop is polling for.

As we are now woken up for servicing client handlers that don't
wait for input but for the lock, we need to set up the pollfds
differently, and iterate over all clients when handling events,
not only over the ones that are receiving. The hangup handling
is changed, too. We have to look at every client, even if one has
hung up. Note that I don't take client_lock for the loop in
uxsock_listen(), it's not necessary and will be removed elsewhere
in a follow-up patch.

With this in place, the lock need not be taken in execute_handler()
any more. The uxlsnr only ever calls trylock() on the vecs lock,
avoiding any waiting for other threads to finish.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/uxlsnr.c | 211 ++++++++++++++++++++++++++++++--------------
 1 file changed, 143 insertions(+), 68 deletions(-)

diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
index 553274b..4637954 100644
--- a/multipathd/uxlsnr.c
+++ b/multipathd/uxlsnr.c
@@ -24,6 +24,7 @@
 #include <signal.h>
 #include <stdbool.h>
 #include <sys/inotify.h>
+#include <sys/eventfd.h>
 #include "checkers.h"
 #include "memory.h"
 #include "debug.h"
@@ -70,6 +71,7 @@ struct client {
 enum {
 	POLLFD_UX = 0,
 	POLLFD_NOTIFY,
+	POLLFD_IDLE,
 	POLLFDS_BASE,
 };
 
@@ -90,8 +92,23 @@ static LIST_HEAD(clients);
 static pthread_mutex_t client_lock = PTHREAD_MUTEX_INITIALIZER;
 static struct pollfd *polls;
 static int notify_fd = -1;
+static int idle_fd = -1;
 static char *watch_config_dir;
 
+struct possible_lock {
+	struct mutex_lock *lock;
+	bool held;
+};
+
+static void unlock_if_held(void *arg)
+{
+	struct possible_lock *pl = arg;
+
+	/* don't call unlock_wakeup() here, lest we wakeup ourselves */
+	if (pl->held)
+		__unlock(pl->lock);
+}
+
 static bool _socket_client_is_root(int fd)
 {
 	socklen_t len = 0;
@@ -187,6 +204,17 @@ void uxsock_cleanup(void *arg)
 	free_polls();
 }
 
+void wakeup_cleanup(void *arg)
+{
+	struct mutex_lock *lck = arg;
+	int fd = idle_fd;
+
+	idle_fd = -1;
+	set_wakeup_fn(lck, NULL);
+	if (fd != -1)
+		close(fd);
+}
+
 struct watch_descriptors {
 	int conf_wd;
 	int dir_wd;
@@ -293,6 +321,18 @@ static void handle_inotify(int fd, struct watch_descriptors *wds)
 
 static const struct timespec ts_zero = { .tv_sec = 0, };
 
+/* call with clients lock held */
+static bool __need_lock(void)
+{
+	struct client *c;
+
+	list_for_each_entry(c, &clients, node) {
+		if (c->state == CLT_WAIT_LOCK)
+			return true;
+	}
+	return false;
+}
+
 static int parse_cmd(struct client *c)
 {
 	int r;
@@ -310,40 +350,31 @@ static int parse_cmd(struct client *c)
 	return r;
 }
 
-static int execute_handler(struct client *c, struct vectors *vecs, int timeout)
+static int execute_handler(struct client *c, struct vectors *vecs)
 {
-	int r;
-	struct timespec tmo;
 
-	if (!c->handler)
+	if (!c->handler || !c->handler->fn)
 		return -EINVAL;
 
-	if (clock_gettime(CLOCK_REALTIME, &tmo) == 0) {
-		tmo.tv_sec += timeout;
-	} else {
-		tmo.tv_sec = 0;
-	}
+	return c->handler->fn(c->cmdvec, &c->reply, vecs);
+}
 
-	if (c->handler->locked) {
-		int locked = 0;
+static void wakeup_listener(void)
+{
+	uint64_t one = 1;
 
-		pthread_cleanup_push(cleanup_lock, &vecs->lock);
-		if (tmo.tv_sec) {
-			r = timedlock(&vecs->lock, &tmo);
-		} else {
-			lock(&vecs->lock);
-			r = 0;
-		}
-		if (r == 0) {
-			locked = 1;
-			pthread_testcancel();
-			r = c->handler->fn(c->cmdvec, &c->reply, vecs);
-		}
-		pthread_cleanup_pop(locked);
-	} else
-		r = c->handler->fn(c->cmdvec, &c->reply, vecs);
+	if (idle_fd != -1 &&
+	    write(idle_fd, &one, sizeof(one)) != sizeof(one))
+		condlog(1, "%s: failed", __func__);
+}
 
-	return r;
+static void drain_idle_fd(int fd)
+{
+	uint64_t val;
+	int rc;
+
+	rc = read(fd, &val, sizeof(val));
+	condlog(4, "%s: %d, %"PRIu64, __func__, rc, val);
 }
 
 void default_reply(struct client *c, int r)
@@ -391,15 +422,26 @@ static void set_client_state(struct client *c, int state)
 	c->state = state;
 }
 
-static void handle_client(struct client *c, struct vectors *vecs)
+static void handle_client(struct client *c, struct vectors *vecs, short revents)
 {
 	ssize_t n;
+	struct possible_lock pl = {
+		.lock = &vecs->lock,
+		.held = false,
+	};
 
-	condlog(4, "%s: cli[%d] state=%d cmd=\"%s\" repl \"%s\"", __func__,
-		c->fd, c->state, c->cmd, get_strbuf_str(&c->reply));
+	if (revents & (POLLHUP|POLLERR)) {
+		c->error = -ECONNRESET;
+		return;
+	}
+
+	condlog(4, "%s: cli[%d] poll=%x state=%d cmd=\"%s\" repl \"%s\"", __func__,
+		c->fd, revents, c->state, c->cmd, get_strbuf_str(&c->reply));
 
 	switch (c->state) {
 	case CLT_RECV:
+		if (!(revents & POLLIN))
+			return;
 		if (c->cmd_len == 0) {
 			/*
 			 * We got POLLIN; assume that at least the length can
@@ -463,15 +505,28 @@ static void handle_client(struct client *c, struct vectors *vecs)
 	case CLT_WAIT_LOCK:
 		condlog(4, "%s: cli[%d] state=%d cmd=\"%s\" repl \"%s\"", __func__,
 			c->fd, c->state, c->cmd, get_strbuf_str(&c->reply));
-		/* tbd */
+
+		if (c->error == 0 && c->handler && c->handler->locked) {
+			pl.held = trylock(pl.lock) == 0;
+			if (!pl.held) {
+				condlog(4, "%s: cli[%d] waiting for lock",
+					__func__, c->fd);
+				return;
+			} else
+				condlog(4, "%s: cli[%d] grabbed lock",
+					__func__, c->fd);
+		}
 		set_client_state(c, CLT_WORK);
 		/* fallthrough */
 
 	case CLT_WORK:
 		condlog(4, "%s: cli[%d] state=%d cmd=\"%s\" repl \"%s\"", __func__,
 			c->fd, c->state, c->cmd, get_strbuf_str(&c->reply));
-		if (c->error == 0 && c->handler)
-			c->error = execute_handler(c, vecs, uxsock_timeout / 1000);
+		if (c->error == 0 && c->handler) {
+			pthread_cleanup_push(unlock_if_held, &pl);
+			c->error = execute_handler(c, vecs);
+			pthread_cleanup_pop(1);
+		}
 
 		if (c->cmdvec) {
 			free_keys(c->cmdvec);
@@ -511,6 +566,8 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
 	/* conf->sequence_nr will be 1 when uxsock_listen is first called */
 	unsigned int sequence_nr = 0;
 	struct watch_descriptors wds = { .conf_wd = -1, .dir_wd = -1 };
+	bool need_lock = false;
+	struct vectors *vecs = trigger_data;
 
 	condlog(3, "uxsock: startup listener");
 	polls = MALLOC(max_pfds * sizeof(*polls));
@@ -521,6 +578,14 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
 	notify_fd = inotify_init1(IN_NONBLOCK);
 	if (notify_fd == -1) /* it's fine if notifications fail */
 		condlog(3, "failed to start up configuration notifications");
+
+	pthread_cleanup_push(wakeup_cleanup, &vecs->lock);
+	idle_fd = eventfd(0, EFD_NONBLOCK|EFD_CLOEXEC);
+	if (idle_fd == -1)
+		condlog(1, "failed to create idle fd");
+	else
+		set_wakeup_fn(&vecs->lock, wakeup_listener);
+
 	sigfillset(&mask);
 	sigdelset(&mask, SIGINT);
 	sigdelset(&mask, SIGTERM);
@@ -572,16 +637,30 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
 		else
 			polls[POLLFD_NOTIFY].events = POLLIN;
 
+		need_lock = __need_lock();
+		polls[POLLFD_IDLE].fd = idle_fd;
+		if (need_lock)
+			polls[POLLFD_IDLE].events = POLLIN;
+		else
+			polls[POLLFD_IDLE].events = 0;
+
 		/* setup the clients */
-		i = POLLFDS_BASE;
-		list_for_each_entry(c, &clients, node) {
-			polls[i].fd = c->fd;
-			polls[i].events = POLLIN;
-			i++;
-			if (i >= max_pfds)
-				break;
-		}
-		n_pfds = i;
+                i = POLLFDS_BASE;
+                list_for_each_entry(c, &clients, node) {
+                        switch(c->state) {
+                        case CLT_RECV:
+                                polls[i].events = POLLIN;
+                                break;
+                        default:
+				/* don't poll for this client */
+                                continue;
+                        }
+                        polls[i].fd = c->fd;
+                        i++;
+                        if (i >= max_pfds)
+                                break;
+                }
+                n_pfds = i;
 		pthread_cleanup_pop(1);
 
 		/* most of our life is spent in this call */
@@ -604,33 +683,28 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
 			handle_signals(true);
 			continue;
 		}
+		if (polls[POLLFD_IDLE].fd != -1 &&
+		    polls[POLLFD_IDLE].revents & POLLIN)
+			drain_idle_fd(idle_fd);
 
-		/* see if a client wants to speak to us */
-		for (i = POLLFDS_BASE; i < n_pfds; i++) {
-			if (polls[i].revents & (POLLIN|POLLHUP|POLLERR)) {
-				c = NULL;
-				pthread_mutex_lock(&client_lock);
-				list_for_each_entry(tmp, &clients, node) {
-					if (tmp->fd == polls[i].fd) {
-						c = tmp;
-						break;
-					}
-				}
-				pthread_mutex_unlock(&client_lock);
-				if (!c) {
-					condlog(4, "cli%d: new fd %d",
-						i, polls[i].fd);
-					continue;
-				}
-				if (polls[i].revents & (POLLHUP|POLLERR)) {
-					condlog(4, "cli[%d]: Disconnected",
-						c->fd);
-					dead_client(c);
-					continue;
-				}
-				handle_client(c, trigger_data);
-				if (c->error == -ECONNRESET)
-					dead_client(c);
+		/* see if a client needs handling */
+		list_for_each_entry_safe(c, tmp, &clients, node) {
+			short revents = 0;
+
+			for (i = POLLFDS_BASE; i < n_pfds; i++) {
+                                if (polls[i].fd == c->fd) {
+                                        revents = polls[i].revents;
+                                        break;
+                                }
+                        }
+
+			handle_client(c, trigger_data, revents);
+
+			if (c->error == -ECONNRESET) {
+				condlog(4, "cli[%d]: disconnected", c->fd);
+				dead_client(c);
+				if (i < n_pfds)
+					polls[i].fd = -1;
 			}
 		}
 		/* see if we got a non-fatal signal */
@@ -646,5 +720,6 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
 			handle_inotify(notify_fd, &wds);
 	}
 
+	pthread_cleanup_pop(1);
 	return NULL;
 }
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 32/35] multipathd: uxlsnr: add timeout handling
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (30 preceding siblings ...)
  2021-09-10 11:41 ` [dm-devel] [PATCH 31/35] multipathd: uxlsnr: add idle notification mwilck
@ 2021-09-10 11:41 ` mwilck
  2021-09-16  4:17   ` Benjamin Marzinski
  2021-09-10 11:41 ` [dm-devel] [PATCH 33/35] multipathd: uxlsnr: use poll loop for sending, too mwilck
                   ` (2 subsequent siblings)
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:41 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

Our ppoll() call needs to wake up when a client request times out.
This logic can be added by determining the first client that's about
to time out. The logic in handle_client() will then cause a timeout
reply to be sent to the client. This is more client-friendly
as the client timing out without receiving a reply.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/uxlsnr.c | 58 +++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 53 insertions(+), 5 deletions(-)

diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
index 4637954..1bf4126 100644
--- a/multipathd/uxlsnr.c
+++ b/multipathd/uxlsnr.c
@@ -320,6 +320,35 @@ static void handle_inotify(int fd, struct watch_descriptors *wds)
 }
 
 static const struct timespec ts_zero = { .tv_sec = 0, };
+static const struct timespec ts_max = { .tv_sec = LONG_MAX, .tv_nsec = 999999999 };
+
+/* call with clients lock held */
+static struct timespec *__get_soonest_timeout(struct timespec *ts)
+{
+	struct timespec ts_min = ts_max, now;
+	bool any = false;
+	struct client *c;
+
+	list_for_each_entry(c, &clients, node) {
+		if (timespeccmp(&c->expires, &ts_zero) != 0 &&
+		    timespeccmp(&c->expires, &ts_min) < 0) {
+			ts_min = c->expires;
+			any = true;
+		}
+	}
+
+	if (!any)
+		return NULL;
+
+	get_monotonic_time(&now);
+	timespecsub(&ts_min, &now, ts);
+	if (timespeccmp(ts, &ts_zero) < 0)
+		*ts = ts_zero;
+
+	condlog(4, "%s: next client expires in %ld.%03lds", __func__,
+		(long)ts->tv_sec, ts->tv_nsec / 1000000);
+	return ts;
+}
 
 /* call with clients lock held */
 static bool __need_lock(void)
@@ -422,6 +451,24 @@ static void set_client_state(struct client *c, int state)
 	c->state = state;
 }
 
+static void check_timeout(struct client *c)
+{
+	struct timespec now;
+
+	if (timespeccmp(&c->expires, &ts_zero) == 0)
+		return;
+
+	get_monotonic_time(&now);
+	if (timespeccmp(&c->expires, &now) > 0)
+		return;
+
+	condlog(2, "%s: cli[%d]: timed out at %ld.%03ld", __func__,
+		c->fd, (long)c->expires.tv_sec, c->expires.tv_nsec / 1000000);
+
+	c->error = -ETIMEDOUT;
+	set_client_state(c, CLT_SEND);
+}
+
 static void handle_client(struct client *c, struct vectors *vecs, short revents)
 {
 	ssize_t n;
@@ -435,6 +482,8 @@ static void handle_client(struct client *c, struct vectors *vecs, short revents)
 		return;
 	}
 
+	check_timeout(c);
+
 	condlog(4, "%s: cli[%d] poll=%x state=%d cmd=\"%s\" repl \"%s\"", __func__,
 		c->fd, revents, c->state, c->cmd, get_strbuf_str(&c->reply));
 
@@ -594,6 +643,7 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
 	while (1) {
 		struct client *c, *tmp;
 		int i, n_pfds, poll_count, num_clients;
+		struct timespec __timeout, *timeout;
 
 		/* setup for a poll */
 		pthread_mutex_lock(&client_lock);
@@ -661,10 +711,12 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
                                 break;
                 }
                 n_pfds = i;
+		timeout = __get_soonest_timeout(&__timeout);
+
 		pthread_cleanup_pop(1);
 
 		/* most of our life is spent in this call */
-		poll_count = ppoll(polls, n_pfds, NULL, &mask);
+		poll_count = ppoll(polls, n_pfds, timeout, &mask);
 
 		handle_signals(false);
 		if (poll_count == -1) {
@@ -679,10 +731,6 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
 			break;
 		}
 
-		if (poll_count == 0) {
-			handle_signals(true);
-			continue;
-		}
 		if (polls[POLLFD_IDLE].fd != -1 &&
 		    polls[POLLFD_IDLE].revents & POLLIN)
 			drain_idle_fd(idle_fd);
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 33/35] multipathd: uxlsnr: use poll loop for sending, too
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (31 preceding siblings ...)
  2021-09-10 11:41 ` [dm-devel] [PATCH 32/35] multipathd: uxlsnr: add timeout handling mwilck
@ 2021-09-10 11:41 ` mwilck
  2021-09-16  4:22   ` Benjamin Marzinski
  2021-09-10 11:41 ` [dm-devel] [PATCH 34/35] multipathd: uxlsnr: drop client_lock mwilck
  2021-09-10 11:41 ` [dm-devel] [PATCH 35/35] multipathd: uxclt: allow client mode for non-root, too mwilck
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:41 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

send_packet() may busy-loop. By polling for POLLOUT, we can
avoid that, even if it's very unlikely in practice.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/uxlsnr.c | 39 ++++++++++++++++++++++++++++++++-------
 1 file changed, 32 insertions(+), 7 deletions(-)

diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
index 1bf4126..c18b2c4 100644
--- a/multipathd/uxlsnr.c
+++ b/multipathd/uxlsnr.c
@@ -588,15 +588,37 @@ static void handle_client(struct client *c, struct vectors *vecs, short revents)
 		if (get_strbuf_len(&c->reply) == 0)
 			default_reply(c, c->error);
 
-		const char *buf = get_strbuf_str(&c->reply);
+		if (c->cmd_len == 0) {
+			size_t len = get_strbuf_len(&c->reply) + 1;
 
-		if (send_packet(c->fd, buf) != 0)
-			dead_client(c);
-		else
-			condlog(4, "cli[%d]: Reply [%zu bytes]", c->fd,
-				get_strbuf_len(&c->reply) + 1);
-		reset_strbuf(&c->reply);
+			if (send(c->fd, &len, sizeof(len), MSG_NOSIGNAL)
+			    != sizeof(len)) {
+				c->error = -ECONNRESET;
+				return;
+			}
+			c->cmd_len = len;
+			return;
+		}
 
+		if (c->len < c->cmd_len) {
+			const char *buf = get_strbuf_str(&c->reply);
+
+			n = send(c->fd, buf + c->len, c->cmd_len, MSG_NOSIGNAL);
+			if (n == -1) {
+				if (errno == EAGAIN || errno == EINTR)
+					return;
+				else {
+					c->error = -ECONNRESET;
+					return;
+				}
+			}
+			c->len += n;
+		}
+		if (c->len < c->cmd_len)
+			/* continue polling */
+			return;
+
+		condlog(4, "cli[%d]: Reply [%zu bytes]", c->fd, c->cmd_len);
 		set_client_state(c, CLT_RECV);
 		break;
 
@@ -701,6 +723,9 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
                         case CLT_RECV:
                                 polls[i].events = POLLIN;
                                 break;
+			case CLT_SEND:
+				polls[i].events = POLLOUT;
+				break;
                         default:
 				/* don't poll for this client */
                                 continue;
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 34/35] multipathd: uxlsnr: drop client_lock
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (32 preceding siblings ...)
  2021-09-10 11:41 ` [dm-devel] [PATCH 33/35] multipathd: uxlsnr: use poll loop for sending, too mwilck
@ 2021-09-10 11:41 ` mwilck
  2021-09-16  4:24   ` Benjamin Marzinski
  2021-09-10 11:41 ` [dm-devel] [PATCH 35/35] multipathd: uxclt: allow client mode for non-root, too mwilck
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:41 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

The list of clients is never changed anywhere except in
uxsock_listen(). No need to lock.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/uxlsnr.c | 21 ++-------------------
 1 file changed, 2 insertions(+), 19 deletions(-)

diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
index c18b2c4..7b763b6 100644
--- a/multipathd/uxlsnr.c
+++ b/multipathd/uxlsnr.c
@@ -89,7 +89,6 @@ enum {
 static __attribute__((unused)) char ___a[-(MIN_POLLS <= 0)];
 
 static LIST_HEAD(clients);
-static pthread_mutex_t client_lock = PTHREAD_MUTEX_INITIALIZER;
 static struct pollfd *polls;
 static int notify_fd = -1;
 static int idle_fd = -1;
@@ -150,15 +149,13 @@ static void new_client(int ux_sock)
 	c->is_root = _socket_client_is_root(c->fd);
 
 	/* put it in our linked list */
-	pthread_mutex_lock(&client_lock);
 	list_add_tail(&c->node, &clients);
-	pthread_mutex_unlock(&client_lock);
 }
 
 /*
  * kill off a dead client
  */
-static void _dead_client(struct client *c)
+static void dead_client(struct client *c)
 {
 	int fd = c->fd;
 	list_del_init(&c->node);
@@ -170,14 +167,6 @@ static void _dead_client(struct client *c)
 	close(fd);
 }
 
-static void dead_client(struct client *c)
-{
-	pthread_cleanup_push(cleanup_mutex, &client_lock);
-	pthread_mutex_lock(&client_lock);
-	_dead_client(c);
-	pthread_cleanup_pop(1);
-}
-
 static void free_polls (void)
 {
 	if (polls)
@@ -194,11 +183,9 @@ void uxsock_cleanup(void *arg)
 	close(notify_fd);
 	free(watch_config_dir);
 
-	pthread_mutex_lock(&client_lock);
 	list_for_each_entry_safe(client_loop, client_tmp, &clients, node) {
-		_dead_client(client_loop);
+		dead_client(client_loop);
 	}
-	pthread_mutex_unlock(&client_lock);
 
 	cli_exit();
 	free_polls();
@@ -668,8 +655,6 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
 		struct timespec __timeout, *timeout;
 
 		/* setup for a poll */
-		pthread_mutex_lock(&client_lock);
-		pthread_cleanup_push(cleanup_mutex, &client_lock);
 		num_clients = 0;
 		list_for_each_entry(c, &clients, node) {
 			num_clients++;
@@ -738,8 +723,6 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
                 n_pfds = i;
 		timeout = __get_soonest_timeout(&__timeout);
 
-		pthread_cleanup_pop(1);
-
 		/* most of our life is spent in this call */
 		poll_count = ppoll(polls, n_pfds, timeout, &mask);
 
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [dm-devel] [PATCH 35/35] multipathd: uxclt: allow client mode for non-root, too
  2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
                   ` (33 preceding siblings ...)
  2021-09-10 11:41 ` [dm-devel] [PATCH 34/35] multipathd: uxlsnr: drop client_lock mwilck
@ 2021-09-10 11:41 ` mwilck
  2021-09-16  4:24   ` Benjamin Marzinski
  34 siblings, 1 reply; 89+ messages in thread
From: mwilck @ 2021-09-10 11:41 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: lixiaokeng, Chongyun Wu, dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

The server checks for root permissions anyway. "multipathd -k"
should work for ordinary users as long as no priviledged commands
are executed.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/main.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/multipathd/main.c b/multipathd/main.c
index ec4bcc3..ff5b600 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -3329,11 +3329,6 @@ main (int argc, char *argv[])
 
 	logsink = LOGSINK_SYSLOG;
 
-	if (getuid() != 0) {
-		fprintf(stderr, "need to be root\n");
-		exit(1);
-	}
-
 	/* make sure we don't lock any path */
 	if (chdir("/") < 0)
 		fprintf(stderr, "can't chdir to root directory : %s\n",
@@ -3420,6 +3415,11 @@ main (int argc, char *argv[])
 		return err;
 	}
 
+	if (getuid() != 0) {
+		fprintf(stderr, "need to be root\n");
+		exit(1);
+	}
+
 	if (foreground) {
 		if (!isatty(fileno(stdout)))
 			setbuf(stdout, NULL);
-- 
2.33.0


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


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 01/35] libmultipath: add timespeccmp() utility function
  2021-09-10 11:40 ` [dm-devel] [PATCH 01/35] libmultipath: add timespeccmp() utility function mwilck
@ 2021-09-15 22:07   ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-15 22:07 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:40:46PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> Add a small utility that will be used in later patches.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmultipath/libmultipath.version |  5 +++++
>  libmultipath/time-util.c          | 12 ++++++++++++
>  libmultipath/time-util.h          |  1 +
>  3 files changed, 18 insertions(+)
> 
> diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version
> index eb5b5b5..c98cf7f 100644
> --- a/libmultipath/libmultipath.version
> +++ b/libmultipath/libmultipath.version
> @@ -287,3 +287,8 @@ global:
>  local:
>  	*;
>  };
> +
> +LIBMULTIPATH_9.1.0 {
> +global:
> +	timespeccmp;
> +} LIBMULTIPATH_9.0.0;
> diff --git a/libmultipath/time-util.c b/libmultipath/time-util.c
> index 55f366c..2919300 100644
> --- a/libmultipath/time-util.c
> +++ b/libmultipath/time-util.c
> @@ -49,3 +49,15 @@ void timespecsub(const struct timespec *a, const struct timespec *b,
>  	res->tv_nsec = a->tv_nsec - b->tv_nsec;
>  	normalize_timespec(res);
>  }
> +
> +int timespeccmp(const struct timespec *a, const struct timespec *b)
> +{
> +	struct timespec tmp;
> +
> +	timespecsub(a, b, &tmp);
> +	if (tmp.tv_sec > 0)
> +		return 1;
> +	if (tmp.tv_sec < 0)
> +		return -1;
> +	return tmp.tv_nsec > 0 ? 1 : (tmp.tv_nsec < 0 ? -1 : 0);
> +}
> diff --git a/libmultipath/time-util.h b/libmultipath/time-util.h
> index b23d328..4a80ebd 100644
> --- a/libmultipath/time-util.h
> +++ b/libmultipath/time-util.h
> @@ -10,5 +10,6 @@ void pthread_cond_init_mono(pthread_cond_t *cond);
>  void normalize_timespec(struct timespec *ts);
>  void timespecsub(const struct timespec *a, const struct timespec *b,
>  		 struct timespec *res);
> +int timespeccmp(const struct timespec *a, const struct timespec *b);
>  
>  #endif /* _TIME_UTIL_H_ */
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 02/35] libmultipath: add trylock() helper
  2021-09-10 11:40 ` [dm-devel] [PATCH 02/35] libmultipath: add trylock() helper mwilck
@ 2021-09-15 22:07   ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-15 22:07 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:40:47PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> Add a small helper.
> 
> Signed-off-by: Martin Wilck <mwilck@suse.com>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> ---
>  libmultipath/lock.h | 5 +++++
>  1 file changed, 5 insertions(+)
> 
> diff --git a/libmultipath/lock.h b/libmultipath/lock.h
> index a170efe..d99eedb 100644
> --- a/libmultipath/lock.h
> +++ b/libmultipath/lock.h
> @@ -12,6 +12,11 @@ static inline void lock(struct mutex_lock *a)
>  	pthread_mutex_lock(&a->mutex);
>  }
>  
> +static inline int trylock(struct mutex_lock *a)
> +{
> +	return pthread_mutex_trylock(&a->mutex);
> +}
> +
>  static inline int timedlock(struct mutex_lock *a, struct timespec *tmo)
>  {
>  	return pthread_mutex_timedlock(&a->mutex, tmo);
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 03/35] libmultipath: add optional wakeup functionality to lock.c
  2021-09-10 11:40 ` [dm-devel] [PATCH 03/35] libmultipath: add optional wakeup functionality to lock.c mwilck
@ 2021-09-15 22:13   ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-15 22:13 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:40:48PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> Have struct mutex_lock take an optional wakeup function.
> unlock() is renamed to __unlock() in order to prevent it from
> being called by mistake.
> 

It might be worth it to make the waiter_lock into a regular
pthread_mutex_t, since it doesn't need any special handling, but
this change doesn't cause any problems with it, so

Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmultipath/libmultipath.version |  5 +++++
>  libmultipath/lock.c               | 12 +++++++++++-
>  libmultipath/lock.h               |  6 +++++-
>  3 files changed, 21 insertions(+), 2 deletions(-)
> 
> diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version
> index c98cf7f..2107c51 100644
> --- a/libmultipath/libmultipath.version
> +++ b/libmultipath/libmultipath.version
> @@ -292,3 +292,8 @@ LIBMULTIPATH_9.1.0 {
>  global:
>  	timespeccmp;
>  } LIBMULTIPATH_9.0.0;
> +
> +LIBMULTIPATH_9.2.0 {
> +global:
> +	set_wakeup_fn;
> +} LIBMULTIPATH_9.1.0;
> diff --git a/libmultipath/lock.c b/libmultipath/lock.c
> index 72c70e3..93b48db 100644
> --- a/libmultipath/lock.c
> +++ b/libmultipath/lock.c
> @@ -3,6 +3,16 @@
>  void cleanup_lock (void * data)
>  {
>  	struct mutex_lock *lock = data;
> +	wakeup_fn *fn = lock->wakeup;
>  
> -	unlock(lock);
> +	__unlock(lock);
> +	if (fn)
> +		fn();
> +}
> +
> +void set_wakeup_fn(struct mutex_lock *lck, wakeup_fn *fn)
> +{
> +	lock(lck);
> +	lck->wakeup = fn;
> +	__unlock(lck);
>  }
> diff --git a/libmultipath/lock.h b/libmultipath/lock.h
> index d99eedb..d7b779e 100644
> --- a/libmultipath/lock.h
> +++ b/libmultipath/lock.h
> @@ -3,8 +3,11 @@
>  
>  #include <pthread.h>
>  
> +typedef void (wakeup_fn)(void);
> +
>  struct mutex_lock {
>  	pthread_mutex_t mutex;
> +	wakeup_fn *wakeup;
>  };
>  
>  static inline void lock(struct mutex_lock *a)
> @@ -22,7 +25,7 @@ static inline int timedlock(struct mutex_lock *a, struct timespec *tmo)
>  	return pthread_mutex_timedlock(&a->mutex, tmo);
>  }
>  
> -static inline void unlock(struct mutex_lock *a)
> +static inline void __unlock(struct mutex_lock *a)
>  {
>  	pthread_mutex_unlock(&a->mutex);
>  }
> @@ -30,5 +33,6 @@ static inline void unlock(struct mutex_lock *a)
>  #define lock_cleanup_pop(a) pthread_cleanup_pop(1)
>  
>  void cleanup_lock (void * data);
> +void set_wakeup_fn(struct mutex_lock *lock, wakeup_fn *fn);
>  
>  #endif /* _LOCK_H */
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 04/35] libmultipath: print: add __snprint_config()
  2021-09-10 11:40 ` [dm-devel] [PATCH 04/35] libmultipath: print: add __snprint_config() mwilck
@ 2021-09-15 22:14   ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-15 22:14 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:40:49PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> exactly like snprint_config(), but takes a struct strbuf * as argument.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmultipath/libmultipath.version |  5 +++++
>  libmultipath/print.c              | 34 +++++++++++++++++++++----------
>  libmultipath/print.h              |  2 ++
>  3 files changed, 30 insertions(+), 11 deletions(-)
> 
> diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version
> index 2107c51..b2feee2 100644
> --- a/libmultipath/libmultipath.version
> +++ b/libmultipath/libmultipath.version
> @@ -297,3 +297,8 @@ LIBMULTIPATH_9.2.0 {
>  global:
>  	set_wakeup_fn;
>  } LIBMULTIPATH_9.1.0;
> +
> +LIBMULTIPATH_9.3.0 {
> +global:
> +	__snprint_config;
> +} LIBMULTIPATH_9.2.0;
> diff --git a/libmultipath/print.c b/libmultipath/print.c
> index 2fb9f4e..d2ef010 100644
> --- a/libmultipath/print.c
> +++ b/libmultipath/print.c
> @@ -1756,24 +1756,36 @@ static int snprint_blacklist_except(const struct config *conf,
>  	return get_strbuf_len(buff) - initial_len;
>  }
>  
> +int __snprint_config(const struct config *conf, struct strbuf *buff,
> +		     const struct _vector *hwtable, const struct _vector *mpvec)
> +{
> +	int rc;
> +
> +	if ((rc = snprint_defaults(conf, buff)) < 0 ||
> +	    (rc = snprint_blacklist(conf, buff)) < 0 ||
> +	    (rc = snprint_blacklist_except(conf, buff)) < 0 ||
> +	    (rc = snprint_hwtable(conf, buff,
> +				  hwtable ? hwtable : conf->hwtable)) < 0 ||
> +	    (rc = snprint_overrides(conf, buff, conf->overrides)) < 0)
> +		return rc;
> +
> +	if (VECTOR_SIZE(conf->mptable) > 0 ||
> +	    (mpvec != NULL && VECTOR_SIZE(mpvec) > 0))
> +		if ((rc = snprint_mptable(conf, buff, mpvec)) < 0)
> +			return rc;
> +
> +	return 0;
> +}
> +
>  char *snprint_config(const struct config *conf, int *len,
>  		     const struct _vector *hwtable, const struct _vector *mpvec)
>  {
>  	STRBUF_ON_STACK(buff);
>  	char *reply;
> -	int rc;
> +	int rc = __snprint_config(conf, &buff, hwtable, mpvec);
>  
> -	if ((rc = snprint_defaults(conf, &buff)) < 0 ||
> -	    (rc = snprint_blacklist(conf, &buff)) < 0 ||
> -	    (rc = snprint_blacklist_except(conf, &buff)) < 0 ||
> -	    (rc = snprint_hwtable(conf, &buff,
> -				  hwtable ? hwtable : conf->hwtable)) < 0 ||
> -	    (rc = snprint_overrides(conf, &buff, conf->overrides)) < 0)
> +	if (rc < 0)
>  		return NULL;
> -	if (VECTOR_SIZE(conf->mptable) > 0 ||
> -	    (mpvec != NULL && VECTOR_SIZE(mpvec) > 0))
> -		if ((rc = snprint_mptable(conf, &buff, mpvec)) < 0)
> -			return NULL;
>  
>  	if (len)
>  		*len = get_strbuf_len(&buff);
> diff --git a/libmultipath/print.h b/libmultipath/print.h
> index c6674a5..b149275 100644
> --- a/libmultipath/print.h
> +++ b/libmultipath/print.h
> @@ -54,6 +54,8 @@ int _snprint_multipath_topology (const struct gen_multipath *, struct strbuf *,
>  #define snprint_multipath_topology(buf, mpp, v) \
>  	_snprint_multipath_topology (dm_multipath_to_gen(mpp), buf, v)
>  int snprint_multipath_topology_json(struct strbuf *, const struct vectors *vecs);
> +int __snprint_config(const struct config *conf, struct strbuf *buff,
> +		     const struct _vector *hwtable, const struct _vector *mpvec);
>  char *snprint_config(const struct config *conf, int *len,
>  		     const struct _vector *hwtable,
>  		     const struct _vector *mpvec);
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 05/35] libmultipath: improve cleanup of uevent queues on exit
  2021-09-10 11:40 ` [dm-devel] [PATCH 05/35] libmultipath: improve cleanup of uevent queues on exit mwilck
@ 2021-09-15 22:20   ` Benjamin Marzinski
  2021-09-16  7:10     ` Martin Wilck
  0 siblings, 1 reply; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-15 22:20 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:40:50PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> uevents listed on merge_node must be cleaned up, too. uevents
> cancelled while being serviced and temporary queues, likewise.
> The global uevq must be cleaned out in the uevent listener thread,
> because it might have added events after the dispatcher thread
> had already finished.
> 

There's nothing wrong with this, but for the global list, wouldn't it be
easier to just wait till after cleanup_child() calls cleanup_threads(),
and then call cleanup_global_uevq(). That way you know nothing else is
running.

-Ben

> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmultipath/uevent.c | 49 ++++++++++++++++++++++++++++++++-----------
>  1 file changed, 37 insertions(+), 12 deletions(-)
> 
> diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c
> index 4265904..082e891 100644
> --- a/libmultipath/uevent.c
> +++ b/libmultipath/uevent.c
> @@ -91,16 +91,25 @@ struct uevent * alloc_uevent (void)
>  	return uev;
>  }
>  
> +static void uevq_cleanup(struct list_head *tmpq);
> +
> +static void cleanup_uev(void *arg)
> +{
> +	struct uevent *uev = arg;
> +
> +	uevq_cleanup(&uev->merge_node);
> +	if (uev->udev)
> +		udev_device_unref(uev->udev);
> +	FREE(uev);
> +}
> +
>  static void uevq_cleanup(struct list_head *tmpq)
>  {
>  	struct uevent *uev, *tmp;
>  
>  	list_for_each_entry_safe(uev, tmp, tmpq, node) {
>  		list_del_init(&uev->node);
> -
> -		if (uev->udev)
> -			udev_device_unref(uev->udev);
> -		FREE(uev);
> +		cleanup_uev(uev);
>  	}
>  }
>  
> @@ -384,14 +393,10 @@ service_uevq(struct list_head *tmpq)
>  	list_for_each_entry_safe(uev, tmp, tmpq, node) {
>  		list_del_init(&uev->node);
>  
> +		pthread_cleanup_push(cleanup_uev, uev);
>  		if (my_uev_trigger && my_uev_trigger(uev, my_trigger_data))
>  			condlog(0, "uevent trigger error");
> -
> -		uevq_cleanup(&uev->merge_node);
> -
> -		if (uev->udev)
> -			udev_device_unref(uev->udev);
> -		FREE(uev);
> +		pthread_cleanup_pop(1);
>  	}
>  }
>  
> @@ -411,6 +416,18 @@ static void monitor_cleanup(void *arg)
>  	udev_monitor_unref(monitor);
>  }
>  
> +static void cleanup_uevq(void *arg)
> +{
> +	uevq_cleanup(arg);
> +}
> +
> +static void cleanup_global_uevq(void *arg __attribute__((unused)))
> +{
> +	pthread_mutex_lock(uevq_lockp);
> +	uevq_cleanup(&uevq);
> +	pthread_mutex_unlock(uevq_lockp);
> +}
> +
>  /*
>   * Service the uevent queue.
>   */
> @@ -425,6 +442,7 @@ int uevent_dispatch(int (*uev_trigger)(struct uevent *, void * trigger_data),
>  	while (1) {
>  		LIST_HEAD(uevq_tmp);
>  
> +		pthread_cleanup_push(cleanup_mutex, uevq_lockp);
>  		pthread_mutex_lock(uevq_lockp);
>  		servicing_uev = 0;
>  		/*
> @@ -436,14 +454,17 @@ int uevent_dispatch(int (*uev_trigger)(struct uevent *, void * trigger_data),
>  		}
>  		servicing_uev = 1;
>  		list_splice_init(&uevq, &uevq_tmp);
> -		pthread_mutex_unlock(uevq_lockp);
> +		pthread_cleanup_pop(1);
> +
>  		if (!my_uev_trigger)
>  			break;
> +
> +		pthread_cleanup_push(cleanup_uevq, &uevq_tmp);
>  		merge_uevq(&uevq_tmp);
>  		service_uevq(&uevq_tmp);
> +		pthread_cleanup_pop(1);
>  	}
>  	condlog(3, "Terminating uev service queue");
> -	uevq_cleanup(&uevq);
>  	return 0;
>  }
>  
> @@ -600,6 +621,8 @@ int uevent_listen(struct udev *udev)
>  
>  	events = 0;
>  	gettimeofday(&start_time, NULL);
> +	pthread_cleanup_push(cleanup_global_uevq, NULL);
> +	pthread_cleanup_push(cleanup_uevq, &uevlisten_tmp);
>  	while (1) {
>  		struct uevent *uev;
>  		struct udev_device *dev;
> @@ -650,6 +673,8 @@ int uevent_listen(struct udev *udev)
>  		gettimeofday(&start_time, NULL);
>  		timeout = 30;
>  	}
> +	pthread_cleanup_pop(1);
> +	pthread_cleanup_pop(1);
>  out:
>  	pthread_cleanup_pop(1);
>  out_udev:
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 06/35] multipathd: fix systemd notification when stopping while reloading
  2021-09-10 11:40 ` [dm-devel] [PATCH 06/35] multipathd: fix systemd notification when stopping while reloading mwilck
@ 2021-09-15 22:55   ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-15 22:55 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:40:51PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> After sending "RELOADING=1" to systemd, a service must send
> "READY=1" before "STOPPING=1". Otherwise systemd will be confused
> and will not regard the service as stopped. Subsequent attempts
> to start multipathd via socket activation fail until systemd times
> out the reload operation.
> 
> The problem can be reproduced by running "multipathd shutdown"
> quickly after "multipathd reconfigure".
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/main.c | 7 +++++--
>  1 file changed, 5 insertions(+), 2 deletions(-)
> 
> diff --git a/multipathd/main.c b/multipathd/main.c
> index 3aff241..67160b9 100644
> --- a/multipathd/main.c
> +++ b/multipathd/main.c
> @@ -210,9 +210,12 @@ static void do_sd_notify(enum daemon_status old_state,
>  	if (msg && !safe_sprintf(notify_msg, "STATUS=%s", msg))
>  		sd_notify(0, notify_msg);
>  
> -	if (new_state == DAEMON_SHUTDOWN)
> +	if (new_state == DAEMON_SHUTDOWN) {
> +		/* Tell systemd that we're not RELOADING any more */
> +		if (old_state == DAEMON_CONFIGURE && startup_done)
> +			sd_notify(0, "READY=1");
>  		sd_notify(0, "STOPPING=1");
> -	else if (new_state == DAEMON_IDLE && old_state == DAEMON_CONFIGURE) {
> +	} else if (new_state == DAEMON_IDLE && old_state == DAEMON_CONFIGURE) {
>  		sd_notify(0, "READY=1");
>  		startup_done = true;
>  	} else if (new_state == DAEMON_CONFIGURE && startup_done)
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 07/35] multipathd: improve delayed reconfigure
  2021-09-10 11:40 ` [dm-devel] [PATCH 07/35] multipathd: improve delayed reconfigure mwilck
@ 2021-09-15 23:00   ` Benjamin Marzinski
  2021-09-16  7:16     ` Martin Wilck
  0 siblings, 1 reply; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-15 23:00 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:40:52PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> When a reconfigure operation is requested, either by the admin
> or by some condition multipathd encounters, the current code
> attempts to set DAEMON_CONFIGURE state and gives up after a second
> if it doesn't succeed. Apart from shutdown, this happens only
> if multipathd is either already reconfiguring, or busy in the
> path checker loop.
> 
> This patch modifies the logic as follows: rather than waiting,
> we set a flag that requests a reconfigure operation asap, i.e.
> when the current operation is finished and the status switched
> to DAEMON_IDLE. In this case, multipathd will not switch to IDLE
> but start another reconfigure cycle.
> 
> This assumes that if a reconfigure is requested while one is already
> running, the admin has made some (additional) changes and wants
> multipathd to pull them in. As we can't be sure that the currently
> running reconfigure has seen the configuration changes, we need
> to start over again.
> 
> A positive side effect is less waiting in clients and multipathd.
> 
> After this change, the only caller of set_config_state() is
> checkerloop(). Waking up every second just to see that DAEMON_RUNNING
> couldn't be set makes no sense. Therefore set_config_state() is
> changed to wait "forever", or until shutdown is requested. Unless
> multipathd completely hangs, the wait will terminate sooner or
> later.
> 
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/cli_handlers.c | 10 +----
>  multipathd/main.c         | 92 +++++++++++++++++++++++++++++----------
>  multipathd/main.h         |  3 +-
>  3 files changed, 71 insertions(+), 34 deletions(-)
> 
> diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
> index 6d3a0ae..44f76ee 100644
> --- a/multipathd/cli_handlers.c
> +++ b/multipathd/cli_handlers.c
> @@ -1076,17 +1076,9 @@ cli_switch_group(void * v, char ** reply, int * len, void * data)
>  int
>  cli_reconfigure(void * v, char ** reply, int * len, void * data)
>  {
> -	int rc;
> -
>  	condlog(2, "reconfigure (operator)");
>  
> -	rc = set_config_state(DAEMON_CONFIGURE);
> -	if (rc == ETIMEDOUT) {
> -		condlog(2, "timeout starting reconfiguration");
> -		return 1;
> -	} else if (rc == EINVAL)
> -		/* daemon shutting down */
> -		return 1;
> +	schedule_reconfigure();
>  	return 0;
>  }
>  
> diff --git a/multipathd/main.c b/multipathd/main.c
> index 67160b9..5fb6989 100644
> --- a/multipathd/main.c
> +++ b/multipathd/main.c
> @@ -221,6 +221,10 @@ static void do_sd_notify(enum daemon_status old_state,
>  	} else if (new_state == DAEMON_CONFIGURE && startup_done)
>  		sd_notify(0, "RELOADING=1");
>  }
> +#else
> +static void do_sd_notify(__attribute__((unused)) enum daemon_status old_state,
> +			 __attribute__((unused)) enum daemon_status new_state)
> +{}
>  #endif
>  
>  static void config_cleanup(__attribute__((unused)) void *arg)
> @@ -266,19 +270,38 @@ enum daemon_status wait_for_state_change_if(enum daemon_status oldstate,
>  	return st;
>  }
>  
> +/* Don't access this variable without holding config_lock */
> +static bool reconfigure_pending;
> +
>  /* must be called with config_lock held */
>  static void __post_config_state(enum daemon_status state)
>  {
>  	if (state != running_state && running_state != DAEMON_SHUTDOWN) {
> -#ifdef USE_SYSTEMD
>  		enum daemon_status old_state = running_state;
> -#endif
>  
> +		/*
> +		 * Handle a pending reconfigure request.
> +		 * DAEMON_IDLE is set from child() after reconfigure(),
> +		 * or from checkerloop() after completing checkers.
> +		 * In either case, child() will see DAEMON_CONFIGURE
> +		 * again and start another reconfigure cycle.
> +		 */
> +		if (reconfigure_pending && state == DAEMON_IDLE &&
> +		    (old_state == DAEMON_CONFIGURE ||
> +		     old_state == DAEMON_RUNNING)) {
> +			/*
> +			 * notify systemd of transient idle state, lest systemd
> +			 * thinks the reload lasts forever.
> +			 */
> +			do_sd_notify(old_state, DAEMON_IDLE);
> +			old_state = DAEMON_IDLE;
> +			state = DAEMON_CONFIGURE;
> +		}
> +		if (reconfigure_pending && state == DAEMON_CONFIGURE)
> +			reconfigure_pending = false;
>  		running_state = state;
>  		pthread_cond_broadcast(&config_cond);
> -#ifdef USE_SYSTEMD
>  		do_sd_notify(old_state, state);
> -#endif
>  	}
>  }
>  
> @@ -290,24 +313,48 @@ void post_config_state(enum daemon_status state)
>  	pthread_cleanup_pop(1);
>  }
>  
> -int set_config_state(enum daemon_status state)
> +void schedule_reconfigure(void)
> +{
> +	pthread_mutex_lock(&config_lock);
> +	pthread_cleanup_push(config_cleanup, NULL);
> +	switch (running_state)
> +	{
> +	case DAEMON_SHUTDOWN:
> +		break;
> +	case DAEMON_IDLE:
> +		__post_config_state(DAEMON_CONFIGURE);
> +		break;
> +	case DAEMON_CONFIGURE:
> +	case DAEMON_RUNNING:
> +		reconfigure_pending = true;
> +		break;
> +	default:
> +		break;
> +	}
> +	pthread_cleanup_pop(1);
> +}
> +
> +enum daemon_status set_config_state(enum daemon_status state)
>  {
>  	int rc = 0;
> +	enum daemon_status st;
>  
>  	pthread_cleanup_push(config_cleanup, NULL);
>  	pthread_mutex_lock(&config_lock);
> -	if (running_state != state) {
>  
> -		if (running_state == DAEMON_SHUTDOWN)
> -			rc = EINVAL;
> -		else
> -			rc = __wait_for_state_change(
> -				running_state != DAEMON_IDLE, 1000);
> -		if (!rc)
> -			__post_config_state(state);
> +	while (rc == 0 &&
> +	       running_state != state &&
> +	       running_state != DAEMON_SHUTDOWN &&
> +	       running_state != DAEMON_IDLE) {
> +		rc = pthread_cond_wait(&config_cond, &config_lock);
>  	}
> +
> +	if (rc == 0 && running_state == DAEMON_IDLE && state != DAEMON_IDLE)
> +		__post_config_state(state);
> +	st = running_state;
> +
>  	pthread_cleanup_pop(1);
> -	return rc;
> +	return st;
>  }
>  
>  struct config *get_multipath_config(void)
> @@ -734,7 +781,7 @@ ev_add_map (char * dev, const char * alias, struct vectors * vecs)
>  			if (delayed_reconfig &&
>  			    !need_to_delay_reconfig(vecs)) {
>  				condlog(2, "reconfigure (delayed)");
> -				set_config_state(DAEMON_CONFIGURE);
> +				schedule_reconfigure();
>  				return 0;
>  			}
>  		}
> @@ -1845,7 +1892,7 @@ missing_uev_wait_tick(struct vectors *vecs)
>  	if (timed_out && delayed_reconfig &&
>  	    !need_to_delay_reconfig(vecs)) {
>  		condlog(2, "reconfigure (delayed)");
> -		set_config_state(DAEMON_CONFIGURE);
> +		schedule_reconfigure();
>  	}
>  }
>  
> @@ -2484,6 +2531,10 @@ checkerloop (void *ap)
>  		int num_paths = 0, strict_timing, rc = 0;
>  		unsigned int ticks = 0;
>  
> +		if (set_config_state(DAEMON_RUNNING) != DAEMON_RUNNING)
> +			/* daemon shutdown */
> +			break;
> +
>  		get_monotonic_time(&start_time);
>  		if (start_time.tv_sec && last_time.tv_sec) {
>  			timespecsub(&start_time, &last_time, &diff_time);
> @@ -2499,13 +2550,6 @@ checkerloop (void *ap)
>  		if (use_watchdog)
>  			sd_notify(0, "WATCHDOG=1");
>  #endif
> -		rc = set_config_state(DAEMON_RUNNING);
> -		if (rc == ETIMEDOUT) {
> -			condlog(4, "timeout waiting for DAEMON_IDLE");
> -			continue;
> -		} else if (rc == EINVAL)
> -			/* daemon shutdown */
> -			break;
>  
>  		pthread_cleanup_push(cleanup_lock, &vecs->lock);
>  		lock(&vecs->lock);
> @@ -2833,7 +2877,7 @@ handle_signals(bool nonfatal)
>  		return;
>  	if (reconfig_sig) {
>  		condlog(2, "reconfigure (signal)");
> -		set_config_state(DAEMON_CONFIGURE);
> +		schedule_reconfigure();
>  	}
>  	if (log_reset_sig) {
>  		condlog(2, "reset log (signal)");
> diff --git a/multipathd/main.h b/multipathd/main.h
> index bc1f938..23ce919 100644
> --- a/multipathd/main.h
> +++ b/multipathd/main.h
> @@ -37,6 +37,7 @@ void exit_daemon(void);
>  const char * daemon_status(void);
>  enum daemon_status wait_for_state_change_if(enum daemon_status oldstate,
>  					    unsigned long ms);
> +void schedule_reconfigure(void);
>  int need_to_delay_reconfig (struct vectors *);
>  int reconfigure (struct vectors *);
>  int ev_add_path (struct path *, struct vectors *, int);
> @@ -44,7 +45,7 @@ int ev_remove_path (struct path *, struct vectors *, int);
>  int ev_add_map (char *, const char *, struct vectors *);
>  int ev_remove_map (char *, char *, int, struct vectors *);
>  int flush_map(struct multipath *, struct vectors *, int);
> -int set_config_state(enum daemon_status);
> +enum daemon_status set_config_state(enum daemon_status);

Can't we just remove set_config_state from main.h, and make it static?
Other than that, everything looks fine.

-Ben

>  void * mpath_alloc_prin_response(int prin_sa);
>  int prin_do_scsi_ioctl(char *, int rq_servact, struct prin_resp * resp,
>  		       int noisy);
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 08/35] multipathd: cli.h: formatting improvements
  2021-09-10 11:40 ` [dm-devel] [PATCH 08/35] multipathd: cli.h: formatting improvements mwilck
@ 2021-09-15 23:01   ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-15 23:01 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:40:53PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> No functional changes. Just make the code a little easier to read.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/cli.h | 82 ++++++++++++++++++++++++------------------------
>  1 file changed, 41 insertions(+), 41 deletions(-)
> 
> diff --git a/multipathd/cli.h b/multipathd/cli.h
> index fdfb9ae..1e8948a 100644
> --- a/multipathd/cli.h
> +++ b/multipathd/cli.h
> @@ -4,83 +4,83 @@
>  #include <stdint.h>
>  
>  enum {
> -	__LIST,
> +	__LIST,			/*  0 */
>  	__ADD,
>  	__DEL,
>  	__SWITCH,
>  	__SUSPEND,
> -	__RESUME,
> +	__RESUME,			/*  5 */
>  	__REINSTATE,
>  	__FAIL,
>  	__RESIZE,
>  	__RESET,
> -	__RELOAD,
> +	__RELOAD,			/* 10 */
>  	__FORCEQ,
>  	__DISABLEQ,
>  	__RESTOREQ,
>  	__PATHS,
> -	__MAPS,
> +	__MAPS,			/* 15 */
>  	__PATH,
>  	__MAP,
>  	__GROUP,
>  	__RECONFIGURE,
> -	__DAEMON,
> +	__DAEMON,			/* 20 */
>  	__STATUS,
>  	__STATS,
>  	__TOPOLOGY,
>  	__CONFIG,
> -	__BLACKLIST,
> +	__BLACKLIST,			/* 25 */
>  	__DEVICES,
>  	__RAW,
>  	__WILDCARDS,
>  	__QUIT,
> -	__SHUTDOWN,
> +	__SHUTDOWN,			/* 30 */
>  	__GETPRSTATUS,
>  	__SETPRSTATUS,
>  	__UNSETPRSTATUS,
>  	__FMT,
> -	__JSON,
> +	__JSON,			/* 35 */
>  	__GETPRKEY,
>  	__SETPRKEY,
>  	__UNSETPRKEY,
>  	__KEY,
> -	__LOCAL,
> +	__LOCAL,			/* 40 */
>  	__SETMARGINAL,
>  	__UNSETMARGINAL,
>  };
>  
> -#define LIST		(1 << __LIST)
> -#define ADD		(1 << __ADD)
> -#define DEL		(1 << __DEL)
> -#define SWITCH		(1 << __SWITCH)
> -#define SUSPEND		(1 << __SUSPEND)
> -#define RESUME		(1 << __RESUME)
> -#define REINSTATE	(1 << __REINSTATE)
> -#define FAIL		(1 << __FAIL)
> -#define RESIZE		(1 << __RESIZE)
> -#define RESET		(1 << __RESET)
> -#define RELOAD		(1 << __RELOAD)
> -#define FORCEQ		(1 << __FORCEQ)
> -#define DISABLEQ	(1 << __DISABLEQ)
> -#define RESTOREQ	(1 << __RESTOREQ)
> -#define PATHS		(1 << __PATHS)
> -#define MAPS		(1 << __MAPS)
> -#define PATH		(1 << __PATH)
> -#define MAP		(1 << __MAP)
> -#define GROUP		(1 << __GROUP)
> -#define RECONFIGURE	(1 << __RECONFIGURE)
> -#define DAEMON		(1 << __DAEMON)
> -#define STATUS		(1 << __STATUS)
> -#define STATS		(1 << __STATS)
> -#define TOPOLOGY	(1 << __TOPOLOGY)
> -#define CONFIG		(1 << __CONFIG)
> -#define BLACKLIST	(1 << __BLACKLIST)
> -#define DEVICES		(1 << __DEVICES)
> -#define RAW		(1 << __RAW)
> -#define COUNT		(1 << __COUNT)
> -#define WILDCARDS	(1 << __WILDCARDS)
> -#define QUIT		(1 << __QUIT)
> -#define SHUTDOWN	(1 << __SHUTDOWN)
> +#define LIST		(1ULL << __LIST)
> +#define ADD		(1ULL << __ADD)
> +#define DEL		(1ULL << __DEL)
> +#define SWITCH		(1ULL << __SWITCH)
> +#define SUSPEND	(1ULL << __SUSPEND)
> +#define RESUME		(1ULL << __RESUME)
> +#define REINSTATE	(1ULL << __REINSTATE)
> +#define FAIL		(1ULL << __FAIL)
> +#define RESIZE		(1ULL << __RESIZE)
> +#define RESET		(1ULL << __RESET)
> +#define RELOAD		(1ULL << __RELOAD)
> +#define FORCEQ		(1ULL << __FORCEQ)
> +#define DISABLEQ	(1ULL << __DISABLEQ)
> +#define RESTOREQ	(1ULL << __RESTOREQ)
> +#define PATHS		(1ULL << __PATHS)
> +#define MAPS		(1ULL << __MAPS)
> +#define PATH		(1ULL << __PATH)
> +#define MAP		(1ULL << __MAP)
> +#define GROUP		(1ULL << __GROUP)
> +#define RECONFIGURE	(1ULL << __RECONFIGURE)
> +#define DAEMON		(1ULL << __DAEMON)
> +#define STATUS		(1ULL << __STATUS)
> +#define STATS		(1ULL << __STATS)
> +#define TOPOLOGY	(1ULL << __TOPOLOGY)
> +#define CONFIG		(1ULL << __CONFIG)
> +#define BLACKLIST	(1ULL << __BLACKLIST)
> +#define DEVICES	(1ULL << __DEVICES)
> +#define RAW		(1ULL << __RAW)
> +#define COUNT		(1ULL << __COUNT)
> +#define WILDCARDS	(1ULL << __WILDCARDS)
> +#define QUIT		(1ULL << __QUIT)
> +#define SHUTDOWN	(1ULL << __SHUTDOWN)
>  #define GETPRSTATUS	(1ULL << __GETPRSTATUS)
>  #define SETPRSTATUS	(1ULL << __SETPRSTATUS)
>  #define UNSETPRSTATUS	(1ULL << __UNSETPRSTATUS)
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 09/35] multipathd: cli_del_map: fix reply for delayed action
  2021-09-10 11:40 ` [dm-devel] [PATCH 09/35] multipathd: cli_del_map: fix reply for delayed action mwilck
@ 2021-09-15 23:40   ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-15 23:40 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:40:54PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> Return code 2 from ev_remove_map means that a delayed remove has
> been started, which is not the same as failure.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/cli_handlers.c | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
> index 44f76ee..2e4b239 100644
> --- a/multipathd/cli_handlers.c
> +++ b/multipathd/cli_handlers.c
> @@ -783,6 +783,9 @@ cli_del_map (void * v, char ** reply, int * len, void * data)
>  		return 1;
>  	}
>  	rc = ev_remove_map(param, alias, minor, vecs);
> +	if (rc == 2)
> +		*reply = strdup("delayed");
> +
>  	FREE(alias);
>  	return rc;
>  }
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 10/35] multipathd: add prototype for cli_handler functions
  2021-09-10 11:40 ` [dm-devel] [PATCH 10/35] multipathd: add prototype for cli_handler functions mwilck
@ 2021-09-15 23:53   ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-15 23:53 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:40:55PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> Use a typedef instead of spelling out the function type everywhere.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/cli.c |  6 +++---
>  multipathd/cli.h | 10 ++++++----
>  2 files changed, 9 insertions(+), 7 deletions(-)
> 
> diff --git a/multipathd/cli.c b/multipathd/cli.c
> index 4d6c37c..5213813 100644
> --- a/multipathd/cli.c
> +++ b/multipathd/cli.c
> @@ -65,7 +65,7 @@ out:
>  }
>  
>  int
> -add_handler (uint64_t fp, int (*fn)(void *, char **, int *, void *))
> +add_handler (uint64_t fp, cli_handler *fn)
>  {
>  	struct handler * h;
>  
> @@ -100,7 +100,7 @@ find_handler (uint64_t fp)
>  }
>  
>  int
> -set_handler_callback (uint64_t fp, int (*fn)(void *, char **, int *, void *))
> +set_handler_callback (uint64_t fp, cli_handler *fn)
>  {
>  	struct handler * h = find_handler(fp);
>  
> @@ -112,7 +112,7 @@ set_handler_callback (uint64_t fp, int (*fn)(void *, char **, int *, void *))
>  }
>  
>  int
> -set_unlocked_handler_callback (uint64_t fp,int (*fn)(void *, char **, int *, void *))
> +set_unlocked_handler_callback (uint64_t fp, cli_handler *fn)
>  {
>  	struct handler * h = find_handler(fp);
>  
> diff --git a/multipathd/cli.h b/multipathd/cli.h
> index 1e8948a..3dac1b4 100644
> --- a/multipathd/cli.h
> +++ b/multipathd/cli.h
> @@ -124,16 +124,18 @@ struct key {
>  	int has_param;
>  };
>  
> +typedef int (cli_handler)(void *keywords, char **reply, int *len, void *data);
> +
>  struct handler {
>  	uint64_t fingerprint;
>  	int locked;
> -	int (*fn)(void *, char **, int *, void *);
> +	cli_handler *fn;
>  };
>  
>  int alloc_handlers (void);
> -int add_handler (uint64_t fp, int (*fn)(void *, char **, int *, void *));
> -int set_handler_callback (uint64_t fp, int (*fn)(void *, char **, int *, void *));
> -int set_unlocked_handler_callback (uint64_t fp, int (*fn)(void *, char **, int *, void *));
> +int add_handler (uint64_t fp, cli_handler *fn);
> +int set_handler_callback (uint64_t fp, cli_handler *fn);
> +int set_unlocked_handler_callback (uint64_t fp, cli_handler *fn);
>  int parse_cmd (char * cmd, char ** reply, int * len, void *, int);
>  int load_keys (void);
>  char * get_keyparam (vector v, uint64_t code);
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 11/35] multipathd: make all cli_handlers static
  2021-09-10 11:40 ` [dm-devel] [PATCH 11/35] multipathd: make all cli_handlers static mwilck
@ 2021-09-15 23:53   ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-15 23:53 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:40:56PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> The cli_handler functions are only called from the handler table and
> need not be exported.
> 
> Signed-off-by: Martin Wilck <mwilck@suse.com>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> ---
>  multipathd/cli_handlers.c | 214 ++++++++++++++++++++++----------------
>  multipathd/cli_handlers.h |  61 ++---------
>  multipathd/main.c         |  58 +----------
>  3 files changed, 134 insertions(+), 199 deletions(-)
> 
> diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
> index 2e4b239..1a9c822 100644
> --- a/multipathd/cli_handlers.c
> +++ b/multipathd/cli_handlers.c
> @@ -39,7 +39,7 @@
>  		*(__len) = *(__rep) ? sizeof(string_literal) : 0;	\
>  	} while (0)
>  
> -int
> +static int
>  show_paths (char ** r, int * len, struct vectors * vecs, char * style,
>  	    int pretty)
>  {
> @@ -70,7 +70,7 @@ show_paths (char ** r, int * len, struct vectors * vecs, char * style,
>  	return 0;
>  }
>  
> -int
> +static int
>  show_path (char ** r, int * len, struct vectors * vecs, struct path *pp,
>  	   char * style)
>  {
> @@ -85,7 +85,7 @@ show_path (char ** r, int * len, struct vectors * vecs, struct path *pp,
>  	return 0;
>  }
>  
> -int
> +static int
>  show_map_topology (char ** r, int * len, struct multipath * mpp,
>  		   struct vectors * vecs)
>  {
> @@ -102,7 +102,7 @@ show_map_topology (char ** r, int * len, struct multipath * mpp,
>  	return 0;
>  }
>  
> -int
> +static int
>  show_maps_topology (char ** r, int * len, struct vectors * vecs)
>  {
>  	STRBUF_ON_STACK(reply);
> @@ -128,7 +128,7 @@ show_maps_topology (char ** r, int * len, struct vectors * vecs)
>  	return 0;
>  }
>  
> -int
> +static int
>  show_maps_json (char ** r, int * len, struct vectors * vecs)
>  {
>  	STRBUF_ON_STACK(reply);
> @@ -149,7 +149,7 @@ show_maps_json (char ** r, int * len, struct vectors * vecs)
>  	return 0;
>  }
>  
> -int
> +static int
>  show_map_json (char ** r, int * len, struct multipath * mpp,
>  		   struct vectors * vecs)
>  {
> @@ -194,7 +194,7 @@ reset_stats(struct multipath * mpp)
>  	mpp->stat_map_failures = 0;
>  }
>  
> -int
> +static int
>  cli_list_config (void * v, char ** reply, int * len, void * data)
>  {
>  	condlog(3, "list config (operator)");
> @@ -207,7 +207,7 @@ static void v_free(void *x)
>  	vector_free(x);
>  }
>  
> -int
> +static int
>  cli_list_config_local (void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -223,7 +223,7 @@ cli_list_config_local (void * v, char ** reply, int * len, void * data)
>  	return ret;
>  }
>  
> -int
> +static int
>  cli_list_paths (void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -233,7 +233,7 @@ cli_list_paths (void * v, char ** reply, int * len, void * data)
>  	return show_paths(reply, len, vecs, PRINT_PATH_CHECKER, 1);
>  }
>  
> -int
> +static int
>  cli_list_paths_fmt (void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -244,7 +244,7 @@ cli_list_paths_fmt (void * v, char ** reply, int * len, void * data)
>  	return show_paths(reply, len, vecs, fmt, 1);
>  }
>  
> -int
> +static int
>  cli_list_paths_raw (void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -255,7 +255,7 @@ cli_list_paths_raw (void * v, char ** reply, int * len, void * data)
>  	return show_paths(reply, len, vecs, fmt, 0);
>  }
>  
> -int
> +static int
>  cli_list_path (void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -272,7 +272,7 @@ cli_list_path (void * v, char ** reply, int * len, void * data)
>  	return show_path(reply, len, vecs, pp, "%o");
>  }
>  
> -int
> +static int
>  cli_list_map_topology (void * v, char ** reply, int * len, void * data)
>  {
>  	struct multipath * mpp;
> @@ -291,7 +291,7 @@ cli_list_map_topology (void * v, char ** reply, int * len, void * data)
>  	return show_map_topology(reply, len, mpp, vecs);
>  }
>  
> -int
> +static int
>  cli_list_maps_topology (void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -301,7 +301,7 @@ cli_list_maps_topology (void * v, char ** reply, int * len, void * data)
>  	return show_maps_topology(reply, len, vecs);
>  }
>  
> -int
> +static int
>  cli_list_map_json (void * v, char ** reply, int * len, void * data)
>  {
>  	struct multipath * mpp;
> @@ -320,7 +320,7 @@ cli_list_map_json (void * v, char ** reply, int * len, void * data)
>  	return show_map_json(reply, len, mpp, vecs);
>  }
>  
> -int
> +static int
>  cli_list_maps_json (void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -330,7 +330,7 @@ cli_list_maps_json (void * v, char ** reply, int * len, void * data)
>  	return show_maps_json(reply, len, vecs);
>  }
>  
> -int
> +static int
>  cli_list_wildcards (void * v, char ** reply, int * len, void * data)
>  {
>  	STRBUF_ON_STACK(buf);
> @@ -343,7 +343,7 @@ cli_list_wildcards (void * v, char ** reply, int * len, void * data)
>  	return 0;
>  }
>  
> -int
> +static int
>  show_status (char ** r, int *len, struct vectors * vecs)
>  {
>  	STRBUF_ON_STACK(reply);
> @@ -356,7 +356,7 @@ show_status (char ** r, int *len, struct vectors * vecs)
>  	return 0;
>  }
>  
> -int
> +static int
>  show_daemon (char ** r, int *len)
>  {
>  	STRBUF_ON_STACK(reply);
> @@ -370,7 +370,7 @@ show_daemon (char ** r, int *len)
>  	return 0;
>  }
>  
> -int
> +static int
>  show_map (char ** r, int *len, struct multipath * mpp, char * style,
>  	  int pretty)
>  {
> @@ -384,7 +384,7 @@ show_map (char ** r, int *len, struct multipath * mpp, char * style,
>  	return 0;
>  }
>  
> -int
> +static int
>  show_maps (char ** r, int *len, struct vectors * vecs, char * style,
>  	   int pretty)
>  {
> @@ -419,7 +419,7 @@ show_maps (char ** r, int *len, struct vectors * vecs, char * style,
>  	return 0;
>  }
>  
> -int
> +static int
>  cli_list_maps_fmt (void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -430,7 +430,7 @@ cli_list_maps_fmt (void * v, char ** reply, int * len, void * data)
>  	return show_maps(reply, len, vecs, fmt, 1);
>  }
>  
> -int
> +static int
>  cli_list_maps_raw (void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -441,7 +441,7 @@ cli_list_maps_raw (void * v, char ** reply, int * len, void * data)
>  	return show_maps(reply, len, vecs, fmt, 0);
>  }
>  
> -int
> +static int
>  cli_list_map_fmt (void * v, char ** reply, int * len, void * data)
>  {
>  	struct multipath * mpp;
> @@ -461,27 +461,7 @@ cli_list_map_fmt (void * v, char ** reply, int * len, void * data)
>  	return show_map(reply, len, mpp, fmt, 1);
>  }
>  
> -int
> -cli_list_map_raw (void * v, char ** reply, int * len, void * data)
> -{
> -	struct multipath * mpp;
> -	struct vectors * vecs = (struct vectors *)data;
> -	char * param = get_keyparam(v, MAP);
> -	char * fmt = get_keyparam(v, FMT);
> -
> -	param = convert_dev(param, 0);
> -	get_path_layout(vecs->pathvec, 0);
> -	get_multipath_layout(vecs->mpvec, 1);
> -	mpp = find_mp_by_str(vecs->mpvec, param);
> -	if (!mpp)
> -		return 1;
> -
> -	condlog(3, "list map %s fmt %s (operator)", param, fmt);
> -
> -	return show_map(reply, len, mpp, fmt, 0);
> -}
> -
> -int
> +static int
>  cli_list_maps (void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -491,7 +471,7 @@ cli_list_maps (void * v, char ** reply, int * len, void * data)
>  	return show_maps(reply, len, vecs, PRINT_MAP_NAMES, 1);
>  }
>  
> -int
> +static int
>  cli_list_status (void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -501,7 +481,7 @@ cli_list_status (void * v, char ** reply, int * len, void * data)
>  	return show_status(reply, len, vecs);
>  }
>  
> -int
> +static int
>  cli_list_maps_status (void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -511,7 +491,7 @@ cli_list_maps_status (void * v, char ** reply, int * len, void * data)
>  	return show_maps(reply, len, vecs, PRINT_MAP_STATUS, 1);
>  }
>  
> -int
> +static int
>  cli_list_maps_stats (void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -521,7 +501,7 @@ cli_list_maps_stats (void * v, char ** reply, int * len, void * data)
>  	return show_maps(reply, len, vecs, PRINT_MAP_STATS, 1);
>  }
>  
> -int
> +static int
>  cli_list_daemon (void * v, char ** reply, int * len, void * data)
>  {
>  	condlog(3, "list daemon (operator)");
> @@ -529,7 +509,7 @@ cli_list_daemon (void * v, char ** reply, int * len, void * data)
>  	return show_daemon(reply, len);
>  }
>  
> -int
> +static int
>  cli_reset_maps_stats (void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -544,7 +524,7 @@ cli_reset_maps_stats (void * v, char ** reply, int * len, void * data)
>  	return 0;
>  }
>  
> -int
> +static int
>  cli_reset_map_stats (void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -562,7 +542,7 @@ cli_reset_map_stats (void * v, char ** reply, int * len, void * data)
>  	return 0;
>  }
>  
> -int
> +static int
>  cli_add_path (void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -678,7 +658,7 @@ blacklisted:
>  	return 0;
>  }
>  
> -int
> +static int
>  cli_del_path (void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -701,7 +681,7 @@ cli_del_path (void * v, char ** reply, int * len, void * data)
>  	return (ret == REMOVE_PATH_FAILURE);
>  }
>  
> -int
> +static int
>  cli_add_map (void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -761,7 +741,7 @@ cli_add_map (void * v, char ** reply, int * len, void * data)
>  	return rc;
>  }
>  
> -int
> +static int
>  cli_del_map (void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -790,7 +770,7 @@ cli_del_map (void * v, char ** reply, int * len, void * data)
>  	return rc;
>  }
>  
> -int
> +static int
>  cli_del_maps (void *v, char **reply, int *len, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -809,7 +789,7 @@ cli_del_maps (void *v, char **reply, int *len, void *data)
>  	return ret;
>  }
>  
> -int
> +static int
>  cli_reload(void *v, char **reply, int *len, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -837,7 +817,7 @@ cli_reload(void *v, char **reply, int *len, void *data)
>  	return reload_and_sync_map(mpp, vecs, 0);
>  }
>  
> -int resize_map(struct multipath *mpp, unsigned long long size,
> +static int resize_map(struct multipath *mpp, unsigned long long size,
>  	       struct vectors * vecs)
>  {
>  	char *params __attribute__((cleanup(cleanup_charp))) = NULL;
> @@ -862,7 +842,7 @@ int resize_map(struct multipath *mpp, unsigned long long size,
>  	return 0;
>  }
>  
> -int
> +static int
>  cli_resize(void *v, char **reply, int *len, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -927,7 +907,7 @@ cli_resize(void *v, char **reply, int *len, void *data)
>  	return 0;
>  }
>  
> -int
> +static int
>  cli_force_no_daemon_q(void * v, char ** reply, int * len, void * data)
>  {
>  	struct config *conf;
> @@ -940,7 +920,7 @@ cli_force_no_daemon_q(void * v, char ** reply, int * len, void * data)
>  	return 0;
>  }
>  
> -int
> +static int
>  cli_restore_no_daemon_q(void * v, char ** reply, int * len, void * data)
>  {
>  	struct config *conf;
> @@ -953,7 +933,7 @@ cli_restore_no_daemon_q(void * v, char ** reply, int * len, void * data)
>  	return 0;
>  }
>  
> -int
> +static int
>  cli_restore_queueing(void *v, char **reply, int *len, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -994,7 +974,7 @@ cli_restore_queueing(void *v, char **reply, int *len, void *data)
>  	return 0;
>  }
>  
> -int
> +static int
>  cli_restore_all_queueing(void *v, char **reply, int *len, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -1016,7 +996,7 @@ cli_restore_all_queueing(void *v, char **reply, int *len, void *data)
>  	return 0;
>  }
>  
> -int
> +static int
>  cli_disable_queueing(void *v, char **reply, int *len, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -1045,7 +1025,7 @@ cli_disable_queueing(void *v, char **reply, int *len, void *data)
>  	return 0;
>  }
>  
> -int
> +static int
>  cli_disable_all_queueing(void *v, char **reply, int *len, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -1064,7 +1044,7 @@ cli_disable_all_queueing(void *v, char **reply, int *len, void *data)
>  	return 0;
>  }
>  
> -int
> +static int
>  cli_switch_group(void * v, char ** reply, int * len, void * data)
>  {
>  	char * mapname = get_keyparam(v, MAP);
> @@ -1076,7 +1056,7 @@ cli_switch_group(void * v, char ** reply, int * len, void * data)
>  	return dm_switchgroup(mapname, groupnum);
>  }
>  
> -int
> +static int
>  cli_reconfigure(void * v, char ** reply, int * len, void * data)
>  {
>  	condlog(2, "reconfigure (operator)");
> @@ -1085,7 +1065,7 @@ cli_reconfigure(void * v, char ** reply, int * len, void * data)
>  	return 0;
>  }
>  
> -int
> +static int
>  cli_suspend(void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -1115,7 +1095,7 @@ cli_suspend(void * v, char ** reply, int * len, void * data)
>  	return 0;
>  }
>  
> -int
> +static int
>  cli_resume(void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -1147,7 +1127,7 @@ cli_resume(void * v, char ** reply, int * len, void * data)
>  	return 0;
>  }
>  
> -int
> +static int
>  cli_reinstate(void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -1170,7 +1150,7 @@ cli_reinstate(void * v, char ** reply, int * len, void * data)
>  	return dm_reinstate_path(pp->mpp->alias, pp->dev_t);
>  }
>  
> -int
> +static int
>  cli_reassign (void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -1194,7 +1174,7 @@ cli_reassign (void * v, char ** reply, int * len, void * data)
>  	return 0;
>  }
>  
> -int
> +static int
>  cli_fail(void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -1223,7 +1203,7 @@ cli_fail(void * v, char ** reply, int * len, void * data)
>  	return r;
>  }
>  
> -int
> +static int
>  show_blacklist (char ** r, int * len)
>  {
>  	STRBUF_ON_STACK(reply);
> @@ -1243,7 +1223,7 @@ show_blacklist (char ** r, int * len)
>  	return 0;
>  }
>  
> -int
> +static int
>  cli_list_blacklist (void * v, char ** reply, int * len, void * data)
>  {
>  	condlog(3, "list blacklist (operator)");
> @@ -1251,7 +1231,7 @@ cli_list_blacklist (void * v, char ** reply, int * len, void * data)
>  	return show_blacklist(reply, len);
>  }
>  
> -int
> +static int
>  show_devices (char ** r, int * len, struct vectors *vecs)
>  {
>  	STRBUF_ON_STACK(reply);
> @@ -1272,7 +1252,7 @@ show_devices (char ** r, int * len, struct vectors *vecs)
>  	return 0;
>  }
>  
> -int
> +static int
>  cli_list_devices (void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -1282,13 +1262,13 @@ cli_list_devices (void * v, char ** reply, int * len, void * data)
>  	return show_devices(reply, len, vecs);
>  }
>  
> -int
> +static int
>  cli_quit (void * v, char ** reply, int * len, void * data)
>  {
>  	return 0;
>  }
>  
> -int
> +static int
>  cli_shutdown (void * v, char ** reply, int * len, void * data)
>  {
>  	condlog(3, "shutdown (operator)");
> @@ -1296,7 +1276,7 @@ cli_shutdown (void * v, char ** reply, int * len, void * data)
>  	return 0;
>  }
>  
> -int
> +static int
>  cli_getprstatus (void * v, char ** reply, int * len, void * data)
>  {
>  	struct multipath * mpp;
> @@ -1321,7 +1301,7 @@ cli_getprstatus (void * v, char ** reply, int * len, void * data)
>  	return 0;
>  }
>  
> -int
> +static int
>  cli_setprstatus(void * v, char ** reply, int * len, void * data)
>  {
>  	struct multipath * mpp;
> @@ -1344,7 +1324,7 @@ cli_setprstatus(void * v, char ** reply, int * len, void * data)
>  	return 0;
>  }
>  
> -int
> +static int
>  cli_unsetprstatus(void * v, char ** reply, int * len, void * data)
>  {
>  	struct multipath * mpp;
> @@ -1366,7 +1346,7 @@ cli_unsetprstatus(void * v, char ** reply, int * len, void * data)
>  	return 0;
>  }
>  
> -int
> +static int
>  cli_getprkey(void * v, char ** reply, int * len, void * data)
>  {
>  	struct multipath * mpp;
> @@ -1398,7 +1378,7 @@ cli_getprkey(void * v, char ** reply, int * len, void * data)
>  	return 0;
>  }
>  
> -int
> +static int
>  cli_unsetprkey(void * v, char ** reply, int * len, void * data)
>  {
>  	struct multipath * mpp;
> @@ -1422,7 +1402,7 @@ cli_unsetprkey(void * v, char ** reply, int * len, void * data)
>  	return ret;
>  }
>  
> -int
> +static int
>  cli_setprkey(void * v, char ** reply, int * len, void * data)
>  {
>  	struct multipath * mpp;
> @@ -1454,7 +1434,7 @@ cli_setprkey(void * v, char ** reply, int * len, void * data)
>  	return ret;
>  }
>  
> -int cli_set_marginal(void * v, char ** reply, int * len, void * data)
> +static int cli_set_marginal(void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	char * param = get_keyparam(v, PATH);
> @@ -1481,7 +1461,7 @@ int cli_set_marginal(void * v, char ** reply, int * len, void * data)
>  	return reload_and_sync_map(pp->mpp, vecs, 0);
>  }
>  
> -int cli_unset_marginal(void * v, char ** reply, int * len, void * data)
> +static int cli_unset_marginal(void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	char * param = get_keyparam(v, PATH);
> @@ -1508,7 +1488,7 @@ int cli_unset_marginal(void * v, char ** reply, int * len, void * data)
>  	return reload_and_sync_map(pp->mpp, vecs, 0);
>  }
>  
> -int cli_unset_all_marginal(void * v, char ** reply, int * len, void * data)
> +static int cli_unset_all_marginal(void * v, char ** reply, int * len, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	char * mapname = get_keyparam(v, MAP);
> @@ -1544,3 +1524,63 @@ int cli_unset_all_marginal(void * v, char ** reply, int * len, void * data)
>  
>  	return reload_and_sync_map(mpp, vecs, 0);
>  }
> +
> +void init_handler_callbacks(void)
> +{
> +	set_handler_callback(LIST+PATHS, cli_list_paths);
> +	set_handler_callback(LIST+PATHS+FMT, cli_list_paths_fmt);
> +	set_handler_callback(LIST+PATHS+RAW+FMT, cli_list_paths_raw);
> +	set_handler_callback(LIST+PATH, cli_list_path);
> +	set_handler_callback(LIST+MAPS, cli_list_maps);
> +	set_handler_callback(LIST+STATUS, cli_list_status);
> +	set_unlocked_handler_callback(LIST+DAEMON, cli_list_daemon);
> +	set_handler_callback(LIST+MAPS+STATUS, cli_list_maps_status);
> +	set_handler_callback(LIST+MAPS+STATS, cli_list_maps_stats);
> +	set_handler_callback(LIST+MAPS+FMT, cli_list_maps_fmt);
> +	set_handler_callback(LIST+MAPS+RAW+FMT, cli_list_maps_raw);
> +	set_handler_callback(LIST+MAPS+TOPOLOGY, cli_list_maps_topology);
> +	set_handler_callback(LIST+TOPOLOGY, cli_list_maps_topology);
> +	set_handler_callback(LIST+MAPS+JSON, cli_list_maps_json);
> +	set_handler_callback(LIST+MAP+TOPOLOGY, cli_list_map_topology);
> +	set_handler_callback(LIST+MAP+FMT, cli_list_map_fmt);
> +	set_handler_callback(LIST+MAP+RAW+FMT, cli_list_map_fmt);
> +	set_handler_callback(LIST+MAP+JSON, cli_list_map_json);
> +	set_handler_callback(LIST+CONFIG+LOCAL, cli_list_config_local);
> +	set_handler_callback(LIST+CONFIG, cli_list_config);
> +	set_handler_callback(LIST+BLACKLIST, cli_list_blacklist);
> +	set_handler_callback(LIST+DEVICES, cli_list_devices);
> +	set_handler_callback(LIST+WILDCARDS, cli_list_wildcards);
> +	set_handler_callback(RESET+MAPS+STATS, cli_reset_maps_stats);
> +	set_handler_callback(RESET+MAP+STATS, cli_reset_map_stats);
> +	set_handler_callback(ADD+PATH, cli_add_path);
> +	set_handler_callback(DEL+PATH, cli_del_path);
> +	set_handler_callback(ADD+MAP, cli_add_map);
> +	set_handler_callback(DEL+MAP, cli_del_map);
> +	set_handler_callback(DEL+MAPS, cli_del_maps);
> +	set_handler_callback(SWITCH+MAP+GROUP, cli_switch_group);
> +	set_unlocked_handler_callback(RECONFIGURE, cli_reconfigure);
> +	set_handler_callback(SUSPEND+MAP, cli_suspend);
> +	set_handler_callback(RESUME+MAP, cli_resume);
> +	set_handler_callback(RESIZE+MAP, cli_resize);
> +	set_handler_callback(RELOAD+MAP, cli_reload);
> +	set_handler_callback(RESET+MAP, cli_reassign);
> +	set_handler_callback(REINSTATE+PATH, cli_reinstate);
> +	set_handler_callback(FAIL+PATH, cli_fail);
> +	set_handler_callback(DISABLEQ+MAP, cli_disable_queueing);
> +	set_handler_callback(RESTOREQ+MAP, cli_restore_queueing);
> +	set_handler_callback(DISABLEQ+MAPS, cli_disable_all_queueing);
> +	set_handler_callback(RESTOREQ+MAPS, cli_restore_all_queueing);
> +	set_unlocked_handler_callback(QUIT, cli_quit);
> +	set_unlocked_handler_callback(SHUTDOWN, cli_shutdown);
> +	set_handler_callback(GETPRSTATUS+MAP, cli_getprstatus);
> +	set_handler_callback(SETPRSTATUS+MAP, cli_setprstatus);
> +	set_handler_callback(UNSETPRSTATUS+MAP, cli_unsetprstatus);
> +	set_handler_callback(FORCEQ+DAEMON, cli_force_no_daemon_q);
> +	set_handler_callback(RESTOREQ+DAEMON, cli_restore_no_daemon_q);
> +	set_handler_callback(GETPRKEY+MAP, cli_getprkey);
> +	set_handler_callback(SETPRKEY+MAP+KEY, cli_setprkey);
> +	set_handler_callback(UNSETPRKEY+MAP, cli_unsetprkey);
> +	set_handler_callback(SETMARGINAL+PATH, cli_set_marginal);
> +	set_handler_callback(UNSETMARGINAL+PATH, cli_unset_marginal);
> +	set_handler_callback(UNSETMARGINAL+MAP, cli_unset_all_marginal);
> +}
> diff --git a/multipathd/cli_handlers.h b/multipathd/cli_handlers.h
> index 6f57b42..7eaf847 100644
> --- a/multipathd/cli_handlers.h
> +++ b/multipathd/cli_handlers.h
> @@ -1,55 +1,6 @@
> -int cli_list_paths (void * v, char ** reply, int * len, void * data);
> -int cli_list_paths_fmt (void * v, char ** reply, int * len, void * data);
> -int cli_list_paths_raw (void * v, char ** reply, int * len, void * data);
> -int cli_list_path (void * v, char ** reply, int * len, void * data);
> -int cli_list_status (void * v, char ** reply, int * len, void * data);
> -int cli_list_daemon (void * v, char ** reply, int * len, void * data);
> -int cli_list_maps (void * v, char ** reply, int * len, void * data);
> -int cli_list_maps_fmt (void * v, char ** reply, int * len, void * data);
> -int cli_list_maps_raw (void * v, char ** reply, int * len, void * data);
> -int cli_list_map_fmt (void * v, char ** reply, int * len, void * data);
> -int cli_list_map_raw (void * v, char ** reply, int * len, void * data);
> -int cli_list_maps_status (void * v, char ** reply, int * len, void * data);
> -int cli_list_maps_stats (void * v, char ** reply, int * len, void * data);
> -int cli_list_map_topology (void * v, char ** reply, int * len, void * data);
> -int cli_list_maps_topology (void * v, char ** reply, int * len, void * data);
> -int cli_list_map_json (void * v, char ** reply, int * len, void * data);
> -int cli_list_maps_json (void * v, char ** reply, int * len, void * data);
> -int cli_list_config (void * v, char ** reply, int * len, void * data);
> -int cli_list_config_local (void * v, char ** reply, int * len, void * data);
> -int cli_list_blacklist (void * v, char ** reply, int * len, void * data);
> -int cli_list_devices (void * v, char ** reply, int * len, void * data);
> -int cli_list_wildcards (void * v, char ** reply, int * len, void * data);
> -int cli_reset_maps_stats (void * v, char ** reply, int * len, void * data);
> -int cli_reset_map_stats (void * v, char ** reply, int * len, void * data);
> -int cli_add_path (void * v, char ** reply, int * len, void * data);
> -int cli_del_path (void * v, char ** reply, int * len, void * data);
> -int cli_add_map (void * v, char ** reply, int * len, void * data);
> -int cli_del_map (void * v, char ** reply, int * len, void * data);
> -int cli_del_maps (void * v, char ** reply, int * len, void * data);
> -int cli_switch_group(void * v, char ** reply, int * len, void * data);
> -int cli_reconfigure(void * v, char ** reply, int * len, void * data);
> -int cli_resize(void * v, char ** reply, int * len, void * data);
> -int cli_reload(void * v, char ** reply, int * len, void * data);
> -int cli_disable_queueing(void * v, char ** reply, int * len, void * data);
> -int cli_disable_all_queueing(void * v, char ** reply, int * len, void * data);
> -int cli_restore_queueing(void * v, char ** reply, int * len, void * data);
> -int cli_restore_all_queueing(void * v, char ** reply, int * len, void * data);
> -int cli_suspend(void * v, char ** reply, int * len, void * data);
> -int cli_resume(void * v, char ** reply, int * len, void * data);
> -int cli_reinstate(void * v, char ** reply, int * len, void * data);
> -int cli_fail(void * v, char ** reply, int * len, void * data);
> -int cli_force_no_daemon_q(void * v, char ** reply, int * len, void * data);
> -int cli_restore_no_daemon_q(void * v, char ** reply, int * len, void * data);
> -int cli_quit(void * v, char ** reply, int * len, void * data);
> -int cli_shutdown(void * v, char ** reply, int * len, void * data);
> -int cli_reassign (void * v, char ** reply, int * len, void * data);
> -int cli_getprstatus(void * v, char ** reply, int * len, void * data);
> -int cli_setprstatus(void * v, char ** reply, int * len, void * data);
> -int cli_unsetprstatus(void * v, char ** reply, int * len, void * data);
> -int cli_getprkey(void * v, char ** reply, int * len, void * data);
> -int cli_setprkey(void * v, char ** reply, int * len, void * data);
> -int cli_unsetprkey(void * v, char ** reply, int * len, void * data);
> -int cli_set_marginal(void * v, char ** reply, int * len, void * data);
> -int cli_unset_marginal(void * v, char ** reply, int * len, void * data);
> -int cli_unset_all_marginal(void * v, char ** reply, int * len, void * data);
> +#ifndef _CLI_HANDLERS_H
> +#define _CLI_HANDLERS_H
> +
> +void init_handler_callbacks(void);
> +
> +#endif
> diff --git a/multipathd/main.c b/multipathd/main.c
> index 5fb6989..bda51c9 100644
> --- a/multipathd/main.c
> +++ b/multipathd/main.c
> @@ -1693,63 +1693,7 @@ uxlsnrloop (void * ap)
>  	/* Tell main thread that thread has started */
>  	post_config_state(DAEMON_CONFIGURE);
>  
> -	set_handler_callback(LIST+PATHS, cli_list_paths);
> -	set_handler_callback(LIST+PATHS+FMT, cli_list_paths_fmt);
> -	set_handler_callback(LIST+PATHS+RAW+FMT, cli_list_paths_raw);
> -	set_handler_callback(LIST+PATH, cli_list_path);
> -	set_handler_callback(LIST+MAPS, cli_list_maps);
> -	set_handler_callback(LIST+STATUS, cli_list_status);
> -	set_unlocked_handler_callback(LIST+DAEMON, cli_list_daemon);
> -	set_handler_callback(LIST+MAPS+STATUS, cli_list_maps_status);
> -	set_handler_callback(LIST+MAPS+STATS, cli_list_maps_stats);
> -	set_handler_callback(LIST+MAPS+FMT, cli_list_maps_fmt);
> -	set_handler_callback(LIST+MAPS+RAW+FMT, cli_list_maps_raw);
> -	set_handler_callback(LIST+MAPS+TOPOLOGY, cli_list_maps_topology);
> -	set_handler_callback(LIST+TOPOLOGY, cli_list_maps_topology);
> -	set_handler_callback(LIST+MAPS+JSON, cli_list_maps_json);
> -	set_handler_callback(LIST+MAP+TOPOLOGY, cli_list_map_topology);
> -	set_handler_callback(LIST+MAP+FMT, cli_list_map_fmt);
> -	set_handler_callback(LIST+MAP+RAW+FMT, cli_list_map_fmt);
> -	set_handler_callback(LIST+MAP+JSON, cli_list_map_json);
> -	set_handler_callback(LIST+CONFIG+LOCAL, cli_list_config_local);
> -	set_handler_callback(LIST+CONFIG, cli_list_config);
> -	set_handler_callback(LIST+BLACKLIST, cli_list_blacklist);
> -	set_handler_callback(LIST+DEVICES, cli_list_devices);
> -	set_handler_callback(LIST+WILDCARDS, cli_list_wildcards);
> -	set_handler_callback(RESET+MAPS+STATS, cli_reset_maps_stats);
> -	set_handler_callback(RESET+MAP+STATS, cli_reset_map_stats);
> -	set_handler_callback(ADD+PATH, cli_add_path);
> -	set_handler_callback(DEL+PATH, cli_del_path);
> -	set_handler_callback(ADD+MAP, cli_add_map);
> -	set_handler_callback(DEL+MAP, cli_del_map);
> -	set_handler_callback(DEL+MAPS, cli_del_maps);
> -	set_handler_callback(SWITCH+MAP+GROUP, cli_switch_group);
> -	set_unlocked_handler_callback(RECONFIGURE, cli_reconfigure);
> -	set_handler_callback(SUSPEND+MAP, cli_suspend);
> -	set_handler_callback(RESUME+MAP, cli_resume);
> -	set_handler_callback(RESIZE+MAP, cli_resize);
> -	set_handler_callback(RELOAD+MAP, cli_reload);
> -	set_handler_callback(RESET+MAP, cli_reassign);
> -	set_handler_callback(REINSTATE+PATH, cli_reinstate);
> -	set_handler_callback(FAIL+PATH, cli_fail);
> -	set_handler_callback(DISABLEQ+MAP, cli_disable_queueing);
> -	set_handler_callback(RESTOREQ+MAP, cli_restore_queueing);
> -	set_handler_callback(DISABLEQ+MAPS, cli_disable_all_queueing);
> -	set_handler_callback(RESTOREQ+MAPS, cli_restore_all_queueing);
> -	set_unlocked_handler_callback(QUIT, cli_quit);
> -	set_unlocked_handler_callback(SHUTDOWN, cli_shutdown);
> -	set_handler_callback(GETPRSTATUS+MAP, cli_getprstatus);
> -	set_handler_callback(SETPRSTATUS+MAP, cli_setprstatus);
> -	set_handler_callback(UNSETPRSTATUS+MAP, cli_unsetprstatus);
> -	set_handler_callback(FORCEQ+DAEMON, cli_force_no_daemon_q);
> -	set_handler_callback(RESTOREQ+DAEMON, cli_restore_no_daemon_q);
> -	set_handler_callback(GETPRKEY+MAP, cli_getprkey);
> -	set_handler_callback(SETPRKEY+MAP+KEY, cli_setprkey);
> -	set_handler_callback(UNSETPRKEY+MAP, cli_unsetprkey);
> -	set_handler_callback(SETMARGINAL+PATH, cli_set_marginal);
> -	set_handler_callback(UNSETMARGINAL+PATH, cli_unset_marginal);
> -	set_handler_callback(UNSETMARGINAL+MAP, cli_unset_all_marginal);
> -
> +	init_handler_callbacks();
>  	umask(077);
>  	uxsock_listen(&uxsock_trigger, ux_sock, ap);
>  
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 12/35] multipathd: add and set cli_handlers in a single step
  2021-09-10 11:40 ` [dm-devel] [PATCH 12/35] multipathd: add and set cli_handlers in a single step mwilck
@ 2021-09-16  0:01   ` Benjamin Marzinski
  2021-09-16  7:22     ` Martin Wilck
  2021-11-12 21:45     ` Martin Wilck
  0 siblings, 2 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16  0:01 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:40:57PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> Modify set_handler_callback() such that a missing slot is created
> if no matching slot is found. This way, we can skip the initialization
> with NULL handlers on startup.
> 
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/cli.c | 85 +++++++-----------------------------------------
>  multipathd/cli.h |  6 ++--
>  2 files changed, 15 insertions(+), 76 deletions(-)
> 
> diff --git a/multipathd/cli.c b/multipathd/cli.c
> index 5213813..7020d2b 100644
> --- a/multipathd/cli.c
> +++ b/multipathd/cli.c
> @@ -100,26 +100,20 @@ find_handler (uint64_t fp)
>  }
>  
>  int
> -set_handler_callback (uint64_t fp, cli_handler *fn)
> +__set_handler_callback (uint64_t fp, cli_handler *fn, bool locked)
>  {
> -	struct handler * h = find_handler(fp);
> +	struct handler *h = find_handler(fp);
>  

Wouldn't it be a bug if we reset the handler? Is this really something
we need to check for? Also, if add_handler() just returned a pointer to
the handler, we wouldn't need to call fail_handler() immediately after
creating it.

-Ben

> -	if (!h)
> +	if (!h) {
> +		add_handler(fp, fn);
> +		h = find_handler(fp);
> +	}
> +	if (!h) {
> +		condlog(0, "%s: failed to set handler for code %"PRIu64,
> +			__func__, fp);
>  		return 1;
> -	h->fn = fn;
> -	h->locked = 1;
> -	return 0;
> -}
> -
> -int
> -set_unlocked_handler_callback (uint64_t fp, cli_handler *fn)
> -{
> -	struct handler * h = find_handler(fp);
> -
> -	if (!h)
> -		return 1;
> -	h->fn = fn;
> -	h->locked = 0;
> +	}
> +	h->locked = locked;
>  	return 0;
>  }
>  
> @@ -513,63 +507,6 @@ cli_init (void) {
>  	if (alloc_handlers())
>  		return 1;
>  
> -	add_handler(LIST+PATHS, NULL);
> -	add_handler(LIST+PATHS+FMT, NULL);
> -	add_handler(LIST+PATHS+RAW+FMT, NULL);
> -	add_handler(LIST+PATH, NULL);
> -	add_handler(LIST+STATUS, NULL);
> -	add_handler(LIST+DAEMON, NULL);
> -	add_handler(LIST+MAPS, NULL);
> -	add_handler(LIST+MAPS+STATUS, NULL);
> -	add_handler(LIST+MAPS+STATS, NULL);
> -	add_handler(LIST+MAPS+FMT, NULL);
> -	add_handler(LIST+MAPS+RAW+FMT, NULL);
> -	add_handler(LIST+MAPS+TOPOLOGY, NULL);
> -	add_handler(LIST+MAPS+JSON, NULL);
> -	add_handler(LIST+TOPOLOGY, NULL);
> -	add_handler(LIST+MAP+TOPOLOGY, NULL);
> -	add_handler(LIST+MAP+JSON, NULL);
> -	add_handler(LIST+MAP+FMT, NULL);
> -	add_handler(LIST+MAP+RAW+FMT, NULL);
> -	add_handler(LIST+CONFIG, NULL);
> -	add_handler(LIST+CONFIG+LOCAL, NULL);
> -	add_handler(LIST+BLACKLIST, NULL);
> -	add_handler(LIST+DEVICES, NULL);
> -	add_handler(LIST+WILDCARDS, NULL);
> -	add_handler(RESET+MAPS+STATS, NULL);
> -	add_handler(RESET+MAP+STATS, NULL);
> -	add_handler(ADD+PATH, NULL);
> -	add_handler(DEL+PATH, NULL);
> -	add_handler(ADD+MAP, NULL);
> -	add_handler(DEL+MAP, NULL);
> -	add_handler(DEL+MAPS, NULL);
> -	add_handler(SWITCH+MAP+GROUP, NULL);
> -	add_handler(RECONFIGURE, NULL);
> -	add_handler(SUSPEND+MAP, NULL);
> -	add_handler(RESUME+MAP, NULL);
> -	add_handler(RESIZE+MAP, NULL);
> -	add_handler(RESET+MAP, NULL);
> -	add_handler(RELOAD+MAP, NULL);
> -	add_handler(DISABLEQ+MAP, NULL);
> -	add_handler(RESTOREQ+MAP, NULL);
> -	add_handler(DISABLEQ+MAPS, NULL);
> -	add_handler(RESTOREQ+MAPS, NULL);
> -	add_handler(REINSTATE+PATH, NULL);
> -	add_handler(FAIL+PATH, NULL);
> -	add_handler(QUIT, NULL);
> -	add_handler(SHUTDOWN, NULL);
> -	add_handler(GETPRSTATUS+MAP, NULL);
> -	add_handler(SETPRSTATUS+MAP, NULL);
> -	add_handler(UNSETPRSTATUS+MAP, NULL);
> -	add_handler(GETPRKEY+MAP, NULL);
> -	add_handler(SETPRKEY+MAP+KEY, NULL);
> -	add_handler(UNSETPRKEY+MAP, NULL);
> -	add_handler(FORCEQ+DAEMON, NULL);
> -	add_handler(RESTOREQ+DAEMON, NULL);
> -	add_handler(SETMARGINAL+PATH, NULL);
> -	add_handler(UNSETMARGINAL+PATH, NULL);
> -	add_handler(UNSETMARGINAL+MAP, NULL);
> -
>  	return 0;
>  }
>  
> diff --git a/multipathd/cli.h b/multipathd/cli.h
> index 3dac1b4..dbb75be 100644
> --- a/multipathd/cli.h
> +++ b/multipathd/cli.h
> @@ -134,8 +134,10 @@ struct handler {
>  
>  int alloc_handlers (void);
>  int add_handler (uint64_t fp, cli_handler *fn);
> -int set_handler_callback (uint64_t fp, cli_handler *fn);
> -int set_unlocked_handler_callback (uint64_t fp, cli_handler *fn);
> +int __set_handler_callback (uint64_t fp, cli_handler *fn, bool locked);
> +#define set_handler_callback(fp, fn) __set_handler_callback(fp, fn, true)
> +#define set_unlocked_handler_callback(fp, fn) __set_handler_callback(fp, fn, false)
> +
>  int parse_cmd (char * cmd, char ** reply, int * len, void *, int);
>  int load_keys (void);
>  char * get_keyparam (vector v, uint64_t code);
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 13/35] multipathd: cli.c: use ESRCH for "command not found"
  2021-09-10 11:40 ` [dm-devel] [PATCH 13/35] multipathd: cli.c: use ESRCH for "command not found" mwilck
@ 2021-09-16  0:02   ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16  0:02 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:40:58PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> EAGAIN is too generic, and doesn't fit semantically either.
> ESRCH in't used anywhere else in our code.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/cli.c | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/multipathd/cli.c b/multipathd/cli.c
> index 7020d2b..f8c1dbd 100644
> --- a/multipathd/cli.c
> +++ b/multipathd/cli.c
> @@ -253,7 +253,7 @@ find_key (const char * str)
>   *
>   * returns:
>   * ENOMEM: not enough memory to allocate command
> - * EAGAIN: command not found
> + * ESRCH: command not found
>   * EINVAL: argument missing for command
>   */
>  static int
> @@ -288,7 +288,7 @@ get_cmdvec (char * cmd, vector *v)
>  		}
>  		kw = find_key(buff);
>  		if (!kw) {
> -			r = EAGAIN;
> +			r = ESRCH;
>  			goto out;
>  		}
>  		cmdkw = alloc_key();
> @@ -378,7 +378,7 @@ do_genhelp(struct strbuf *reply, const char *cmd, int error) {
>  	case ENOMEM:
>  		rc = print_strbuf(reply, "%s: Not enough memory\n", cmd);
>  		break;
> -	case EAGAIN:
> +	case ESRCH:
>  		rc = print_strbuf(reply, "%s: not found\n", cmd);
>  		break;
>  	case EINVAL:
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 14/35] multipathd: add "force_reconfigure" option
  2021-09-10 11:40 ` [dm-devel] [PATCH 14/35] multipathd: add "force_reconfigure" option mwilck
@ 2021-09-16  0:13   ` Benjamin Marzinski
  2021-09-16  7:34     ` Martin Wilck
  0 siblings, 1 reply; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16  0:13 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:40:59PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> Since e3270f7 ("multipathd: use weaker "force_reload" at startup"),
> (multipath-tools 0.7.0), we only reload those maps that must be
> reloaded during startup. "multipath reconfigure", OTOH, reloads
> every map, which may take a long time on systems with lots of
> storage devices, as every DM_DEVICE_RELOAD ioctl involves a
> suspend/resume cycle.
> 
> The logic we use during startup is actually very robust and catches
> all cases in which a reload is necessary. "reconfigure" operations
> are often done because of configuration changes, and usually don't
> require a full reload of every map.
> 
> This patch changes the default behavior of "multipath reconfigure"
> to "weak" reload, like we do on startup since e3270f7. The behavior
> can be changed by setting the configuration option
> "force_reconfigure yes" before starting the reconfigure operation.
> "multipath -r" is also affected, but "multipath -D -r" is not.
> 
> It would have been nice to have introduced a new cli command
> "reconfigure force" instead, but the way "reconfigure" is
> implemented, that would have required a major rewrite of the code.

This looks o.k. But I don't think it would be that hard to add a new
multipathd command to reconfigure all the devices.  My personal
preference would be to leave force_reconfigure off by default, so that
we keep the same behavior, and add a command to force a full reconfig.
I'll try to work up a patch with my idea that can apply on top of this.

But the code itself looks fine, and if we don't agree on my patch, I can
always just change the default for the RHEL version, at least until the
next major release, so

Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>

> 
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmultipath/config.c      |  1 +
>  libmultipath/config.h      |  1 +
>  libmultipath/configure.c   | 19 ++++++++++++++++++-
>  libmultipath/defaults.h    |  1 +
>  libmultipath/dict.c        |  4 ++++
>  multipath/multipath.8      |  6 ++++--
>  multipath/multipath.conf.5 | 17 +++++++++++++++++
>  multipathd/main.c          | 18 +++++-------------
>  multipathd/multipathd.8    |  6 ++++--
>  9 files changed, 55 insertions(+), 18 deletions(-)
> 
> diff --git a/libmultipath/config.c b/libmultipath/config.c
> index 30046a1..a1ef4c3 100644
> --- a/libmultipath/config.c
> +++ b/libmultipath/config.c
> @@ -869,6 +869,7 @@ int _init_config (const char *file, struct config *conf)
>  	conf->ghost_delay = DEFAULT_GHOST_DELAY;
>  	conf->all_tg_pt = DEFAULT_ALL_TG_PT;
>  	conf->recheck_wwid = DEFAULT_RECHECK_WWID;
> +	conf->force_reconfigure = DEFAULT_FORCE_RECONFIGURE;
>  	/*
>  	 * preload default hwtable
>  	 */
> diff --git a/libmultipath/config.h b/libmultipath/config.h
> index 933fe0d..4617177 100644
> --- a/libmultipath/config.h
> +++ b/libmultipath/config.h
> @@ -189,6 +189,7 @@ struct config {
>  	int skip_delegate;
>  	unsigned int sequence_nr;
>  	int recheck_wwid;
> +	int force_reconfigure;
>  
>  	char * multipath_dir;
>  	char * selector;
> diff --git a/libmultipath/configure.c b/libmultipath/configure.c
> index 7edb355..262657e 100644
> --- a/libmultipath/configure.c
> +++ b/libmultipath/configure.c
> @@ -1093,12 +1093,27 @@ out:
>  	return ret;
>  }
>  
> +static const char* reconfigure_str(int force)
> +{
> +	switch(force) {
> +	case FORCE_RELOAD_NONE:
> +		return "new";
> +	case FORCE_RELOAD_WEAK:
> +		return "changed";
> +	case FORCE_RELOAD_YES:
> +		return "all";
> +	default:
> +		return "<undefined>";
> +	}
> +}
> +
>  /*
>   * The force_reload parameter determines how coalesce_paths treats existing maps.
>   * FORCE_RELOAD_NONE: existing maps aren't touched at all
>   * FORCE_RELOAD_YES: all maps are rebuilt from scratch and (re)loaded in DM
>   * FORCE_RELOAD_WEAK: existing maps are compared to the current conf and only
> - * reloaded in DM if there's a difference. This is useful during startup.
> + * reloaded in DM if there's a difference. This is normally sufficient.
> + * This is controlled by the "force_reconfigure" config option.
>   */
>  int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid,
>  		    int force_reload, enum mpath_cmds cmd)
> @@ -1117,6 +1132,8 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid,
>  	int allow_queueing;
>  	struct bitfield *size_mismatch_seen;
>  
> +	condlog(2, "%s: reloading %s maps", __func__,
> +		reconfigure_str(force_reload));
>  	/* ignore refwwid if it's empty */
>  	if (refwwid && !strlen(refwwid))
>  		refwwid = NULL;
> diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h
> index c27946c..eab08a0 100644
> --- a/libmultipath/defaults.h
> +++ b/libmultipath/defaults.h
> @@ -56,6 +56,7 @@
>  #define DEFAULT_RECHECK_WWID RECHECK_WWID_OFF
>  /* Enable no foreign libraries by default */
>  #define DEFAULT_ENABLE_FOREIGN "NONE"
> +#define DEFAULT_FORCE_RECONFIGURE YN_NO
>  
>  #define CHECKINT_UNDEF		UINT_MAX
>  #define DEFAULT_CHECKINT	5
> diff --git a/libmultipath/dict.c b/libmultipath/dict.c
> index 7a72738..fee08cf 100644
> --- a/libmultipath/dict.c
> +++ b/libmultipath/dict.c
> @@ -299,6 +299,9 @@ declare_def_snprint(verbosity, print_int)
>  declare_def_handler(reassign_maps, set_yes_no)
>  declare_def_snprint(reassign_maps, print_yes_no)
>  
> +declare_def_handler(force_reconfigure, set_yes_no)
> +declare_def_snprint(force_reconfigure, print_yes_no)
> +
>  declare_def_handler(multipath_dir, set_str)
>  declare_def_snprint(multipath_dir, print_str)
>  
> @@ -1713,6 +1716,7 @@ init_keywords(vector keywords)
>  	install_keyword("polling_interval", &checkint_handler, &snprint_def_checkint);
>  	install_keyword("max_polling_interval", &def_max_checkint_handler, &snprint_def_max_checkint);
>  	install_keyword("reassign_maps", &def_reassign_maps_handler, &snprint_def_reassign_maps);
> +	install_keyword("force_reconfigure", &def_force_reconfigure_handler, &snprint_def_force_reconfigure);
>  	install_keyword("multipath_dir", &def_multipath_dir_handler, &snprint_def_multipath_dir);
>  	install_keyword("path_selector", &def_selector_handler, &snprint_def_selector);
>  	install_keyword("path_grouping_policy", &def_pgpolicy_handler, &snprint_def_pgpolicy);
> diff --git a/multipath/multipath.8 b/multipath/multipath.8
> index 17df59f..b3980c0 100644
> --- a/multipath/multipath.8
> +++ b/multipath/multipath.8
> @@ -264,9 +264,11 @@ Force new maps to use the specified policy, overriding the configuration in
>  .
>  .TP
>  .B \-r
> -Force a reload of all existing multipath maps. This command is delegated to
> +Reload existing multipath maps. This command is delegated to
>  the multipathd daemon if it's running. In this case, other command line
> -switches of the \fImultipath\fR command have no effect.
> +switches of the \fImultipath\fR command have no effect, and multipathd
> +executes a \fIreconfigure\fR command. See the \fIforce_reconfigure\fR option
> +in \fBmultipath.conf(5)\fR.
>  .
>  .TP
>  .BI \-R " retries"
> diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5
> index 42a15ff..814de66 100644
> --- a/multipath/multipath.conf.5
> +++ b/multipath/multipath.conf.5
> @@ -34,6 +34,7 @@ or \fBmultipathd show config\fR command.
>  .SH SYNTAX
>  .\" ----------------------------------------------------------------------------
>  .
> +.
>  The configuration file contains entries of the form:
>  .RS
>  .nf
> @@ -165,6 +166,22 @@ The default is: \fB4 * polling_interval\fR
>  .
>  .
>  .TP
> +.B force_reconfigure
> +This controls what happens when \fBmultipathd reconfigure\fR or
> +\fBmultipath -r\fR is executed. If set to \fIyes\fR, all multipath
> +maps will be reloaded, regardless if this is necessary or not, which
> +may take a lot of time on large systems. This used to be the default
> +on previous versions of multipath-tools. If set to \fIno\fR,
> +only those maps will be reloaded for which some parameters
> +have changed that are relevant for the device-mapper configuration of the map.
> +This is the behavior during \fImultipathd\fR startup.
> +.RS
> +.TP
> +The default is: \fBno\fR
> +.RE
> +.
> +.
> +.TP
>  .B reassign_maps
>  Enable reassigning of device-mapper maps. With this option multipathd
>  will remap existing device-mapper maps to always point to multipath
> diff --git a/multipathd/main.c b/multipathd/main.c
> index bda51c9..6d7c8c9 100644
> --- a/multipathd/main.c
> +++ b/multipathd/main.c
> @@ -2594,14 +2594,13 @@ checkerloop (void *ap)
>  }
>  
>  int
> -configure (struct vectors * vecs)
> +configure (struct vectors *vecs, bool force)
>  {
>  	struct multipath * mpp;
>  	struct path * pp;
>  	vector mpvec;
>  	int i, ret;
>  	struct config *conf;
> -	static int force_reload = FORCE_RELOAD_WEAK;
>  
>  	if (!vecs->pathvec && !(vecs->pathvec = vector_alloc())) {
>  		condlog(0, "couldn't allocate path vec in configure");
> @@ -2649,15 +2648,9 @@ configure (struct vectors * vecs)
>  	if (should_exit())
>  		goto fail;
>  
> -	/*
> -	 * create new set of maps & push changed ones into dm
> -	 * In the first call, use FORCE_RELOAD_WEAK to avoid making
> -	 * superfluous ACT_RELOAD ioctls. Later calls are done
> -	 * with FORCE_RELOAD_YES.
> -	 */
> -	ret = coalesce_paths(vecs, mpvec, NULL, force_reload, CMD_NONE);
> -	if (force_reload == FORCE_RELOAD_WEAK)
> -		force_reload = FORCE_RELOAD_YES;
> +	ret = coalesce_paths(vecs, mpvec, NULL,
> +			     force ? FORCE_RELOAD_YES : FORCE_RELOAD_WEAK,
> +			     CMD_NONE);
>  	if (ret != CP_OK) {
>  		condlog(0, "configure failed while coalescing paths");
>  		goto fail;
> @@ -2769,8 +2762,7 @@ reconfigure (struct vectors * vecs)
>  	rcu_assign_pointer(multipath_conf, conf);
>  	call_rcu(&old->rcu, rcu_free_config);
>  
> -	configure(vecs);
> -
> +	configure(vecs, conf->force_reconfigure == YN_YES);
>  
>  	return 0;
>  }
> diff --git a/multipathd/multipathd.8 b/multipathd/multipathd.8
> index 048a838..b52a617 100644
> --- a/multipathd/multipathd.8
> +++ b/multipathd/multipathd.8
> @@ -195,8 +195,10 @@ group index, starting with 1.
>  .
>  .TP
>  .B reconfigure
> -Reconfigures the multipaths. This should be triggered automatically after anyi
> -hotplug event.
> +Reconfigure the multipaths. This is only necessary after applying configuration
> +changes, as multipathd sets up new devices automatically. See the
> +\fIforce_reconfigure\fR option in \fBmultipath.conf(5)\fR. Note that multipathd
> +can't process most commands while reconfiguring.
>  .
>  .TP
>  .B suspend map|multipath $map
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 15/35] multipathd: uxlsnr: avoid stalled clients during reconfigure
  2021-09-10 11:41 ` [dm-devel] [PATCH 15/35] multipathd: uxlsnr: avoid stalled clients during reconfigure mwilck
@ 2021-09-16  2:17   ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16  2:17 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:41:00PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> Since 47cc1d3 ("multipathd: fix client response for socket
> activation"), we hold back clients while reconfigure is running.
> The idea of 47cc1d3 was to fix the behavior during initial
> start up. When multipathd reconfigures itself during runtime,
> and the reconfiguration takes a long time (a minute or more is
> not unusual in big configurations), clients will time out with
> no response ("timeout receiving packet"). Waiting for reconfigure
> to finish breaks our timeout handling.
> 
> Therefore we should only apply the logic of 47cc1d3 during initial
> configuration. In this case, the client that triggered socket
> activation may still encounter a timeout, but there's not much we can
> do about that.
> 
> Fixes: 47cc1d3 ("multipathd: fix client response for socket activation")
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/main.c   |  9 +++++++++
>  multipathd/uxlsnr.c | 12 ------------
>  2 files changed, 9 insertions(+), 12 deletions(-)
> 
> diff --git a/multipathd/main.c b/multipathd/main.c
> index 6d7c8c9..c6357ef 100644
> --- a/multipathd/main.c
> +++ b/multipathd/main.c
> @@ -1695,6 +1695,15 @@ uxlsnrloop (void * ap)
>  
>  	init_handler_callbacks();
>  	umask(077);
> +
> +	/*
> +	 * Wait for initial reconfiguration to finish, while
> +	 * hadling signals
> +	 */
> +	while (wait_for_state_change_if(DAEMON_CONFIGURE, 50)
> +	       == DAEMON_CONFIGURE)
> +		handle_signals(false);
> +
>  	uxsock_listen(&uxsock_trigger, ux_sock, ap);
>  
>  out_sock:
> diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
> index dbee0d6..20efbd3 100644
> --- a/multipathd/uxlsnr.c
> +++ b/multipathd/uxlsnr.c
> @@ -391,18 +391,6 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
>  			continue;
>  		}
>  
> -		/*
> -		 * Client connection. We shouldn't answer while we're
> -		 * configuring - nothing may be configured yet.
> -		 * But we can't wait forever either, because this thread
> -		 * must handle signals. So wait a short while only.
> -		 */
> -		if (wait_for_state_change_if(DAEMON_CONFIGURE, 10)
> -		    == DAEMON_CONFIGURE) {
> -			handle_signals(false);
> -			continue;
> -		}
> -
>  		/* see if a client wants to speak to us */
>  		for (i = POLLFDS_BASE; i < n_pfds; i++) {
>  			if (polls[i].revents & POLLIN) {
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 16/35] multipathd: uxlsnr: handle client HUP
  2021-09-10 11:41 ` [dm-devel] [PATCH 16/35] multipathd: uxlsnr: handle client HUP mwilck
@ 2021-09-16  2:17   ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16  2:17 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:41:01PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> The unix socket listener thread doesn't even look at the revents
> returned by poll() while the daemon is configuring. This may cause a
> closed client socket to be kept open for a long time by the server,
> while the listener basically performs a busy loop, as ppoll() always
> returns immediately as long as the POLLHUP condition exists.
> 
> Worse, it can happen that multipathd reads data from such a closed
> client socket after the client has disconnected. See the description
> of POLLHUP in poll(2).
> 
> Close connections immediately if HUP is received.
> 
> Also, use the fd in log messages to identify the client rather
> than the random index.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/uxlsnr.c | 12 +++++++++---
>  1 file changed, 9 insertions(+), 3 deletions(-)
> 
> diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
> index 20efbd3..7e88538 100644
> --- a/multipathd/uxlsnr.c
> +++ b/multipathd/uxlsnr.c
> @@ -393,7 +393,7 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
>  
>  		/* see if a client wants to speak to us */
>  		for (i = POLLFDS_BASE; i < n_pfds; i++) {
> -			if (polls[i].revents & POLLIN) {
> +			if (polls[i].revents & (POLLIN|POLLHUP|POLLERR)) {
>  				struct timespec start_time;
>  
>  				c = NULL;
> @@ -410,6 +410,12 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
>  						i, polls[i].fd);
>  					continue;
>  				}
> +				if (polls[i].revents & (POLLHUP|POLLERR)) {
> +					condlog(4, "cli[%d]: Disconnected",
> +						c->fd);
> +					dead_client(c);
> +					continue;
> +				}
>  				get_monotonic_time(&start_time);
>  				if (recv_packet_from_client(c->fd, &inbuf,
>  							    uxsock_timeout)
> @@ -423,7 +429,7 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
>  					continue;
>  				}
>  				condlog(4, "cli[%d]: Got request [%s]",
> -					i, inbuf);
> +					polls[i].fd, inbuf);
>  				uxsock_trigger(inbuf, &reply, &rlen,
>  					       _socket_client_is_root(c->fd),
>  					       trigger_data);
> @@ -434,7 +440,7 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
>  					} else {
>  						condlog(4, "cli[%d]: "
>  							"Reply [%d bytes]",
> -							i, rlen);
> +							polls[i].fd, rlen);
>  					}
>  					FREE(reply);
>  					reply = NULL;
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 17/35] multipathd: uxlsnr: use symbolic values for pollfd indices
  2021-09-10 11:41 ` [dm-devel] [PATCH 17/35] multipathd: uxlsnr: use symbolic values for pollfd indices mwilck
@ 2021-09-16  2:18   ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16  2:18 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:41:02PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> Avoid hardcoding the indices as 0, 1, 2...
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/uxlsnr.c | 25 +++++++++++++++----------
>  1 file changed, 15 insertions(+), 10 deletions(-)
> 
> diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
> index 7e88538..6506109 100644
> --- a/multipathd/uxlsnr.c
> +++ b/multipathd/uxlsnr.c
> @@ -46,8 +46,13 @@ struct client {
>  	int fd;
>  };
>  
> -/* The number of fds we poll on, other than individual client connections */
> -#define POLLFDS_BASE 2
> +/* Indices for array of poll fds */
> +enum {
> +	POLLFD_UX = 0,
> +	POLLFD_NOTIFY,
> +	POLLFDS_BASE,
> +};
> +
>  #define POLLFD_CHUNK (4096 / sizeof(struct pollfd))
>  /* Minimum mumber of pollfds to reserve for clients */
>  #define MIN_POLLS (POLLFD_CHUNK - POLLFDS_BASE)
> @@ -339,8 +344,8 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
>  			}
>  		}
>  		if (num_clients < MAX_CLIENTS) {
> -			polls[0].fd = ux_sock;
> -			polls[0].events = POLLIN;
> +			polls[POLLFD_UX].fd = ux_sock;
> +			polls[POLLFD_UX].events = POLLIN;
>  		} else {
>  			/*
>  			 * New clients can't connect, num_clients won't grow
> @@ -348,15 +353,15 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
>  			 */
>  			condlog(1, "%s: max client connections reached, pausing polling",
>  				__func__);
> -			polls[0].fd = -1;
> +			polls[POLLFD_UX].fd = -1;
>  		}
>  
>  		reset_watch(notify_fd, &wds, &sequence_nr);
>  		if (notify_fd == -1 || (wds.conf_wd == -1 && wds.dir_wd == -1))
> -			polls[1].fd = -1;
> +			polls[POLLFD_NOTIFY].fd = -1;
>  		else
> -			polls[1].fd = notify_fd;
> -		polls[1].events = POLLIN;
> +			polls[POLLFD_NOTIFY].fd = notify_fd;
> +		polls[POLLFD_NOTIFY].events = POLLIN;
>  
>  		/* setup the clients */
>  		i = POLLFDS_BASE;
> @@ -454,12 +459,12 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
>  		handle_signals(true);
>  
>  		/* see if we got a new client */
> -		if (polls[0].revents & POLLIN) {
> +		if (polls[POLLFD_UX].revents & POLLIN) {
>  			new_client(ux_sock);
>  		}
>  
>  		/* handle inotify events on config files */
> -		if (polls[1].revents & POLLIN)
> +		if (polls[POLLFD_NOTIFY].revents & POLLIN)
>  			handle_inotify(notify_fd, &wds);
>  	}
>  
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 18/35] multipathd: uxlsnr: avoid using fd -1 in ppoll()
  2021-09-10 11:41 ` [dm-devel] [PATCH 18/35] multipathd: uxlsnr: avoid using fd -1 in ppoll() mwilck
@ 2021-09-16  2:18   ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16  2:18 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:41:03PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> Minor edit: if notifications are off, we set the poll fd to
> -1 but still use the POLLIN mask. It looks nicer if to poll
> the correct fd, but reset the event mask to 0 if we're not
> actually interested in it.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/uxlsnr.c | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
> index 6506109..98a9f71 100644
> --- a/multipathd/uxlsnr.c
> +++ b/multipathd/uxlsnr.c
> @@ -357,11 +357,11 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
>  		}
>  
>  		reset_watch(notify_fd, &wds, &sequence_nr);
> +		polls[POLLFD_NOTIFY].fd = notify_fd;
>  		if (notify_fd == -1 || (wds.conf_wd == -1 && wds.dir_wd == -1))
> -			polls[POLLFD_NOTIFY].fd = -1;
> +			polls[POLLFD_NOTIFY].events = 0;
>  		else
> -			polls[POLLFD_NOTIFY].fd = notify_fd;
> -		polls[POLLFD_NOTIFY].events = POLLIN;
> +			polls[POLLFD_NOTIFY].events = POLLIN;
>  
>  		/* setup the clients */
>  		i = POLLFDS_BASE;
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 19/35] multipathd: uxlsnr: data structure for stateful client connection
  2021-09-10 11:41 ` [dm-devel] [PATCH 19/35] multipathd: uxlsnr: data structure for stateful client connection mwilck
@ 2021-09-16  2:19   ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16  2:19 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:41:04PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> Currently the uxlsnr handles each client request (receive requset -
> handle request - respond) in a single loop iteration. This has
> severe disadvantages. In particular, the code may wait in poll()
> called from read_all(), or wait for the vecs lock, while other
> clients are ready to be serviced or signals to be handled.
> 
> This patch adds some fields to "struct client" which will be used
> by later patches to change this into a state machine that basically
> waits only in place, the ppoll() call in uxsock_listen().
> 
> For now, we just introduce and initialize the fields.
> 

Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/uxlsnr.c | 27 +++++++++++++++++++++++++--
>  1 file changed, 25 insertions(+), 2 deletions(-)
> 
> diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
> index 98a9f71..e701a1c 100644
> --- a/multipathd/uxlsnr.c
> +++ b/multipathd/uxlsnr.c
> @@ -40,10 +40,30 @@
>  #include "main.h"
>  #include "cli.h"
>  #include "uxlsnr.h"
> +#include "strbuf.h"
> +
> +/* state of client connection */
> +enum {
> +	CLT_RECV,
> +	CLT_PARSE,
> +	CLT_WAIT_LOCK,
> +	CLT_WORK,
> +	CLT_SEND,
> +};
>  
>  struct client {
>  	struct list_head node;
> +	struct timespec expires;
> +	int state;
>  	int fd;
> +	vector cmdvec;
> +	/* NUL byte at end */
> +	char cmd[_MAX_CMD_LEN + 1];
> +	struct strbuf reply;
> +	struct handler *handler;
> +	size_t cmd_len, len;
> +	int error;
> +	bool is_root;
>  };
>  
>  /* Indices for array of poll fds */
> @@ -104,14 +124,14 @@ static void new_client(int ux_sock)
>  	if (fd == -1)
>  		return;
>  
> -	c = (struct client *)MALLOC(sizeof(*c));
> +	c = calloc(1, sizeof(*c));
>  	if (!c) {
>  		close(fd);
>  		return;
>  	}
> -	memset(c, 0, sizeof(*c));
>  	INIT_LIST_HEAD(&c->node);
>  	c->fd = fd;
> +	c->state = CLT_RECV;
>  
>  	/* put it in our linked list */
>  	pthread_mutex_lock(&client_lock);
> @@ -127,6 +147,9 @@ static void _dead_client(struct client *c)
>  	int fd = c->fd;
>  	list_del_init(&c->node);
>  	c->fd = -1;
> +	reset_strbuf(&c->reply);
> +	if (c->cmdvec)
> +		free_keys(c->cmdvec);
>  	FREE(c);
>  	close(fd);
>  }
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 20/35] multipathd: move uxsock_trigger() to uxlsnr.c
  2021-09-10 11:41 ` [dm-devel] [PATCH 20/35] multipathd: move uxsock_trigger() to uxlsnr.c mwilck
@ 2021-09-16  2:19   ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16  2:19 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:41:05PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> uxsock_trigger() really belongs into cli.c. I suppose that way back in
> the past there were strong reasons to call this function via a
> pointer. I don't think these reasons are valid any more. Moving
> the function to cli.c allows restructuring the code.
> 
> No functional changes.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/main.c   | 44 +-------------------------------------------
>  multipathd/uxlsnr.c | 44 ++++++++++++++++++++++++++++++++++++++++++--
>  multipathd/uxlsnr.h |  4 +---
>  3 files changed, 44 insertions(+), 48 deletions(-)
> 
> diff --git a/multipathd/main.c b/multipathd/main.c
> index c6357ef..ec4bcc3 100644
> --- a/multipathd/main.c
> +++ b/multipathd/main.c
> @@ -1526,48 +1526,6 @@ map_discovery (struct vectors * vecs)
>  	return 0;
>  }
>  
> -int
> -uxsock_trigger (char * str, char ** reply, int * len, bool is_root,
> -		void * trigger_data)
> -{
> -	struct vectors * vecs;
> -	int r;
> -
> -	*reply = NULL;
> -	*len = 0;
> -	vecs = (struct vectors *)trigger_data;
> -
> -	if ((str != NULL) && (is_root == false) &&
> -	    (strncmp(str, "list", strlen("list")) != 0) &&
> -	    (strncmp(str, "show", strlen("show")) != 0)) {
> -		*reply = STRDUP("permission deny: need to be root");
> -		if (*reply)
> -			*len = strlen(*reply) + 1;
> -		return 1;
> -	}
> -
> -	r = parse_cmd(str, reply, len, vecs, uxsock_timeout / 1000);
> -
> -	if (r > 0) {
> -		if (r == ETIMEDOUT)
> -			*reply = STRDUP("timeout\n");
> -		else
> -			*reply = STRDUP("fail\n");
> -		if (*reply)
> -			*len = strlen(*reply) + 1;
> -		r = 1;
> -	}
> -	else if (!r && *len == 0) {
> -		*reply = STRDUP("ok\n");
> -		if (*reply)
> -			*len = strlen(*reply) + 1;
> -		r = 0;
> -	}
> -	/* else if (r < 0) leave *reply alone */
> -
> -	return r;
> -}
> -
>  int
>  uev_trigger (struct uevent * uev, void * trigger_data)
>  {
> @@ -1704,7 +1662,7 @@ uxlsnrloop (void * ap)
>  	       == DAEMON_CONFIGURE)
>  		handle_signals(false);
>  
> -	uxsock_listen(&uxsock_trigger, ux_sock, ap);
> +	uxsock_listen(ux_sock, ap);
>  
>  out_sock:
>  	pthread_cleanup_pop(1); /* uxsock_cleanup */
> diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
> index e701a1c..622aac1 100644
> --- a/multipathd/uxlsnr.c
> +++ b/multipathd/uxlsnr.c
> @@ -311,11 +311,51 @@ static void handle_inotify(int fd, struct watch_descriptors *wds)
>  		condlog(1, "Multipath configuration updated.\nReload multipathd for changes to take effect");
>  }
>  
> +static int uxsock_trigger(char *str, char **reply, int *len,
> +			  bool is_root, void *trigger_data)
> +{
> +	struct vectors * vecs;
> +	int r;
> +
> +	*reply = NULL;
> +	*len = 0;
> +	vecs = (struct vectors *)trigger_data;
> +
> +	if ((str != NULL) && (is_root == false) &&
> +	    (strncmp(str, "list", strlen("list")) != 0) &&
> +	    (strncmp(str, "show", strlen("show")) != 0)) {
> +		*reply = STRDUP("permission deny: need to be root");
> +		if (*reply)
> +			*len = strlen(*reply) + 1;
> +		return 1;
> +	}
> +
> +	r = parse_cmd(str, reply, len, vecs, uxsock_timeout / 1000);
> +
> +	if (r > 0) {
> +		if (r == ETIMEDOUT)
> +			*reply = STRDUP("timeout\n");
> +		else
> +			*reply = STRDUP("fail\n");
> +		if (*reply)
> +			*len = strlen(*reply) + 1;
> +		r = 1;
> +	}
> +	else if (!r && *len == 0) {
> +		*reply = STRDUP("ok\n");
> +		if (*reply)
> +			*len = strlen(*reply) + 1;
> +		r = 0;
> +	}
> +	/* else if (r < 0) leave *reply alone */
> +
> +	return r;
> +}
> +
>  /*
>   * entry point
>   */
> -void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
> -		     void * trigger_data)
> +void *uxsock_listen(long ux_sock, void *trigger_data)
>  {
>  	int rlen;
>  	char *inbuf;
> diff --git a/multipathd/uxlsnr.h b/multipathd/uxlsnr.h
> index 18f008d..60c3a2c 100644
> --- a/multipathd/uxlsnr.h
> +++ b/multipathd/uxlsnr.h
> @@ -3,10 +3,8 @@
>  
>  #include <stdbool.h>
>  
> -typedef int (uxsock_trigger_fn)(char *, char **, int *, bool, void *);
> -
>  void uxsock_cleanup(void *arg);
> -void *uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
> +void *uxsock_listen(long ux_sock,
>  		    void * trigger_data);
>  
>  #endif
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 21/35] multipathd: move parse_cmd() to uxlsnr.c
  2021-09-10 11:41 ` [dm-devel] [PATCH 21/35] multipathd: move parse_cmd() " mwilck
@ 2021-09-16  2:19   ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16  2:19 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:41:06PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> parse_cmd() does more than the name says - it parses, executes
> handlers, and even provides reply strings for some cases. This doesn't
> work well with the state machine idea. Thus move it to uxlsnr.c,
> where later patches will move some functionality elsewhere.
> 
> No functional changes.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/cli.c    | 74 +++++----------------------------------------
>  multipathd/cli.h    |  5 ++-
>  multipathd/uxlsnr.c | 61 +++++++++++++++++++++++++++++++++++++
>  3 files changed, 73 insertions(+), 67 deletions(-)
> 
> diff --git a/multipathd/cli.c b/multipathd/cli.c
> index f8c1dbd..29d6a6e 100644
> --- a/multipathd/cli.c
> +++ b/multipathd/cli.c
> @@ -256,8 +256,7 @@ find_key (const char * str)
>   * ESRCH: command not found
>   * EINVAL: argument missing for command
>   */
> -static int
> -get_cmdvec (char * cmd, vector *v)
> +int get_cmdvec (char *cmd, vector *v)
>  {
>  	int i;
>  	int r = 0;
> @@ -322,7 +321,7 @@ out:
>  }
>  
>  static uint64_t
> -fingerprint(vector vec)
> +fingerprint(const struct _vector *vec)
>  {
>  	int i;
>  	uint64_t fp = 0;
> @@ -337,6 +336,11 @@ fingerprint(vector vec)
>  	return fp;
>  }
>  
> +struct handler *find_handler_for_cmdvec(const struct _vector *v)
> +{
> +	return find_handler(fingerprint(v));
> +}
> +
>  int
>  alloc_handlers (void)
>  {
> @@ -415,8 +419,7 @@ do_genhelp(struct strbuf *reply, const char *cmd, int error) {
>  }
>  
>  
> -static char *
> -genhelp_handler (const char *cmd, int error)
> +char *genhelp_handler(const char *cmd, int error)
>  {
>  	STRBUF_ON_STACK(reply);
>  
> @@ -425,67 +428,6 @@ genhelp_handler (const char *cmd, int error)
>  	return steal_strbuf_str(&reply);
>  }
>  
> -int
> -parse_cmd (char * cmd, char ** reply, int * len, void * data, int timeout )
> -{
> -	int r;
> -	struct handler * h;
> -	vector cmdvec = NULL;
> -	struct timespec tmo;
> -
> -	r = get_cmdvec(cmd, &cmdvec);
> -
> -	if (r) {
> -		*reply = genhelp_handler(cmd, r);
> -		if (*reply == NULL)
> -			return EINVAL;
> -		*len = strlen(*reply) + 1;
> -		return 0;
> -	}
> -
> -	h = find_handler(fingerprint(cmdvec));
> -
> -	if (!h || !h->fn) {
> -		free_keys(cmdvec);
> -		*reply = genhelp_handler(cmd, EINVAL);
> -		if (*reply == NULL)
> -			return EINVAL;
> -		*len = strlen(*reply) + 1;
> -		return 0;
> -	}
> -
> -	/*
> -	 * execute handler
> -	 */
> -	if (clock_gettime(CLOCK_REALTIME, &tmo) == 0) {
> -		tmo.tv_sec += timeout;
> -	} else {
> -		tmo.tv_sec = 0;
> -	}
> -	if (h->locked) {
> -		int locked = 0;
> -		struct vectors * vecs = (struct vectors *)data;
> -
> -		pthread_cleanup_push(cleanup_lock, &vecs->lock);
> -		if (tmo.tv_sec) {
> -			r = timedlock(&vecs->lock, &tmo);
> -		} else {
> -			lock(&vecs->lock);
> -			r = 0;
> -		}
> -		if (r == 0) {
> -			locked = 1;
> -			pthread_testcancel();
> -			r = h->fn(cmdvec, reply, len, data);
> -		}
> -		pthread_cleanup_pop(locked);
> -	} else
> -		r = h->fn(cmdvec, reply, len, data);
> -	free_keys(cmdvec);
> -
> -	return r;
> -}
> -
>  char *
>  get_keyparam (vector v, uint64_t code)
>  {
> diff --git a/multipathd/cli.h b/multipathd/cli.h
> index dbb75be..eed606a 100644
> --- a/multipathd/cli.h
> +++ b/multipathd/cli.h
> @@ -138,7 +138,10 @@ int __set_handler_callback (uint64_t fp, cli_handler *fn, bool locked);
>  #define set_handler_callback(fp, fn) __set_handler_callback(fp, fn, true)
>  #define set_unlocked_handler_callback(fp, fn) __set_handler_callback(fp, fn, false)
>  
> -int parse_cmd (char * cmd, char ** reply, int * len, void *, int);
> +int get_cmdvec (char *cmd, vector *v);
> +struct handler *find_handler_for_cmdvec(const struct _vector *v);
> +char *genhelp_handler (const char *cmd, int error);
> +
>  int load_keys (void);
>  char * get_keyparam (vector v, uint64_t code);
>  void free_keys (vector vec);
> diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
> index 622aac1..cbbcb2c 100644
> --- a/multipathd/uxlsnr.c
> +++ b/multipathd/uxlsnr.c
> @@ -311,6 +311,67 @@ static void handle_inotify(int fd, struct watch_descriptors *wds)
>  		condlog(1, "Multipath configuration updated.\nReload multipathd for changes to take effect");
>  }
>  
> +static int parse_cmd (char *cmd, char **reply, int *len, void *data,
> +		      int timeout)
> +{
> +	int r;
> +	struct handler * h;
> +	vector cmdvec = NULL;
> +	struct timespec tmo;
> +
> +	r = get_cmdvec(cmd, &cmdvec);
> +
> +	if (r) {
> +		*reply = genhelp_handler(cmd, r);
> +		if (*reply == NULL)
> +			return EINVAL;
> +		*len = strlen(*reply) + 1;
> +		return 0;
> +	}
> +
> +	h = find_handler_for_cmdvec(cmdvec);
> +
> +	if (!h || !h->fn) {
> +		free_keys(cmdvec);
> +		*reply = genhelp_handler(cmd, EINVAL);
> +		if (*reply == NULL)
> +			return EINVAL;
> +		*len = strlen(*reply) + 1;
> +		return 0;
> +	}
> +
> +	/*
> +	 * execute handler
> +	 */
> +	if (clock_gettime(CLOCK_REALTIME, &tmo) == 0) {
> +		tmo.tv_sec += timeout;
> +	} else {
> +		tmo.tv_sec = 0;
> +	}
> +	if (h->locked) {
> +		int locked = 0;
> +		struct vectors * vecs = (struct vectors *)data;
> +
> +		pthread_cleanup_push(cleanup_lock, &vecs->lock);
> +		if (tmo.tv_sec) {
> +			r = timedlock(&vecs->lock, &tmo);
> +		} else {
> +			lock(&vecs->lock);
> +			r = 0;
> +		}
> +		if (r == 0) {
> +			locked = 1;
> +			pthread_testcancel();
> +			r = h->fn(cmdvec, reply, len, data);
> +		}
> +		pthread_cleanup_pop(locked);
> +	} else
> +		r = h->fn(cmdvec, reply, len, data);
> +	free_keys(cmdvec);
> +
> +	return r;
> +}
> +
>  static int uxsock_trigger(char *str, char **reply, int *len,
>  			  bool is_root, void *trigger_data)
>  {
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 22/35] multipathd: uxlsnr: remove check_timeout()
  2021-09-10 11:41 ` [dm-devel] [PATCH 22/35] multipathd: uxlsnr: remove check_timeout() mwilck
@ 2021-09-16  2:21   ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16  2:21 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:41:07PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> This function just prints a warning, anyway. If this warning
> is printed, the client will see a timeout and print a warning, too.
> A later patch will re-introduce this function with real functionality.
> 
The start_time variable should probably get remove from uxsock_listen
here instead of the next patch, but it doesn't actually hurt anything,
so

Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/uxlsnr.c | 21 ---------------------
>  1 file changed, 21 deletions(-)
> 
> diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
> index cbbcb2c..147f81a 100644
> --- a/multipathd/uxlsnr.c
> +++ b/multipathd/uxlsnr.c
> @@ -168,25 +168,6 @@ static void free_polls (void)
>  		FREE(polls);
>  }
>  
> -static void check_timeout(struct timespec start_time, char *inbuf,
> -		   unsigned int timeout)
> -{
> -	struct timespec diff_time, end_time;
> -
> -	if (start_time.tv_sec) {
> -		unsigned long msecs;
> -
> -		get_monotonic_time(&end_time);
> -		timespecsub(&end_time, &start_time, &diff_time);
> -		msecs = diff_time.tv_sec * 1000 +
> -			diff_time.tv_nsec / (1000 * 1000);
> -		if (msecs > timeout)
> -			condlog(2, "cli cmd '%s' timeout reached "
> -				"after %ld.%06lu secs", inbuf,
> -				(long)diff_time.tv_sec, diff_time.tv_nsec / 1000);
> -	}
> -}
> -
>  void uxsock_cleanup(void *arg)
>  {
>  	struct client *client_loop;
> @@ -574,8 +555,6 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
>  					FREE(reply);
>  					reply = NULL;
>  				}
> -				check_timeout(start_time, inbuf,
> -					      uxsock_timeout);
>  				FREE(inbuf);
>  			}
>  		}
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 23/35] multipathd: uxlsnr: move client handling to separate function
  2021-09-10 11:41 ` [dm-devel] [PATCH 23/35] multipathd: uxlsnr: move client handling to separate function mwilck
@ 2021-09-16  2:21   ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16  2:21 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:41:08PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> No functional changes at this point. handle_client() will become
> the state machine for handling client requests.
> 
> Signed-off-by: Martin Wilck <mwilck@suse.com>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> ---
>  multipathd/uxlsnr.c | 67 ++++++++++++++++++++++-----------------------
>  1 file changed, 32 insertions(+), 35 deletions(-)
> 
> diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
> index 147f81a..2fb23c8 100644
> --- a/multipathd/uxlsnr.c
> +++ b/multipathd/uxlsnr.c
> @@ -394,14 +394,42 @@ static int uxsock_trigger(char *str, char **reply, int *len,
>  	return r;
>  }
>  
> +static void handle_client(struct client *c, void *trigger_data)
> +{
> +	int rlen;
> +	char *inbuf, *reply;
> +
> +	if (recv_packet_from_client(c->fd, &inbuf, uxsock_timeout) != 0) {
> +		dead_client(c);
> +		return;
> +	}
> +
> +	if (!inbuf) {
> +		condlog(4, "recv_packet_from_client get null request");
> +		return;
> +	}
> +
> +	condlog(4, "cli[%d]: Got request [%s]", c->fd, inbuf);
> +	uxsock_trigger(inbuf, &reply, &rlen,
> +		       _socket_client_is_root(c->fd),
> +		       trigger_data);
> +
> +	if (reply) {
> +		if (send_packet(c->fd, reply) != 0)
> +			dead_client(c);
> +		else
> +			condlog(4, "cli[%d]: Reply [%d bytes]", c->fd, rlen);
> +		FREE(reply);
> +		reply = NULL;
> +	}
> +	FREE(inbuf);
> +}
> +
>  /*
>   * entry point
>   */
>  void *uxsock_listen(long ux_sock, void *trigger_data)
>  {
> -	int rlen;
> -	char *inbuf;
> -	char *reply;
>  	sigset_t mask;
>  	int max_pfds = MIN_POLLS + POLLFDS_BASE;
>  	/* conf->sequence_nr will be 1 when uxsock_listen is first called */
> @@ -504,8 +532,6 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
>  		/* see if a client wants to speak to us */
>  		for (i = POLLFDS_BASE; i < n_pfds; i++) {
>  			if (polls[i].revents & (POLLIN|POLLHUP|POLLERR)) {
> -				struct timespec start_time;
> -
>  				c = NULL;
>  				pthread_mutex_lock(&client_lock);
>  				list_for_each_entry(tmp, &clients, node) {
> @@ -526,36 +552,7 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
>  					dead_client(c);
>  					continue;
>  				}
> -				get_monotonic_time(&start_time);
> -				if (recv_packet_from_client(c->fd, &inbuf,
> -							    uxsock_timeout)
> -				    != 0) {
> -					dead_client(c);
> -					continue;
> -				}
> -				if (!inbuf) {
> -					condlog(4, "recv_packet_from_client "
> -						"get null request");
> -					continue;
> -				}
> -				condlog(4, "cli[%d]: Got request [%s]",
> -					polls[i].fd, inbuf);
> -				uxsock_trigger(inbuf, &reply, &rlen,
> -					       _socket_client_is_root(c->fd),
> -					       trigger_data);
> -				if (reply) {
> -					if (send_packet(c->fd,
> -							reply) != 0) {
> -						dead_client(c);
> -					} else {
> -						condlog(4, "cli[%d]: "
> -							"Reply [%d bytes]",
> -							polls[i].fd, rlen);
> -					}
> -					FREE(reply);
> -					reply = NULL;
> -				}
> -				FREE(inbuf);
> +				handle_client(c, trigger_data);
>  			}
>  		}
>  		/* see if we got a non-fatal signal */
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 24/35] multipathd: uxlsnr: use main poll loop for receiving
  2021-09-10 11:41 ` [dm-devel] [PATCH 24/35] multipathd: uxlsnr: use main poll loop for receiving mwilck
@ 2021-09-16  2:22   ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16  2:22 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:41:09PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> As a first step towards our state machine, avoid the call to
> read_all() via recv_packet_from_client(). handle_client() is now
> invoked twice for the same connection. The first time it reads
> the command length, and later on it reads the command itself
> piece-wise, as sent by the client. This will be just a single
> read in most cases, but not always.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/uxlsnr.c | 90 +++++++++++++++++++++++++++++++++++++--------
>  1 file changed, 75 insertions(+), 15 deletions(-)
> 
> diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
> index 2fb23c8..eff4f7b 100644
> --- a/multipathd/uxlsnr.c
> +++ b/multipathd/uxlsnr.c
> @@ -292,6 +292,8 @@ static void handle_inotify(int fd, struct watch_descriptors *wds)
>  		condlog(1, "Multipath configuration updated.\nReload multipathd for changes to take effect");
>  }
>  
> +static const struct timespec ts_zero = { .tv_sec = 0, };
> +
>  static int parse_cmd (char *cmd, char **reply, int *len, void *data,
>  		      int timeout)
>  {
> @@ -394,23 +396,78 @@ static int uxsock_trigger(char *str, char **reply, int *len,
>  	return r;
>  }
>  
> +static void set_client_state(struct client *c, int state)
> +{
> +	switch(state)
> +	{
> +	case CLT_RECV:
> +		reset_strbuf(&c->reply);
> +		memset(c->cmd, '\0', sizeof(c->cmd));
> +		c->expires = ts_zero;
> +		/* fallthrough */
> +	case CLT_SEND:
> +		/* reuse these fields for next data transfer */
> +		c->len = c->cmd_len = 0;
> +		break;
> +	default:
> +		break;
> +	}
> +	c->state = state;
> +}
> +
>  static void handle_client(struct client *c, void *trigger_data)
>  {
>  	int rlen;
> -	char *inbuf, *reply;
> +	char *reply;
> +	ssize_t n;
>  
> -	if (recv_packet_from_client(c->fd, &inbuf, uxsock_timeout) != 0) {
> -		dead_client(c);
> -		return;
> +	switch (c->state) {
> +	case CLT_RECV:
> +		if (c->cmd_len == 0) {
> +			/*
> +			 * We got POLLIN; assume that at least the length can
> +			 * be read immediately.
> +			 */
> +			get_monotonic_time(&c->expires);
> +			c->expires.tv_sec += uxsock_timeout / 1000;
> +			c->expires.tv_nsec += (uxsock_timeout % 1000) * 1000000;
> +			normalize_timespec(&c->expires);
> +			n = mpath_recv_reply_len(c->fd, 0);
> +			if (n == -1) {
> +				condlog(1, "%s: cli[%d]: failed to receive reply len",
> +					__func__, c->fd);
> +				c->error = -ECONNRESET;
> +			} else if (n > _MAX_CMD_LEN) {
> +				condlog(1, "%s: cli[%d]: overlong command (%zd bytes)",
> +					__func__, c->fd, n);
> +				c->error = -ECONNRESET;
> +			} else {
> +				c->cmd_len = n;
> +				condlog(4, "%s: cli[%d]: connected", __func__, c->fd);
> +			}
> +			/* poll for data */
> +			return;
> +		} else if (c->len < c->cmd_len) {
> +			n = recv(c->fd, c->cmd + c->len, c->cmd_len - c->len, 0);
> +			if (n <= 0 && errno != EINTR && errno != EAGAIN) {
> +				condlog(1, "%s: cli[%d]: error in recv: %m",
> +					__func__, c->fd);
> +				c->error = -ECONNRESET;
> +				return;
> +			}
> +			c->len += n;
> +			if (c->len < c->cmd_len)
> +				/* continue polling */
> +				return;
> +			set_client_state(c, CLT_PARSE);
> +		}
> +		break;
> +	default:
> +		break;
>  	}
>  
> -	if (!inbuf) {
> -		condlog(4, "recv_packet_from_client get null request");
> -		return;
> -	}
> -
> -	condlog(4, "cli[%d]: Got request [%s]", c->fd, inbuf);
> -	uxsock_trigger(inbuf, &reply, &rlen,
> +	condlog(4, "cli[%d]: Got request [%s]", c->fd, c->cmd);
> +	uxsock_trigger(c->cmd, &reply, &rlen,
>  		       _socket_client_is_root(c->fd),
>  		       trigger_data);
>  
> @@ -418,11 +475,12 @@ static void handle_client(struct client *c, void *trigger_data)
>  		if (send_packet(c->fd, reply) != 0)
>  			dead_client(c);
>  		else
> -			condlog(4, "cli[%d]: Reply [%d bytes]", c->fd, rlen);
> -		FREE(reply);
> -		reply = NULL;
> +			condlog(4, "cli[%d]: Reply [%zu bytes]", c->fd,
> +				get_strbuf_len(&c->reply) + 1);
> +		reset_strbuf(&c->reply);
>  	}
> -	FREE(inbuf);
> +
> +	set_client_state(c, CLT_RECV);
>  }
>  
>  /*
> @@ -553,6 +611,8 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
>  					continue;
>  				}
>  				handle_client(c, trigger_data);
> +				if (c->error == -ECONNRESET)
> +					dead_client(c);
>  			}
>  		}
>  		/* see if we got a non-fatal signal */
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 25/35] multipathd: use strbuf in cli_handler functions
  2021-09-10 11:41 ` [dm-devel] [PATCH 25/35] multipathd: use strbuf in cli_handler functions mwilck
@ 2021-09-16  2:23   ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16  2:23 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:41:10PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> This allows us to simplify callers by not having to track the
> reply length separately.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/cli.c          |   7 +-
>  multipathd/cli.h          |   6 +-
>  multipathd/cli_handlers.c | 316 +++++++++++++++-----------------------
>  multipathd/uxlsnr.c       |  49 +++---
>  4 files changed, 152 insertions(+), 226 deletions(-)
> 
> diff --git a/multipathd/cli.c b/multipathd/cli.c
> index 29d6a6e..414f660 100644
> --- a/multipathd/cli.c
> +++ b/multipathd/cli.c
> @@ -419,13 +419,10 @@ do_genhelp(struct strbuf *reply, const char *cmd, int error) {
>  }
>  
>  
> -char *genhelp_handler(const char *cmd, int error)
> +void genhelp_handler(const char *cmd, int error, struct strbuf *reply)
>  {
> -	STRBUF_ON_STACK(reply);
> -
> -	if (do_genhelp(&reply, cmd, error) == -1)
> +	if (do_genhelp(reply, cmd, error) == -1)
>  		condlog(0, "genhelp_handler: out of memory");
> -	return steal_strbuf_str(&reply);
>  }
>  
>  char *
> diff --git a/multipathd/cli.h b/multipathd/cli.h
> index eed606a..fcb6af0 100644
> --- a/multipathd/cli.h
> +++ b/multipathd/cli.h
> @@ -124,7 +124,9 @@ struct key {
>  	int has_param;
>  };
>  
> -typedef int (cli_handler)(void *keywords, char **reply, int *len, void *data);
> +struct strbuf;
> +
> +typedef int (cli_handler)(void *keywords, struct strbuf *reply, void *data);
>  
>  struct handler {
>  	uint64_t fingerprint;
> @@ -140,7 +142,7 @@ int __set_handler_callback (uint64_t fp, cli_handler *fn, bool locked);
>  
>  int get_cmdvec (char *cmd, vector *v);
>  struct handler *find_handler_for_cmdvec(const struct _vector *v);
> -char *genhelp_handler (const char *cmd, int error);
> +void genhelp_handler (const char *cmd, int error, struct strbuf *reply);
>  
>  int load_keys (void);
>  char * get_keyparam (vector v, uint64_t code);
> diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
> index 1a9c822..f59db3a 100644
> --- a/multipathd/cli_handlers.c
> +++ b/multipathd/cli_handlers.c
> @@ -33,17 +33,9 @@
>  #include "strbuf.h"
>  #include "cli_handlers.h"
>  
> -#define SET_REPLY_AND_LEN(__rep, __len, string_literal)			\
> -	do {								\
> -		*(__rep) = strdup(string_literal);			\
> -		*(__len) = *(__rep) ? sizeof(string_literal) : 0;	\
> -	} while (0)
> -
>  static int
> -show_paths (char ** r, int * len, struct vectors * vecs, char * style,
> -	    int pretty)
> +show_paths (struct strbuf *reply, struct vectors *vecs, char *style, int pretty)
>  {
> -	STRBUF_ON_STACK(reply);
>  	int i;
>  	struct path * pp;
>  	int hdr_len = 0;
> @@ -51,61 +43,49 @@ show_paths (char ** r, int * len, struct vectors * vecs, char * style,
>  	get_path_layout(vecs->pathvec, 1);
>  	foreign_path_layout();
>  
> -	if (pretty && (hdr_len = snprint_path_header(&reply, style)) < 0)
> +	if (pretty && (hdr_len = snprint_path_header(reply, style)) < 0)
>  		return 1;
>  
>  	vector_foreach_slot(vecs->pathvec, pp, i) {
> -		if (snprint_path(&reply, style, pp, pretty) < 0)
> +		if (snprint_path(reply, style, pp, pretty) < 0)
>  			return 1;
>  	}
> -	if (snprint_foreign_paths(&reply, style, pretty) < 0)
> +	if (snprint_foreign_paths(reply, style, pretty) < 0)
>  		return 1;
>  
> -	if (pretty && get_strbuf_len(&reply) == (size_t)hdr_len)
> +	if (pretty && get_strbuf_len(reply) == (size_t)hdr_len)
>  		/* No output - clear header */
> -		truncate_strbuf(&reply, 0);
> +		truncate_strbuf(reply, 0);
>  
> -	*len = (int)get_strbuf_len(&reply) + 1;
> -	*r = steal_strbuf_str(&reply);
>  	return 0;
>  }
>  
>  static int
> -show_path (char ** r, int * len, struct vectors * vecs, struct path *pp,
> -	   char * style)
> +show_path (struct strbuf *reply, struct vectors *vecs, struct path *pp,
> +	   char *style)
>  {
> -	STRBUF_ON_STACK(reply);
> -
>  	get_path_layout(vecs->pathvec, 1);
> -	if (snprint_path(&reply, style, pp, 0) < 0)
> +	if (snprint_path(reply, style, pp, 0) < 0)
>  		return 1;
> -	*len = (int)get_strbuf_len(&reply) + 1;
> -	*r = steal_strbuf_str(&reply);
> -
>  	return 0;
>  }
>  
>  static int
> -show_map_topology (char ** r, int * len, struct multipath * mpp,
> -		   struct vectors * vecs)
> +show_map_topology (struct strbuf *reply, struct multipath *mpp,
> +		   struct vectors *vecs)
>  {
> -	STRBUF_ON_STACK(reply);
> -
>  	if (update_multipath(vecs, mpp->alias, 0))
>  		return 1;
>  
> -	if (snprint_multipath_topology(&reply, mpp, 2) < 0)
> +	if (snprint_multipath_topology(reply, mpp, 2) < 0)
>  		return 1;
> -	*len = (int)get_strbuf_len(&reply) + 1;
> -	*r = steal_strbuf_str(&reply);
>  
>  	return 0;
>  }
>  
>  static int
> -show_maps_topology (char ** r, int * len, struct vectors * vecs)
> +show_maps_topology (struct strbuf *reply, struct vectors * vecs)
>  {
> -	STRBUF_ON_STACK(reply);
>  	int i;
>  	struct multipath * mpp;
>  
> @@ -117,21 +97,18 @@ show_maps_topology (char ** r, int * len, struct vectors * vecs)
>  			i--;
>  			continue;
>  		}
> -		if (snprint_multipath_topology(&reply, mpp, 2) < 0)
> +		if (snprint_multipath_topology(reply, mpp, 2) < 0)
>  			return 1;
>  	}
> -	if (snprint_foreign_topology(&reply, 2) < 0)
> +	if (snprint_foreign_topology(reply, 2) < 0)
>  		return 1;
>  
> -	*len = (int)get_strbuf_len(&reply) + 1;
> -	*r = steal_strbuf_str(&reply);
>  	return 0;
>  }
>  
>  static int
> -show_maps_json (char ** r, int * len, struct vectors * vecs)
> +show_maps_json (struct strbuf *reply, struct vectors * vecs)
>  {
> -	STRBUF_ON_STACK(reply);
>  	int i;
>  	struct multipath * mpp;
>  
> @@ -141,45 +118,38 @@ show_maps_json (char ** r, int * len, struct vectors * vecs)
>  		}
>  	}
>  
> -	if (snprint_multipath_topology_json(&reply, vecs) < 0)
> +	if (snprint_multipath_topology_json(reply, vecs) < 0)
>  		return 1;
>  
> -	*len = (int)get_strbuf_len(&reply) + 1;
> -	*r = steal_strbuf_str(&reply);
>  	return 0;
>  }
>  
>  static int
> -show_map_json (char ** r, int * len, struct multipath * mpp,
> -		   struct vectors * vecs)
> +show_map_json (struct strbuf *reply, struct multipath * mpp,
> +	       struct vectors * vecs)
>  {
> -	STRBUF_ON_STACK(reply);
> -
>  	if (update_multipath(vecs, mpp->alias, 0))
>  		return 1;
>  
> -	if (snprint_multipath_map_json(&reply, mpp) < 0)
> +	if (snprint_multipath_map_json(reply, mpp) < 0)
>  		return 1;
>  
> -	*len = (int)get_strbuf_len(&reply) + 1;
> -	*r = steal_strbuf_str(&reply);
>  	return 0;
>  }
>  
>  static int
> -show_config (char ** r, int * len, const struct _vector *hwtable,
> +show_config (struct strbuf *reply, const struct _vector *hwtable,
>  	     const struct _vector *mpvec)
>  {
>  	struct config *conf;
> -	char *reply;
> +	int rc;
>  
>  	conf = get_multipath_config();
>  	pthread_cleanup_push(put_multipath_config, conf);
> -	reply = snprint_config(conf, len, hwtable, mpvec);
> +	rc = __snprint_config(conf, reply, hwtable, mpvec);
>  	pthread_cleanup_pop(1);
> -	if (reply == NULL)
> +	if (rc < 0)
>  		return 1;
> -	*r = reply;
>  	return 0;
>  }
>  
> @@ -195,11 +165,11 @@ reset_stats(struct multipath * mpp)
>  }
>  
>  static int
> -cli_list_config (void * v, char ** reply, int * len, void * data)
> +cli_list_config (void *v, struct strbuf *reply, void *data)
>  {
>  	condlog(3, "list config (operator)");
>  
> -	return show_config(reply, len, NULL, NULL);
> +	return show_config(reply, NULL, NULL);
>  }
>  
>  static void v_free(void *x)
> @@ -208,9 +178,9 @@ static void v_free(void *x)
>  }
>  
>  static int
> -cli_list_config_local (void * v, char ** reply, int * len, void * data)
> +cli_list_config_local (void *v, struct strbuf *reply, void *data)
>  {
> -	struct vectors * vecs = (struct vectors *)data;
> +	struct vectors *vecs = (struct vectors *)data;
>  	vector hwes;
>  	int ret;
>  
> @@ -218,45 +188,45 @@ cli_list_config_local (void * v, char ** reply, int * len, void * data)
>  
>  	hwes = get_used_hwes(vecs->pathvec);
>  	pthread_cleanup_push(v_free, hwes);
> -	ret = show_config(reply, len, hwes, vecs->mpvec);
> +	ret = show_config(reply, hwes, vecs->mpvec);
>  	pthread_cleanup_pop(1);
>  	return ret;
>  }
>  
>  static int
> -cli_list_paths (void * v, char ** reply, int * len, void * data)
> +cli_list_paths (void *v, struct strbuf *reply, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  
>  	condlog(3, "list paths (operator)");
>  
> -	return show_paths(reply, len, vecs, PRINT_PATH_CHECKER, 1);
> +	return show_paths(reply, vecs, PRINT_PATH_CHECKER, 1);
>  }
>  
>  static int
> -cli_list_paths_fmt (void * v, char ** reply, int * len, void * data)
> +cli_list_paths_fmt (void *v, struct strbuf *reply, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	char * fmt = get_keyparam(v, FMT);
>  
>  	condlog(3, "list paths (operator)");
>  
> -	return show_paths(reply, len, vecs, fmt, 1);
> +	return show_paths(reply, vecs, fmt, 1);
>  }
>  
>  static int
> -cli_list_paths_raw (void * v, char ** reply, int * len, void * data)
> +cli_list_paths_raw (void *v, struct strbuf *reply, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	char * fmt = get_keyparam(v, FMT);
>  
>  	condlog(3, "list paths (operator)");
>  
> -	return show_paths(reply, len, vecs, fmt, 0);
> +	return show_paths(reply, vecs, fmt, 0);
>  }
>  
>  static int
> -cli_list_path (void * v, char ** reply, int * len, void * data)
> +cli_list_path (void *v, struct strbuf *reply, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	char * param = get_keyparam(v, PATH);
> @@ -269,11 +239,11 @@ cli_list_path (void * v, char ** reply, int * len, void * data)
>  	if (!pp)
>  		return 1;
>  
> -	return show_path(reply, len, vecs, pp, "%o");
> +	return show_path(reply, vecs, pp, "%o");
>  }
>  
>  static int
> -cli_list_map_topology (void * v, char ** reply, int * len, void * data)
> +cli_list_map_topology (void *v, struct strbuf *reply, void *data)
>  {
>  	struct multipath * mpp;
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -288,21 +258,21 @@ cli_list_map_topology (void * v, char ** reply, int * len, void * data)
>  
>  	condlog(3, "list multipath %s (operator)", param);
>  
> -	return show_map_topology(reply, len, mpp, vecs);
> +	return show_map_topology(reply, mpp, vecs);
>  }
>  
>  static int
> -cli_list_maps_topology (void * v, char ** reply, int * len, void * data)
> +cli_list_maps_topology (void *v, struct strbuf *reply, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  
>  	condlog(3, "list multipaths (operator)");
>  
> -	return show_maps_topology(reply, len, vecs);
> +	return show_maps_topology(reply, vecs);
>  }
>  
>  static int
> -cli_list_map_json (void * v, char ** reply, int * len, void * data)
> +cli_list_map_json (void *v, struct strbuf *reply, void *data)
>  {
>  	struct multipath * mpp;
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -317,78 +287,61 @@ cli_list_map_json (void * v, char ** reply, int * len, void * data)
>  
>  	condlog(3, "list multipath json %s (operator)", param);
>  
> -	return show_map_json(reply, len, mpp, vecs);
> +	return show_map_json(reply, mpp, vecs);
>  }
>  
>  static int
> -cli_list_maps_json (void * v, char ** reply, int * len, void * data)
> +cli_list_maps_json (void *v, struct strbuf *reply, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  
>  	condlog(3, "list multipaths json (operator)");
>  
> -	return show_maps_json(reply, len, vecs);
> +	return show_maps_json(reply, vecs);
>  }
>  
>  static int
> -cli_list_wildcards (void * v, char ** reply, int * len, void * data)
> +cli_list_wildcards (void *v, struct strbuf *reply, void *data)
>  {
> -	STRBUF_ON_STACK(buf);
> -
> -	if (snprint_wildcards(&buf) < 0)
> +	if (snprint_wildcards(reply) < 0)
>  		return 1;
>  
> -	*len = get_strbuf_len(&buf) + 1;
> -	*reply = steal_strbuf_str(&buf);
>  	return 0;
>  }
>  
>  static int
> -show_status (char ** r, int *len, struct vectors * vecs)
> +show_status (struct strbuf *reply, struct vectors *vecs)
>  {
> -	STRBUF_ON_STACK(reply);
> -
> -	if (snprint_status(&reply, vecs) < 0)
> +	if (snprint_status(reply, vecs) < 0)
>  		return 1;
>  
> -	*len = get_strbuf_len(&reply) + 1;
> -	*r = steal_strbuf_str(&reply);
>  	return 0;
>  }
>  
>  static int
> -show_daemon (char ** r, int *len)
> +show_daemon (struct strbuf *reply)
>  {
> -	STRBUF_ON_STACK(reply);
> -
> -	if (print_strbuf(&reply, "pid %d %s\n",
> +	if (print_strbuf(reply, "pid %d %s\n",
>  			 daemon_pid, daemon_status()) < 0)
>  		return 1;
>  
> -	*len = get_strbuf_len(&reply) + 1;
> -	*r = steal_strbuf_str(&reply);
>  	return 0;
>  }
>  
>  static int
> -show_map (char ** r, int *len, struct multipath * mpp, char * style,
> +show_map (struct strbuf *reply, struct multipath *mpp, char *style,
>  	  int pretty)
>  {
> -	STRBUF_ON_STACK(reply);
> -
> -	if (snprint_multipath(&reply, style, mpp, pretty) < 0)
> +	if (snprint_multipath(reply, style, mpp, pretty) < 0)
>  		return 1;
>  
> -	*len = get_strbuf_len(&reply) + 1;
> -	*r = steal_strbuf_str(&reply);
>  	return 0;
>  }
>  
>  static int
> -show_maps (char ** r, int *len, struct vectors * vecs, char * style,
> +show_maps (struct strbuf *reply, struct vectors *vecs, char *style,
>  	   int pretty)
>  {
> -	STRBUF_ON_STACK(reply);
>  	int i;
>  	struct multipath * mpp;
>  	int hdr_len = 0;
> @@ -396,7 +349,7 @@ show_maps (char ** r, int *len, struct vectors * vecs, char * style,
>  	get_multipath_layout(vecs->mpvec, 1);
>  	foreign_multipath_layout();
>  
> -	if (pretty && (hdr_len = snprint_multipath_header(&reply, style)) < 0)
> +	if (pretty && (hdr_len = snprint_multipath_header(reply, style)) < 0)
>  		return 1;
>  
>  	vector_foreach_slot(vecs->mpvec, mpp, i) {
> @@ -404,45 +357,43 @@ show_maps (char ** r, int *len, struct vectors * vecs, char * style,
>  			i--;
>  			continue;
>  		}
> -		if (snprint_multipath(&reply, style, mpp, pretty) < 0)
> +		if (snprint_multipath(reply, style, mpp, pretty) < 0)
>  			return 1;
>  	}
> -	if (snprint_foreign_multipaths(&reply, style, pretty) < 0)
> +	if (snprint_foreign_multipaths(reply, style, pretty) < 0)
>  		return 1;
>  
> -	if (pretty && get_strbuf_len(&reply) == (size_t)hdr_len)
> +	if (pretty && get_strbuf_len(reply) == (size_t)hdr_len)
>  		/* No output - clear header */
> -		truncate_strbuf(&reply, 0);
> +		truncate_strbuf(reply, 0);
>  
> -	*len = (int)get_strbuf_len(&reply) + 1;
> -	*r = steal_strbuf_str(&reply);
>  	return 0;
>  }
>  
>  static int
> -cli_list_maps_fmt (void * v, char ** reply, int * len, void * data)
> +cli_list_maps_fmt (void *v, struct strbuf *reply, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	char * fmt = get_keyparam(v, FMT);
>  
>  	condlog(3, "list maps (operator)");
>  
> -	return show_maps(reply, len, vecs, fmt, 1);
> +	return show_maps(reply, vecs, fmt, 1);
>  }
>  
>  static int
> -cli_list_maps_raw (void * v, char ** reply, int * len, void * data)
> +cli_list_maps_raw (void *v, struct strbuf *reply, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	char * fmt = get_keyparam(v, FMT);
>  
>  	condlog(3, "list maps (operator)");
>  
> -	return show_maps(reply, len, vecs, fmt, 0);
> +	return show_maps(reply, vecs, fmt, 0);
>  }
>  
>  static int
> -cli_list_map_fmt (void * v, char ** reply, int * len, void * data)
> +cli_list_map_fmt (void *v, struct strbuf *reply, void *data)
>  {
>  	struct multipath * mpp;
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -458,59 +409,59 @@ cli_list_map_fmt (void * v, char ** reply, int * len, void * data)
>  
>  	condlog(3, "list map %s fmt %s (operator)", param, fmt);
>  
> -	return show_map(reply, len, mpp, fmt, 1);
> +	return show_map(reply, mpp, fmt, 1);
>  }
>  
>  static int
> -cli_list_maps (void * v, char ** reply, int * len, void * data)
> +cli_list_maps (void *v, struct strbuf *reply, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  
>  	condlog(3, "list maps (operator)");
>  
> -	return show_maps(reply, len, vecs, PRINT_MAP_NAMES, 1);
> +	return show_maps(reply, vecs, PRINT_MAP_NAMES, 1);
>  }
>  
>  static int
> -cli_list_status (void * v, char ** reply, int * len, void * data)
> +cli_list_status (void *v, struct strbuf *reply, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  
>  	condlog(3, "list status (operator)");
>  
> -	return show_status(reply, len, vecs);
> +	return show_status(reply, vecs);
>  }
>  
>  static int
> -cli_list_maps_status (void * v, char ** reply, int * len, void * data)
> +cli_list_maps_status (void *v, struct strbuf *reply, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  
>  	condlog(3, "list maps status (operator)");
>  
> -	return show_maps(reply, len, vecs, PRINT_MAP_STATUS, 1);
> +	return show_maps(reply, vecs, PRINT_MAP_STATUS, 1);
>  }
>  
>  static int
> -cli_list_maps_stats (void * v, char ** reply, int * len, void * data)
> +cli_list_maps_stats (void *v, struct strbuf *reply, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  
>  	condlog(3, "list maps stats (operator)");
>  
> -	return show_maps(reply, len, vecs, PRINT_MAP_STATS, 1);
> +	return show_maps(reply, vecs, PRINT_MAP_STATS, 1);
>  }
>  
>  static int
> -cli_list_daemon (void * v, char ** reply, int * len, void * data)
> +cli_list_daemon (void *v, struct strbuf *reply, void *data)
>  {
>  	condlog(3, "list daemon (operator)");
>  
> -	return show_daemon(reply, len);
> +	return show_daemon(reply);
>  }
>  
>  static int
> -cli_reset_maps_stats (void * v, char ** reply, int * len, void * data)
> +cli_reset_maps_stats (void *v, struct strbuf *reply, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	int i;
> @@ -525,7 +476,7 @@ cli_reset_maps_stats (void * v, char ** reply, int * len, void * data)
>  }
>  
>  static int
> -cli_reset_map_stats (void * v, char ** reply, int * len, void * data)
> +cli_reset_map_stats (void *v, struct strbuf *reply, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	struct multipath * mpp;
> @@ -543,7 +494,7 @@ cli_reset_map_stats (void * v, char ** reply, int * len, void * data)
>  }
>  
>  static int
> -cli_add_path (void * v, char ** reply, int * len, void * data)
> +cli_add_path (void *v, struct strbuf *reply, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	char * param = get_keyparam(v, PATH);
> @@ -653,13 +604,13 @@ cli_add_path (void * v, char ** reply, int * len, void * data)
>  	}
>  	return ev_add_path(pp, vecs, 1);
>  blacklisted:
> -	SET_REPLY_AND_LEN(reply, len, "blacklisted\n");
> +	append_strbuf_str(reply, "blacklisted\n");
>  	condlog(2, "%s: path blacklisted", param);
>  	return 0;
>  }
>  
>  static int
> -cli_del_path (void * v, char ** reply, int * len, void * data)
> +cli_del_path (void * v, struct strbuf *reply, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	char * param = get_keyparam(v, PATH);
> @@ -675,14 +626,14 @@ cli_del_path (void * v, char ** reply, int * len, void * data)
>  	}
>  	ret = ev_remove_path(pp, vecs, 1);
>  	if (ret == REMOVE_PATH_DELAY)
> -		SET_REPLY_AND_LEN(reply, len, "delayed\n");
> +		append_strbuf_str(reply, "delayed\n");
>  	else if (ret == REMOVE_PATH_MAP_ERROR)
> -		SET_REPLY_AND_LEN(reply, len, "map reload error. removed\n");
> +		append_strbuf_str(reply, "map reload error. removed\n");
>  	return (ret == REMOVE_PATH_FAILURE);
>  }
>  
>  static int
> -cli_add_map (void * v, char ** reply, int * len, void * data)
> +cli_add_map (void * v, struct strbuf *reply, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	char * param = get_keyparam(v, MAP);
> @@ -702,7 +653,7 @@ cli_add_map (void * v, char ** reply, int * len, void * data)
>  		invalid = 1;
>  	pthread_cleanup_pop(1);
>  	if (invalid) {
> -		SET_REPLY_AND_LEN(reply, len, "blacklisted\n");
> +		append_strbuf_str(reply, "blacklisted\n");
>  		condlog(2, "%s: map blacklisted", param);
>  		return 1;
>  	}
> @@ -742,7 +693,7 @@ cli_add_map (void * v, char ** reply, int * len, void * data)
>  }
>  
>  static int
> -cli_del_map (void * v, char ** reply, int * len, void * data)
> +cli_del_map (void * v, struct strbuf *reply, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	char * param = get_keyparam(v, MAP);
> @@ -764,14 +715,14 @@ cli_del_map (void * v, char ** reply, int * len, void * data)
>  	}
>  	rc = ev_remove_map(param, alias, minor, vecs);
>  	if (rc == 2)
> -		*reply = strdup("delayed");
> +		append_strbuf_str(reply, "delayed");
>  
>  	FREE(alias);
>  	return rc;
>  }
>  
>  static int
> -cli_del_maps (void *v, char **reply, int *len, void *data)
> +cli_del_maps (void *v, struct strbuf *reply, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	struct multipath *mpp;
> @@ -790,7 +741,7 @@ cli_del_maps (void *v, char **reply, int *len, void *data)
>  }
>  
>  static int
> -cli_reload(void *v, char **reply, int *len, void *data)
> +cli_reload(void *v, struct strbuf *reply, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	char * mapname = get_keyparam(v, MAP);
> @@ -843,7 +794,7 @@ static int resize_map(struct multipath *mpp, unsigned long long size,
>  }
>  
>  static int
> -cli_resize(void *v, char **reply, int *len, void *data)
> +cli_resize(void *v, struct strbuf *reply, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	char * mapname = get_keyparam(v, MAP);
> @@ -908,7 +859,7 @@ cli_resize(void *v, char **reply, int *len, void *data)
>  }
>  
>  static int
> -cli_force_no_daemon_q(void * v, char ** reply, int * len, void * data)
> +cli_force_no_daemon_q(void * v, struct strbuf *reply, void * data)
>  {
>  	struct config *conf;
>  
> @@ -921,7 +872,7 @@ cli_force_no_daemon_q(void * v, char ** reply, int * len, void * data)
>  }
>  
>  static int
> -cli_restore_no_daemon_q(void * v, char ** reply, int * len, void * data)
> +cli_restore_no_daemon_q(void * v, struct strbuf *reply, void * data)
>  {
>  	struct config *conf;
>  
> @@ -934,7 +885,7 @@ cli_restore_no_daemon_q(void * v, char ** reply, int * len, void * data)
>  }
>  
>  static int
> -cli_restore_queueing(void *v, char **reply, int *len, void *data)
> +cli_restore_queueing(void *v, struct strbuf *reply, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	char * mapname = get_keyparam(v, MAP);
> @@ -975,7 +926,7 @@ cli_restore_queueing(void *v, char **reply, int *len, void *data)
>  }
>  
>  static int
> -cli_restore_all_queueing(void *v, char **reply, int *len, void *data)
> +cli_restore_all_queueing(void *v, struct strbuf *reply, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	struct multipath *mpp;
> @@ -997,7 +948,7 @@ cli_restore_all_queueing(void *v, char **reply, int *len, void *data)
>  }
>  
>  static int
> -cli_disable_queueing(void *v, char **reply, int *len, void *data)
> +cli_disable_queueing(void *v, struct strbuf *reply, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	char * mapname = get_keyparam(v, MAP);
> @@ -1026,7 +977,7 @@ cli_disable_queueing(void *v, char **reply, int *len, void *data)
>  }
>  
>  static int
> -cli_disable_all_queueing(void *v, char **reply, int *len, void *data)
> +cli_disable_all_queueing(void *v, struct strbuf *reply, void *data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	struct multipath *mpp;
> @@ -1045,7 +996,7 @@ cli_disable_all_queueing(void *v, char **reply, int *len, void *data)
>  }
>  
>  static int
> -cli_switch_group(void * v, char ** reply, int * len, void * data)
> +cli_switch_group(void * v, struct strbuf *reply, void * data)
>  {
>  	char * mapname = get_keyparam(v, MAP);
>  	int groupnum = atoi(get_keyparam(v, GROUP));
> @@ -1057,7 +1008,7 @@ cli_switch_group(void * v, char ** reply, int * len, void * data)
>  }
>  
>  static int
> -cli_reconfigure(void * v, char ** reply, int * len, void * data)
> +cli_reconfigure(void * v, struct strbuf *reply, void * data)
>  {
>  	condlog(2, "reconfigure (operator)");
>  
> @@ -1066,7 +1017,7 @@ cli_reconfigure(void * v, char ** reply, int * len, void * data)
>  }
>  
>  static int
> -cli_suspend(void * v, char ** reply, int * len, void * data)
> +cli_suspend(void * v, struct strbuf *reply, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	char * param = get_keyparam(v, MAP);
> @@ -1096,7 +1047,7 @@ cli_suspend(void * v, char ** reply, int * len, void * data)
>  }
>  
>  static int
> -cli_resume(void * v, char ** reply, int * len, void * data)
> +cli_resume(void * v, struct strbuf *reply, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	char * param = get_keyparam(v, MAP);
> @@ -1128,7 +1079,7 @@ cli_resume(void * v, char ** reply, int * len, void * data)
>  }
>  
>  static int
> -cli_reinstate(void * v, char ** reply, int * len, void * data)
> +cli_reinstate(void * v, struct strbuf *reply, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	char * param = get_keyparam(v, PATH);
> @@ -1151,7 +1102,7 @@ cli_reinstate(void * v, char ** reply, int * len, void * data)
>  }
>  
>  static int
> -cli_reassign (void * v, char ** reply, int * len, void * data)
> +cli_reassign (void * v, struct strbuf *reply, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	char * param = get_keyparam(v, MAP);
> @@ -1175,7 +1126,7 @@ cli_reassign (void * v, char ** reply, int * len, void * data)
>  }
>  
>  static int
> -cli_fail(void * v, char ** reply, int * len, void * data)
> +cli_fail(void * v, struct strbuf *reply, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	char * param = get_keyparam(v, PATH);
> @@ -1204,72 +1155,65 @@ cli_fail(void * v, char ** reply, int * len, void * data)
>  }
>  
>  static int
> -show_blacklist (char ** r, int * len)
> +show_blacklist (struct strbuf *reply)
>  {
> -	STRBUF_ON_STACK(reply);
>  	struct config *conf;
>  	bool fail;
>  
>  	conf = get_multipath_config();
>  	pthread_cleanup_push(put_multipath_config, conf);
> -	fail = snprint_blacklist_report(conf, &reply) < 0;
> +	fail = snprint_blacklist_report(conf, reply) < 0;
>  	pthread_cleanup_pop(1);
>  
>  	if (fail)
>  		return 1;
>  
> -	*len = (int)get_strbuf_len(&reply) + 1;
> -	*r = steal_strbuf_str(&reply);
>  	return 0;
>  }
>  
>  static int
> -cli_list_blacklist (void * v, char ** reply, int * len, void * data)
> +cli_list_blacklist (void * v, struct strbuf *reply, void * data)
>  {
>  	condlog(3, "list blacklist (operator)");
>  
> -	return show_blacklist(reply, len);
> +	return show_blacklist(reply);
>  }
>  
>  static int
> -show_devices (char ** r, int * len, struct vectors *vecs)
> +show_devices (struct strbuf *reply, struct vectors *vecs)
>  {
> -	STRBUF_ON_STACK(reply);
>  	struct config *conf;
>  	bool fail;
>  
>  	conf = get_multipath_config();
>  	pthread_cleanup_push(put_multipath_config, conf);
> -	fail = snprint_devices(conf, &reply, vecs) < 0;
> +	fail = snprint_devices(conf, reply, vecs) < 0;
>  	pthread_cleanup_pop(1);
>  
>  	if (fail)
>  		return 1;
>  
> -	*len = (int)get_strbuf_len(&reply) + 1;
> -	*r = steal_strbuf_str(&reply);
> -
>  	return 0;
>  }
>  
>  static int
> -cli_list_devices (void * v, char ** reply, int * len, void * data)
> +cli_list_devices (void * v, struct strbuf *reply, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  
>  	condlog(3, "list devices (operator)");
>  
> -	return show_devices(reply, len, vecs);
> +	return show_devices(reply, vecs);
>  }
>  
>  static int
> -cli_quit (void * v, char ** reply, int * len, void * data)
> +cli_quit (void * v, struct strbuf *reply, void * data)
>  {
>  	return 0;
>  }
>  
>  static int
> -cli_shutdown (void * v, char ** reply, int * len, void * data)
> +cli_shutdown (void * v, struct strbuf *reply, void * data)
>  {
>  	condlog(3, "shutdown (operator)");
>  	exit_daemon();
> @@ -1277,7 +1221,7 @@ cli_shutdown (void * v, char ** reply, int * len, void * data)
>  }
>  
>  static int
> -cli_getprstatus (void * v, char ** reply, int * len, void * data)
> +cli_getprstatus (void * v, struct strbuf *reply, void * data)
>  {
>  	struct multipath * mpp;
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -1292,17 +1236,16 @@ cli_getprstatus (void * v, char ** reply, int * len, void * data)
>  
>  	condlog(3, "%s: prflag = %u", param, (unsigned int)mpp->prflag);
>  
> -	*len = asprintf(reply, "%d", mpp->prflag);
> -	if (*len < 0)
> +	if (print_strbuf(reply, "%d", mpp->prflag) < 0)
>  		return 1;
>  
> -	condlog(3, "%s: reply = %s", param, *reply);
> +	condlog(3, "%s: reply = %s", param, get_strbuf_str(reply));
>  
>  	return 0;
>  }
>  
>  static int
> -cli_setprstatus(void * v, char ** reply, int * len, void * data)
> +cli_setprstatus(void * v, struct strbuf *reply, void * data)
>  {
>  	struct multipath * mpp;
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -1325,7 +1268,7 @@ cli_setprstatus(void * v, char ** reply, int * len, void * data)
>  }
>  
>  static int
> -cli_unsetprstatus(void * v, char ** reply, int * len, void * data)
> +cli_unsetprstatus(void * v, struct strbuf *reply, void * data)
>  {
>  	struct multipath * mpp;
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -1347,7 +1290,7 @@ cli_unsetprstatus(void * v, char ** reply, int * len, void * data)
>  }
>  
>  static int
> -cli_getprkey(void * v, char ** reply, int * len, void * data)
> +cli_getprkey(void * v, struct strbuf *reply, void * data)
>  {
>  	struct multipath * mpp;
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -1361,25 +1304,20 @@ cli_getprkey(void * v, char ** reply, int * len, void * data)
>  	if (!mpp)
>  		return 1;
>  
> -	*reply = malloc(26);
> -	if (!*reply)
> -		return 1;
> -
>  	key = get_be64(mpp->reservation_key);
>  	if (!key) {
> -		sprintf(*reply, "none\n");
> -		*len = sizeof("none\n");
> +		append_strbuf_str(reply, "none\n");
>  		return 0;
>  	}
>  
> -	/* This snprintf() can't overflow - PRIx64 needs max 16 chars */
> -	*len = snprintf(*reply, 26, "0x%" PRIx64 "%s\n", key,
> -			mpp->sa_flags & MPATH_F_APTPL_MASK ? ":aptpl" : "") + 1;
> +	if (print_strbuf(reply, "0x%" PRIx64 "%s\n", key,
> +			 mpp->sa_flags & MPATH_F_APTPL_MASK ? ":aptpl" : "") < 0)
> +		return 1;
>  	return 0;
>  }
>  
>  static int
> -cli_unsetprkey(void * v, char ** reply, int * len, void * data)
> +cli_unsetprkey(void * v, struct strbuf *reply, void * data)
>  {
>  	struct multipath * mpp;
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -1403,7 +1341,7 @@ cli_unsetprkey(void * v, char ** reply, int * len, void * data)
>  }
>  
>  static int
> -cli_setprkey(void * v, char ** reply, int * len, void * data)
> +cli_setprkey(void * v, struct strbuf *reply, void * data)
>  {
>  	struct multipath * mpp;
>  	struct vectors * vecs = (struct vectors *)data;
> @@ -1434,7 +1372,7 @@ cli_setprkey(void * v, char ** reply, int * len, void * data)
>  	return ret;
>  }
>  
> -static int cli_set_marginal(void * v, char ** reply, int * len, void * data)
> +static int cli_set_marginal(void * v, struct strbuf *reply, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	char * param = get_keyparam(v, PATH);
> @@ -1461,7 +1399,7 @@ static int cli_set_marginal(void * v, char ** reply, int * len, void * data)
>  	return reload_and_sync_map(pp->mpp, vecs, 0);
>  }
>  
> -static int cli_unset_marginal(void * v, char ** reply, int * len, void * data)
> +static int cli_unset_marginal(void * v, struct strbuf *reply, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	char * param = get_keyparam(v, PATH);
> @@ -1488,7 +1426,7 @@ static int cli_unset_marginal(void * v, char ** reply, int * len, void * data)
>  	return reload_and_sync_map(pp->mpp, vecs, 0);
>  }
>  
> -static int cli_unset_all_marginal(void * v, char ** reply, int * len, void * data)
> +static int cli_unset_all_marginal(void * v, struct strbuf *reply, void * data)
>  {
>  	struct vectors * vecs = (struct vectors *)data;
>  	char * mapname = get_keyparam(v, MAP);
> diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
> index eff4f7b..9a945ea 100644
> --- a/multipathd/uxlsnr.c
> +++ b/multipathd/uxlsnr.c
> @@ -294,7 +294,7 @@ static void handle_inotify(int fd, struct watch_descriptors *wds)
>  
>  static const struct timespec ts_zero = { .tv_sec = 0, };
>  
> -static int parse_cmd (char *cmd, char **reply, int *len, void *data,
> +static int parse_cmd (char *cmd, struct strbuf *reply, void *data,
>  		      int timeout)
>  {
>  	int r;
> @@ -305,10 +305,9 @@ static int parse_cmd (char *cmd, char **reply, int *len, void *data,
>  	r = get_cmdvec(cmd, &cmdvec);
>  
>  	if (r) {
> -		*reply = genhelp_handler(cmd, r);
> -		if (*reply == NULL)
> +		genhelp_handler(cmd, r, reply);
> +		if (get_strbuf_len(reply) == 0)
>  			return EINVAL;
> -		*len = strlen(*reply) + 1;
>  		return 0;
>  	}
>  
> @@ -316,10 +315,9 @@ static int parse_cmd (char *cmd, char **reply, int *len, void *data,
>  
>  	if (!h || !h->fn) {
>  		free_keys(cmdvec);
> -		*reply = genhelp_handler(cmd, EINVAL);
> -		if (*reply == NULL)
> +		genhelp_handler(cmd, EINVAL, reply);
> +		if (get_strbuf_len(reply) == 0)
>  			return EINVAL;
> -		*len = strlen(*reply) + 1;
>  		return 0;
>  	}
>  
> @@ -345,50 +343,42 @@ static int parse_cmd (char *cmd, char **reply, int *len, void *data,
>  		if (r == 0) {
>  			locked = 1;
>  			pthread_testcancel();
> -			r = h->fn(cmdvec, reply, len, data);
> +			r = h->fn(cmdvec, reply, data);
>  		}
>  		pthread_cleanup_pop(locked);
>  	} else
> -		r = h->fn(cmdvec, reply, len, data);
> +		r = h->fn(cmdvec, reply, data);
>  	free_keys(cmdvec);
>  
>  	return r;
>  }
>  
> -static int uxsock_trigger(char *str, char **reply, int *len,
> +static int uxsock_trigger(char *str, struct strbuf *reply,
>  			  bool is_root, void *trigger_data)
>  {
>  	struct vectors * vecs;
>  	int r;
>  
> -	*reply = NULL;
> -	*len = 0;
>  	vecs = (struct vectors *)trigger_data;
>  
>  	if ((str != NULL) && (is_root == false) &&
>  	    (strncmp(str, "list", strlen("list")) != 0) &&
>  	    (strncmp(str, "show", strlen("show")) != 0)) {
> -		*reply = STRDUP("permission deny: need to be root");
> -		if (*reply)
> -			*len = strlen(*reply) + 1;
> +		append_strbuf_str(reply, "permission deny: need to be root");
>  		return 1;
>  	}
>  
> -	r = parse_cmd(str, reply, len, vecs, uxsock_timeout / 1000);
> +	r = parse_cmd(str, reply, vecs, uxsock_timeout / 1000);
>  
>  	if (r > 0) {
>  		if (r == ETIMEDOUT)
> -			*reply = STRDUP("timeout\n");
> +			append_strbuf_str(reply, "timeout\n");
>  		else
> -			*reply = STRDUP("fail\n");
> -		if (*reply)
> -			*len = strlen(*reply) + 1;
> +			append_strbuf_str(reply, "fail\n");
>  		r = 1;
>  	}
> -	else if (!r && *len == 0) {
> -		*reply = STRDUP("ok\n");
> -		if (*reply)
> -			*len = strlen(*reply) + 1;
> +	else if (!r && get_strbuf_len(reply) == 0) {
> +		append_strbuf_str(reply, "ok\n");
>  		r = 0;
>  	}
>  	/* else if (r < 0) leave *reply alone */
> @@ -417,8 +407,6 @@ static void set_client_state(struct client *c, int state)
>  
>  static void handle_client(struct client *c, void *trigger_data)
>  {
> -	int rlen;
> -	char *reply;
>  	ssize_t n;
>  
>  	switch (c->state) {
> @@ -467,12 +455,13 @@ static void handle_client(struct client *c, void *trigger_data)
>  	}
>  
>  	condlog(4, "cli[%d]: Got request [%s]", c->fd, c->cmd);
> -	uxsock_trigger(c->cmd, &reply, &rlen,
> -		       _socket_client_is_root(c->fd),
> +	uxsock_trigger(c->cmd, &c->reply, _socket_client_is_root(c->fd),
>  		       trigger_data);
>  
> -	if (reply) {
> -		if (send_packet(c->fd, reply) != 0)
> +	if (get_strbuf_len(&c->reply) > 0) {
> +		const char *buf = get_strbuf_str(&c->reply);
> +
> +		if (send_packet(c->fd, buf) != 0)
>  			dead_client(c);
>  		else
>  			condlog(4, "cli[%d]: Reply [%zu bytes]", c->fd,
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 26/35] multipathd: uxlsnr: check root on connection startup
  2021-09-10 11:41 ` [dm-devel] [PATCH 26/35] multipathd: uxlsnr: check root on connection startup mwilck
@ 2021-09-16  2:23   ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16  2:23 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:41:11PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> The SO_PEERCRED socket option returns "the credentials that were
> in effect at the time of the call to connect(2)" (see unix(7)).
> So we might as well fetch these credentials at that time.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/uxlsnr.c | 3 +--
>  1 file changed, 1 insertion(+), 2 deletions(-)
> 
> diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
> index 9a945ea..9cf6964 100644
> --- a/multipathd/uxlsnr.c
> +++ b/multipathd/uxlsnr.c
> @@ -92,8 +92,6 @@ static struct pollfd *polls;
>  static int notify_fd = -1;
>  static char *watch_config_dir;
>  
> -static bool _socket_client_is_root(int fd);
> -
>  static bool _socket_client_is_root(int fd)
>  {
>  	socklen_t len = 0;
> @@ -132,6 +130,7 @@ static void new_client(int ux_sock)
>  	INIT_LIST_HEAD(&c->node);
>  	c->fd = fd;
>  	c->state = CLT_RECV;
> +	c->is_root = _socket_client_is_root(c->fd);
>  
>  	/* put it in our linked list */
>  	pthread_mutex_lock(&client_lock);
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 27/35] multipathd: uxlsnr: pass struct client to uxsock_trigger() and parse_cmd()
  2021-09-10 11:41 ` [dm-devel] [PATCH 27/35] multipathd: uxlsnr: pass struct client to uxsock_trigger() and parse_cmd() mwilck
@ 2021-09-16  2:28   ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16  2:28 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:41:12PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> As a next step towards the state machine, give the handler functions
> access to the state of the client connection.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/uxlsnr.c | 61 +++++++++++++++++++++------------------------
>  1 file changed, 29 insertions(+), 32 deletions(-)
> 
> diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
> index 9cf6964..65cb5ca 100644
> --- a/multipathd/uxlsnr.c
> +++ b/multipathd/uxlsnr.c
> @@ -293,31 +293,28 @@ static void handle_inotify(int fd, struct watch_descriptors *wds)
>  
>  static const struct timespec ts_zero = { .tv_sec = 0, };
>  
> -static int parse_cmd (char *cmd, struct strbuf *reply, void *data,
> -		      int timeout)
> +static int parse_cmd (struct client *c, void *data, int timeout)
>  {
>  	int r;
>  	struct handler * h;
> -	vector cmdvec = NULL;
>  	struct timespec tmo;
>  
> -	r = get_cmdvec(cmd, &cmdvec);
> +	r = get_cmdvec(c->cmd, &c->cmdvec);
>  
>  	if (r) {
> -		genhelp_handler(cmd, r, reply);
> -		if (get_strbuf_len(reply) == 0)
> +		genhelp_handler(c->cmd, r, &c->reply);
> +		if (get_strbuf_len(&c->reply) == 0)
>  			return EINVAL;
>  		return 0;
>  	}
>  
> -	h = find_handler_for_cmdvec(cmdvec);
> +	h = find_handler_for_cmdvec(c->cmdvec);
>  
>  	if (!h || !h->fn) {
> -		free_keys(cmdvec);
> -		genhelp_handler(cmd, EINVAL, reply);
> -		if (get_strbuf_len(reply) == 0)
> -			return EINVAL;
> -		return 0;
> +		genhelp_handler(c->cmd, EINVAL, &c->reply);
> +		if (get_strbuf_len(&c->reply) == 0)
> +			r = EINVAL;
> +		goto free_cmdvec;
>  	}
>  
>  	/*
> @@ -342,46 +339,47 @@ static int parse_cmd (char *cmd, struct strbuf *reply, void *data,
>  		if (r == 0) {
>  			locked = 1;
>  			pthread_testcancel();
> -			r = h->fn(cmdvec, reply, data);
> +			r = h->fn(c->cmdvec, &c->reply, data);
>  		}
>  		pthread_cleanup_pop(locked);
>  	} else
> -		r = h->fn(cmdvec, reply, data);
> -	free_keys(cmdvec);
> +		r = h->fn(c->cmdvec, &c->reply, data);
> +
> +free_cmdvec:
> +	free_keys(c->cmdvec);
> +	c->cmdvec = NULL;
>  
>  	return r;
>  }
>  
> -static int uxsock_trigger(char *str, struct strbuf *reply,
> -			  bool is_root, void *trigger_data)
> +static int uxsock_trigger(struct client *c, void *trigger_data)
>  {
>  	struct vectors * vecs;
> -	int r;
> +	int r = 1;
>  
>  	vecs = (struct vectors *)trigger_data;
>  
> -	if ((str != NULL) && (is_root == false) &&
> -	    (strncmp(str, "list", strlen("list")) != 0) &&
> -	    (strncmp(str, "show", strlen("show")) != 0)) {
> -		append_strbuf_str(reply, "permission deny: need to be root");
> -		return 1;
> +
> +	if (!c->is_root &&
> +	    (strncmp(c->cmd, "list", strlen("list")) != 0) &&
> +	    (strncmp(c->cmd, "show", strlen("show")) != 0)) {
> +		append_strbuf_str(&c->reply, "permission deny: need to be root");
> +		return r;
>  	}
>  
> -	r = parse_cmd(str, reply, vecs, uxsock_timeout / 1000);
> +	r = parse_cmd(c, vecs, uxsock_timeout / 1000);
>  
>  	if (r > 0) {
>  		if (r == ETIMEDOUT)
> -			append_strbuf_str(reply, "timeout\n");
> +			append_strbuf_str(&c->reply, "timeout\n");
>  		else
> -			append_strbuf_str(reply, "fail\n");
> -		r = 1;
> +			append_strbuf_str(&c->reply, "fail\n");
>  	}
> -	else if (!r && get_strbuf_len(reply) == 0) {
> -		append_strbuf_str(reply, "ok\n");
> +	else if (!r && get_strbuf_len(&c->reply) == 0) {
> +		append_strbuf_str(&c->reply, "ok\n");
>  		r = 0;
>  	}
>  	/* else if (r < 0) leave *reply alone */
> -
>  	return r;
>  }
>  
> @@ -454,8 +452,7 @@ static void handle_client(struct client *c, void *trigger_data)
>  	}
>  
>  	condlog(4, "cli[%d]: Got request [%s]", c->fd, c->cmd);
> -	uxsock_trigger(c->cmd, &c->reply, _socket_client_is_root(c->fd),
> -		       trigger_data);
> +	uxsock_trigger(c, trigger_data);
>  
>  	if (get_strbuf_len(&c->reply) > 0) {
>  		const char *buf = get_strbuf_str(&c->reply);
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 28/35] multipathd: uxlsnr: move handler execution to separate function
  2021-09-10 11:41 ` [dm-devel] [PATCH 28/35] multipathd: uxlsnr: move handler execution to separate function mwilck
@ 2021-09-16  2:28   ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16  2:28 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:41:13PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> Move the actual execution of the handler out of parse_cmd(). For now,
> we do it in uxsock_trigger().
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/uxlsnr.c | 47 ++++++++++++++++++++++++++++-----------------
>  1 file changed, 29 insertions(+), 18 deletions(-)
> 
> diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
> index 65cb5ca..cfff0ae 100644
> --- a/multipathd/uxlsnr.c
> +++ b/multipathd/uxlsnr.c
> @@ -293,11 +293,9 @@ static void handle_inotify(int fd, struct watch_descriptors *wds)
>  
>  static const struct timespec ts_zero = { .tv_sec = 0, };
>  
> -static int parse_cmd (struct client *c, void *data, int timeout)
> +static int parse_cmd(struct client *c)
>  {
>  	int r;
> -	struct handler * h;
> -	struct timespec tmo;
>  
>  	r = get_cmdvec(c->cmd, &c->cmdvec);
>  
> @@ -308,26 +306,35 @@ static int parse_cmd (struct client *c, void *data, int timeout)
>  		return 0;
>  	}
>  
> -	h = find_handler_for_cmdvec(c->cmdvec);
> +	c->handler = find_handler_for_cmdvec(c->cmdvec);
>  
> -	if (!h || !h->fn) {
> +	if (!c->handler || !c->handler->fn) {
>  		genhelp_handler(c->cmd, EINVAL, &c->reply);
>  		if (get_strbuf_len(&c->reply) == 0)
>  			r = EINVAL;
> -		goto free_cmdvec;
> +		else
> +			r = 0;
>  	}
>  
> -	/*
> -	 * execute handler
> -	 */
> +	return r;
> +}
> +
> +static int execute_handler(struct client *c, struct vectors *vecs, int timeout)
> +{
> +	int r;
> +	struct timespec tmo;
> +
> +	if (!c->handler)
> +		return EINVAL;
> +
>  	if (clock_gettime(CLOCK_REALTIME, &tmo) == 0) {
>  		tmo.tv_sec += timeout;
>  	} else {
>  		tmo.tv_sec = 0;
>  	}
> -	if (h->locked) {
> +
> +	if (c->handler->locked) {
>  		int locked = 0;
> -		struct vectors * vecs = (struct vectors *)data;
>  
>  		pthread_cleanup_push(cleanup_lock, &vecs->lock);
>  		if (tmo.tv_sec) {
> @@ -339,15 +346,11 @@ static int parse_cmd (struct client *c, void *data, int timeout)
>  		if (r == 0) {
>  			locked = 1;
>  			pthread_testcancel();
> -			r = h->fn(c->cmdvec, &c->reply, data);
> +			r = c->handler->fn(c->cmdvec, &c->reply, vecs);
>  		}
>  		pthread_cleanup_pop(locked);
>  	} else
> -		r = h->fn(c->cmdvec, &c->reply, data);
> -
> -free_cmdvec:
> -	free_keys(c->cmdvec);
> -	c->cmdvec = NULL;
> +		r = c->handler->fn(c->cmdvec, &c->reply, vecs);
>  
>  	return r;
>  }
> @@ -367,7 +370,15 @@ static int uxsock_trigger(struct client *c, void *trigger_data)
>  		return r;
>  	}
>  
> -	r = parse_cmd(c, vecs, uxsock_timeout / 1000);
> +	r = parse_cmd(c);
> +
> +	if (r == 0 && c->handler)
> +		r = execute_handler(c, vecs, uxsock_timeout / 1000);
> +
> +	if (c->cmdvec) {
> +		free_keys(c->cmdvec);
> +		c->cmdvec = NULL;
> +	}
>  
>  	if (r > 0) {
>  		if (r == ETIMEDOUT)
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 29/35] multipathd: uxlsnr: use parser to determine non-root commands
  2021-09-10 11:41 ` [dm-devel] [PATCH 29/35] multipathd: uxlsnr: use parser to determine non-root commands mwilck
@ 2021-09-16  2:29   ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16  2:29 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:41:14PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> Rather than using a separate poor-man's parser for checking root
> commands, use the real parser. It will return "LIST" as first verb
> for the read-only commands that non-root users may execute.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/uxlsnr.c | 27 +++++++++++++++++----------
>  1 file changed, 17 insertions(+), 10 deletions(-)
> 
> diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
> index cfff0ae..ff9604f 100644
> --- a/multipathd/uxlsnr.c
> +++ b/multipathd/uxlsnr.c
> @@ -362,16 +362,15 @@ static int uxsock_trigger(struct client *c, void *trigger_data)
>  
>  	vecs = (struct vectors *)trigger_data;
>  
> -
> -	if (!c->is_root &&
> -	    (strncmp(c->cmd, "list", strlen("list")) != 0) &&
> -	    (strncmp(c->cmd, "show", strlen("show")) != 0)) {
> -		append_strbuf_str(&c->reply, "permission deny: need to be root");
> -		return r;
> -	}
> -
>  	r = parse_cmd(c);
>  
> +	if (r == 0 && c->cmdvec && VECTOR_SIZE(c->cmdvec) > 0) {
> +		struct key *kw = VECTOR_SLOT(c->cmdvec, 0);
> +
> +		if (!c->is_root && kw->code != LIST)
> +			r = EPERM;
> +	}
> +
>  	if (r == 0 && c->handler)
>  		r = execute_handler(c, vecs, uxsock_timeout / 1000);
>  
> @@ -381,10 +380,18 @@ static int uxsock_trigger(struct client *c, void *trigger_data)
>  	}
>  
>  	if (r > 0) {
> -		if (r == ETIMEDOUT)
> +		switch(r) {
> +		case ETIMEDOUT:
>  			append_strbuf_str(&c->reply, "timeout\n");
> -		else
> +			break;
> +		case EPERM:
> +			append_strbuf_str(&c->reply,
> +					  "permission deny: need to be root\n");
> +			break;
> +		default:
>  			append_strbuf_str(&c->reply, "fail\n");
> +			break;
> +		}
>  	}
>  	else if (!r && get_strbuf_len(&c->reply) == 0) {
>  		append_strbuf_str(&c->reply, "ok\n");
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 30/35] multipathd: uxlsnr: merge uxsock_trigger() into state machine
  2021-09-10 11:41 ` [dm-devel] [PATCH 30/35] multipathd: uxlsnr: merge uxsock_trigger() into state machine mwilck
@ 2021-09-16  3:32   ` Benjamin Marzinski
  2021-09-16  8:02     ` Martin Wilck
  2021-11-12 22:07     ` Martin Wilck
  0 siblings, 2 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16  3:32 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:41:15PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> This patch sets up the bulk of the state machine. The idea is to
> fall through the case labels as long as possible (when steps succeed)
> and return to the caller if either an error occurs, or it becomes
> necessary to wait for some pollable condition.
> 
> While doing this, switch to negative error codes for the functions
> in uxlsnr.c (e.g. parse_cmd()). Positive return codes are reserved
> for the cli_handler functions themselves. This way we can clearly
> distinguish the error source, and avoid confusion and misleading
> error messages. No cli_handler returns negative values.
> 
> Note: with this patch applied, clients may hang and time out if
> the handler fails to acquire the vecs lock. This will be fixed in the
> follow-up patch "multipathd: uxlsnr: add idle notification".
> 
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/uxlsnr.c | 145 ++++++++++++++++++++++++--------------------
>  1 file changed, 80 insertions(+), 65 deletions(-)
> 
> diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
> index ff9604f..553274b 100644
> --- a/multipathd/uxlsnr.c
> +++ b/multipathd/uxlsnr.c
> @@ -299,22 +299,13 @@ static int parse_cmd(struct client *c)
>  
>  	r = get_cmdvec(c->cmd, &c->cmdvec);
>  
> -	if (r) {
> -		genhelp_handler(c->cmd, r, &c->reply);
> -		if (get_strbuf_len(&c->reply) == 0)
> -			return EINVAL;
> -		return 0;
> -	}
> +	if (r)
> +		return -r;
>  
>  	c->handler = find_handler_for_cmdvec(c->cmdvec);
>  
> -	if (!c->handler || !c->handler->fn) {
> -		genhelp_handler(c->cmd, EINVAL, &c->reply);
> -		if (get_strbuf_len(&c->reply) == 0)
> -			r = EINVAL;
> -		else
> -			r = 0;
> -	}
> +	if (!c->handler || !c->handler->fn)
> +		return -EINVAL;
>  
>  	return r;
>  }
> @@ -325,7 +316,7 @@ static int execute_handler(struct client *c, struct vectors *vecs, int timeout)
>  	struct timespec tmo;
>  
>  	if (!c->handler)
> -		return EINVAL;
> +		return -EINVAL;
>  
>  	if (clock_gettime(CLOCK_REALTIME, &tmo) == 0) {
>  		tmo.tv_sec += timeout;
> @@ -355,50 +346,29 @@ static int execute_handler(struct client *c, struct vectors *vecs, int timeout)
>  	return r;
>  }
>  
> -static int uxsock_trigger(struct client *c, void *trigger_data)
> +void default_reply(struct client *c, int r)
>  {
> -	struct vectors * vecs;
> -	int r = 1;
> -
> -	vecs = (struct vectors *)trigger_data;
> -
> -	r = parse_cmd(c);
> -
> -	if (r == 0 && c->cmdvec && VECTOR_SIZE(c->cmdvec) > 0) {
> -		struct key *kw = VECTOR_SLOT(c->cmdvec, 0);
> -
> -		if (!c->is_root && kw->code != LIST)
> -			r = EPERM;
> -	}
> -
> -	if (r == 0 && c->handler)
> -		r = execute_handler(c, vecs, uxsock_timeout / 1000);
> -
> -	if (c->cmdvec) {
> -		free_keys(c->cmdvec);
> -		c->cmdvec = NULL;
> -	}
> -
> -	if (r > 0) {
> -		switch(r) {
> -		case ETIMEDOUT:
> -			append_strbuf_str(&c->reply, "timeout\n");
> -			break;
> -		case EPERM:
> -			append_strbuf_str(&c->reply,
> -					  "permission deny: need to be root\n");
> -			break;
> -		default:
> -			append_strbuf_str(&c->reply, "fail\n");
> -			break;
> -		}
> -	}
> -	else if (!r && get_strbuf_len(&c->reply) == 0) {
> +	switch(r) {
> +	case -EINVAL:
> +	case -ESRCH:
> +	case -ENOMEM:

get_cmdvec() returns positive errors and do_genhelp() expects positive
errors, but this expects negative errors.

> +		/* return codes from get_cmdvec() */
> +		genhelp_handler(c->cmd, r, &c->reply);
> +		break;
> +	case -EPERM:
> +		append_strbuf_str(&c->reply,
> +				  "permission deny: need to be root\n");
> +		break;
> +	case -ETIMEDOUT:
> +		append_strbuf_str(&c->reply, "timeout\n");
> +		break;
> +	case 0:
>  		append_strbuf_str(&c->reply, "ok\n");
> -		r = 0;
> +		break;
> +	default:
> +		append_strbuf_str(&c->reply, "fail\n");
> +		break;
>  	}
> -	/* else if (r < 0) leave *reply alone */
> -	return r;
>  }
>  
>  static void set_client_state(struct client *c, int state)
> @@ -409,6 +379,7 @@ static void set_client_state(struct client *c, int state)
>  		reset_strbuf(&c->reply);
>  		memset(c->cmd, '\0', sizeof(c->cmd));
>  		c->expires = ts_zero;
> +		c->error = 0;
>  		/* fallthrough */
>  	case CLT_SEND:
>  		/* reuse these fields for next data transfer */
> @@ -420,10 +391,13 @@ static void set_client_state(struct client *c, int state)
>  	c->state = state;
>  }
>  
> -static void handle_client(struct client *c, void *trigger_data)
> +static void handle_client(struct client *c, struct vectors *vecs)
>  {
>  	ssize_t n;
>  
> +	condlog(4, "%s: cli[%d] state=%d cmd=\"%s\" repl \"%s\"", __func__,
> +		c->fd, c->state, c->cmd, get_strbuf_str(&c->reply));
> +
>  	switch (c->state) {
>  	case CLT_RECV:
>  		if (c->cmd_len == 0) {
> @@ -464,15 +438,52 @@ static void handle_client(struct client *c, void *trigger_data)
>  				return;
>  			set_client_state(c, CLT_PARSE);
>  		}
> -		break;
> -	default:
> -		break;
> -	}
> +		condlog(4, "cli[%d]: Got request [%s]", c->fd, c->cmd);
> +		/* fallthrough */
>  
> -	condlog(4, "cli[%d]: Got request [%s]", c->fd, c->cmd);
> -	uxsock_trigger(c, trigger_data);
> +	case CLT_PARSE:

Since you already print this information at the top of the function, it
seems liek it would make more sense to put these prints before the
followthroughs, so that they get double printed immediately when the
function is called for devices not in CLT_RECV.

> +		condlog(4, "%s: cli[%d] state=%d cmd=\"%s\" repl \"%s\"", __func__,
> +			c->fd, c->state, c->cmd,  get_strbuf_str(&c->reply));
> +		c->error = parse_cmd(c);
> +
> +		/* Permission check */
> +		if (c->error == 0 && c->cmdvec && VECTOR_SIZE(c->cmdvec) > 0) {
> +			struct key *kw = VECTOR_SLOT(c->cmdvec, 0);
> +
> +			if (!c->is_root && kw->code != LIST) {
> +				/* this will fall through to CLT_SEND */
> +				c->error = -EPERM;
> +				condlog(0, "%s: cli[%d]: unauthorized cmd \"%s\"",
> +					__func__, c->fd, c->cmd);
> +			}
> +		}
> +		set_client_state(c, CLT_WAIT_LOCK);

I don't have strong feelings about this, but this state machine doesn't
want to always fall through. sometimes, like if you get -EPERM, you want
to swith from CLT_PARSE to CLT_SEND.  If instead of fallthroughs, you
just put the switch statement in a loop, and simply returned when you
wanted to break out to uxsock_listen, you could jump from any state to
any other state, and wouldn't need to have code to skip the actions of
some states, to enable the follow throughs. Just a thought.

-Ben

> +		/* fallthrough */
> +
> +	case CLT_WAIT_LOCK:
> +		condlog(4, "%s: cli[%d] state=%d cmd=\"%s\" repl \"%s\"", __func__,
> +			c->fd, c->state, c->cmd, get_strbuf_str(&c->reply));
> +		/* tbd */
> +		set_client_state(c, CLT_WORK);
> +		/* fallthrough */
> +
> +	case CLT_WORK:
> +		condlog(4, "%s: cli[%d] state=%d cmd=\"%s\" repl \"%s\"", __func__,
> +			c->fd, c->state, c->cmd, get_strbuf_str(&c->reply));
> +		if (c->error == 0 && c->handler)
> +			c->error = execute_handler(c, vecs, uxsock_timeout / 1000);
> +
> +		if (c->cmdvec) {
> +			free_keys(c->cmdvec);
> +			c->cmdvec = NULL;
> +		}
> +		set_client_state(c, CLT_SEND);
> +		/* fallthrough */
> +
> +	case CLT_SEND:
> +		if (get_strbuf_len(&c->reply) == 0)
> +			default_reply(c, c->error);
>  
> -	if (get_strbuf_len(&c->reply) > 0) {
>  		const char *buf = get_strbuf_str(&c->reply);
>  
>  		if (send_packet(c->fd, buf) != 0)
> @@ -481,9 +492,13 @@ static void handle_client(struct client *c, void *trigger_data)
>  			condlog(4, "cli[%d]: Reply [%zu bytes]", c->fd,
>  				get_strbuf_len(&c->reply) + 1);
>  		reset_strbuf(&c->reply);
> -	}
>  
> -	set_client_state(c, CLT_RECV);
> +		set_client_state(c, CLT_RECV);
> +		break;
> +
> +	default:
> +		break;
> +	}
>  }
>  
>  /*
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 31/35] multipathd: uxlsnr: add idle notification
  2021-09-10 11:41 ` [dm-devel] [PATCH 31/35] multipathd: uxlsnr: add idle notification mwilck
@ 2021-09-16  4:14   ` Benjamin Marzinski
  2021-09-16  8:54     ` Martin Wilck
  0 siblings, 1 reply; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16  4:14 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:41:16PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> The previous patches added the state machine and the timeout handling,
> but there was no wakeup mechanism for the uxlsnr for cases where
> client connections were waiting for the vecs lock.
> 
> This patch uses the previously introduced wakeup mechanism of
> struct mutex_lock for this purpose. Processes which unlock the
> "global" vecs lock send an event in an eventfd which the uxlsnr
> loop is polling for.
> 
> As we are now woken up for servicing client handlers that don't
> wait for input but for the lock, we need to set up the pollfds
> differently, and iterate over all clients when handling events,
> not only over the ones that are receiving. The hangup handling
> is changed, too. We have to look at every client, even if one has
> hung up. Note that I don't take client_lock for the loop in
> uxsock_listen(), it's not necessary and will be removed elsewhere
> in a follow-up patch.
> 
> With this in place, the lock need not be taken in execute_handler()
> any more. The uxlsnr only ever calls trylock() on the vecs lock,
> avoiding any waiting for other threads to finish.
> 
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/uxlsnr.c | 211 ++++++++++++++++++++++++++++++--------------
>  1 file changed, 143 insertions(+), 68 deletions(-)
> 
> diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
> index 553274b..4637954 100644
> --- a/multipathd/uxlsnr.c
> +++ b/multipathd/uxlsnr.c
> @@ -24,6 +24,7 @@
>  #include <signal.h>
>  #include <stdbool.h>
>  #include <sys/inotify.h>
> +#include <sys/eventfd.h>
>  #include "checkers.h"
>  #include "memory.h"
>  #include "debug.h"
> @@ -70,6 +71,7 @@ struct client {
>  enum {
>  	POLLFD_UX = 0,
>  	POLLFD_NOTIFY,
> +	POLLFD_IDLE,
>  	POLLFDS_BASE,
>  };
>  
> @@ -90,8 +92,23 @@ static LIST_HEAD(clients);
>  static pthread_mutex_t client_lock = PTHREAD_MUTEX_INITIALIZER;
>  static struct pollfd *polls;
>  static int notify_fd = -1;
> +static int idle_fd = -1;
>  static char *watch_config_dir;
>  
> +struct possible_lock {
> +	struct mutex_lock *lock;
> +	bool held;
> +};
> +
> +static void unlock_if_held(void *arg)
> +{
> +	struct possible_lock *pl = arg;
> +
> +	/* don't call unlock_wakeup() here, lest we wakeup ourselves */
> +	if (pl->held)
> +		__unlock(pl->lock);
> +}
> +
>  static bool _socket_client_is_root(int fd)
>  {
>  	socklen_t len = 0;
> @@ -187,6 +204,17 @@ void uxsock_cleanup(void *arg)
>  	free_polls();
>  }
>  
> +void wakeup_cleanup(void *arg)
> +{
> +	struct mutex_lock *lck = arg;
> +	int fd = idle_fd;
> +
> +	idle_fd = -1;
> +	set_wakeup_fn(lck, NULL);
> +	if (fd != -1)
> +		close(fd);
> +}
> +
>  struct watch_descriptors {
>  	int conf_wd;
>  	int dir_wd;
> @@ -293,6 +321,18 @@ static void handle_inotify(int fd, struct watch_descriptors *wds)
>  
>  static const struct timespec ts_zero = { .tv_sec = 0, };
>  
> +/* call with clients lock held */
> +static bool __need_lock(void)
> +{
> +	struct client *c;
> +
> +	list_for_each_entry(c, &clients, node) {
> +		if (c->state == CLT_WAIT_LOCK)
> +			return true;
> +	}
> +	return false;
> +}
> +
>  static int parse_cmd(struct client *c)
>  {
>  	int r;
> @@ -310,40 +350,31 @@ static int parse_cmd(struct client *c)
>  	return r;
>  }
>  
> -static int execute_handler(struct client *c, struct vectors *vecs, int timeout)
> +static int execute_handler(struct client *c, struct vectors *vecs)
>  {
> -	int r;
> -	struct timespec tmo;
>  
> -	if (!c->handler)
> +	if (!c->handler || !c->handler->fn)
>  		return -EINVAL;
>  
> -	if (clock_gettime(CLOCK_REALTIME, &tmo) == 0) {
> -		tmo.tv_sec += timeout;
> -	} else {
> -		tmo.tv_sec = 0;
> -	}
> +	return c->handler->fn(c->cmdvec, &c->reply, vecs);
> +}
>  
> -	if (c->handler->locked) {
> -		int locked = 0;
> +static void wakeup_listener(void)
> +{
> +	uint64_t one = 1;
>  
> -		pthread_cleanup_push(cleanup_lock, &vecs->lock);
> -		if (tmo.tv_sec) {
> -			r = timedlock(&vecs->lock, &tmo);
> -		} else {
> -			lock(&vecs->lock);
> -			r = 0;
> -		}
> -		if (r == 0) {
> -			locked = 1;
> -			pthread_testcancel();
> -			r = c->handler->fn(c->cmdvec, &c->reply, vecs);
> -		}
> -		pthread_cleanup_pop(locked);
> -	} else
> -		r = c->handler->fn(c->cmdvec, &c->reply, vecs);
> +	if (idle_fd != -1 &&
> +	    write(idle_fd, &one, sizeof(one)) != sizeof(one))
> +		condlog(1, "%s: failed", __func__);
> +}
>  
> -	return r;
> +static void drain_idle_fd(int fd)
> +{
> +	uint64_t val;
> +	int rc;
> +
> +	rc = read(fd, &val, sizeof(val));
> +	condlog(4, "%s: %d, %"PRIu64, __func__, rc, val);
>  }
>  
>  void default_reply(struct client *c, int r)
> @@ -391,15 +422,26 @@ static void set_client_state(struct client *c, int state)
>  	c->state = state;
>  }
>  
> -static void handle_client(struct client *c, struct vectors *vecs)
> +static void handle_client(struct client *c, struct vectors *vecs, short revents)
>  {
>  	ssize_t n;
> +	struct possible_lock pl = {
> +		.lock = &vecs->lock,
> +		.held = false,
> +	};
>  
> -	condlog(4, "%s: cli[%d] state=%d cmd=\"%s\" repl \"%s\"", __func__,
> -		c->fd, c->state, c->cmd, get_strbuf_str(&c->reply));
> +	if (revents & (POLLHUP|POLLERR)) {
> +		c->error = -ECONNRESET;
> +		return;
> +	}
> +
> +	condlog(4, "%s: cli[%d] poll=%x state=%d cmd=\"%s\" repl \"%s\"", __func__,
> +		c->fd, revents, c->state, c->cmd, get_strbuf_str(&c->reply));
>  
>  	switch (c->state) {
>  	case CLT_RECV:
> +		if (!(revents & POLLIN))
> +			return;
>  		if (c->cmd_len == 0) {
>  			/*
>  			 * We got POLLIN; assume that at least the length can
> @@ -463,15 +505,28 @@ static void handle_client(struct client *c, struct vectors *vecs)
>  	case CLT_WAIT_LOCK:
>  		condlog(4, "%s: cli[%d] state=%d cmd=\"%s\" repl \"%s\"", __func__,
>  			c->fd, c->state, c->cmd, get_strbuf_str(&c->reply));
> -		/* tbd */
> +
> +		if (c->error == 0 && c->handler && c->handler->locked) {
> +			pl.held = trylock(pl.lock) == 0;

I do worry that if there are, for instance, a lot of uevents coming in,
this could starve the uxlsnr thread, since other threads could be
grabbing and releasing the vecs lock, but if it's usually being held,
then the uxlsnr thread might never try to grab it when it's free, and it
will keep losing its place in line. Also, every time that the vecs lock
is dropped between ppoll() calls, a wakeup will get triggered, even if
the lock was grabbed by something else before the ppoll thread runs.

I suppose the only way to deal with that would be to move the locking
commands to a list handled by a separate thread, so that it could block
without stalling the non-locking commands.

> +			if (!pl.held) {
> +				condlog(4, "%s: cli[%d] waiting for lock",
> +					__func__, c->fd);
> +				return;
> +			} else
> +				condlog(4, "%s: cli[%d] grabbed lock",
> +					__func__, c->fd);
> +		}
>  		set_client_state(c, CLT_WORK);
>  		/* fallthrough */
>  

We should never return to uxsock_listen() while the lock is held. The
code doesn't, but the fact that CLT_WORK is a separate state makes it
look like this could be possible.  Since we must never be in CLT_WORK
without first being in CLT_WAIT_LOCK, I don't see any point for having a
separate CLT_WAIT_LOCK state.  CLT_WORK should do both the locking if
necessary, and calling the handler.

>  	case CLT_WORK:
>  		condlog(4, "%s: cli[%d] state=%d cmd=\"%s\" repl \"%s\"", __func__,
>  			c->fd, c->state, c->cmd, get_strbuf_str(&c->reply));
> -		if (c->error == 0 && c->handler)
> -			c->error = execute_handler(c, vecs, uxsock_timeout / 1000);
> +		if (c->error == 0 && c->handler) {
> +			pthread_cleanup_push(unlock_if_held, &pl);
> +			c->error = execute_handler(c, vecs);
> +			pthread_cleanup_pop(1);
> +		}
>  
>  		if (c->cmdvec) {
>  			free_keys(c->cmdvec);
> @@ -511,6 +566,8 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
>  	/* conf->sequence_nr will be 1 when uxsock_listen is first called */
>  	unsigned int sequence_nr = 0;
>  	struct watch_descriptors wds = { .conf_wd = -1, .dir_wd = -1 };
> +	bool need_lock = false;
> +	struct vectors *vecs = trigger_data;
>  
>  	condlog(3, "uxsock: startup listener");
>  	polls = MALLOC(max_pfds * sizeof(*polls));
> @@ -521,6 +578,14 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
>  	notify_fd = inotify_init1(IN_NONBLOCK);
>  	if (notify_fd == -1) /* it's fine if notifications fail */
>  		condlog(3, "failed to start up configuration notifications");
> +
> +	pthread_cleanup_push(wakeup_cleanup, &vecs->lock);
> +	idle_fd = eventfd(0, EFD_NONBLOCK|EFD_CLOEXEC);
> +	if (idle_fd == -1)
> +		condlog(1, "failed to create idle fd");
> +	else
> +		set_wakeup_fn(&vecs->lock, wakeup_listener);
> +
>  	sigfillset(&mask);
>  	sigdelset(&mask, SIGINT);
>  	sigdelset(&mask, SIGTERM);
> @@ -572,16 +637,30 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
>  		else
>  			polls[POLLFD_NOTIFY].events = POLLIN;
>  
> +		need_lock = __need_lock();
> +		polls[POLLFD_IDLE].fd = idle_fd;
> +		if (need_lock)
> +			polls[POLLFD_IDLE].events = POLLIN;
> +		else
> +			polls[POLLFD_IDLE].events = 0;
> +
>  		/* setup the clients */
> -		i = POLLFDS_BASE;
> -		list_for_each_entry(c, &clients, node) {
> -			polls[i].fd = c->fd;
> -			polls[i].events = POLLIN;
> -			i++;
> -			if (i >= max_pfds)
> -				break;
> -		}
> -		n_pfds = i;
> +                i = POLLFDS_BASE;
> +                list_for_each_entry(c, &clients, node) {
> +                        switch(c->state) {
> +                        case CLT_RECV:
> +                                polls[i].events = POLLIN;
> +                                break;
> +                        default:
> +				/* don't poll for this client */
> +                                continue;
> +                        }
> +                        polls[i].fd = c->fd;
> +                        i++;
> +                        if (i >= max_pfds)
> +                                break;
> +                }
> +                n_pfds = i;
>  		pthread_cleanup_pop(1);
>  
>  		/* most of our life is spent in this call */
> @@ -604,33 +683,28 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
>  			handle_signals(true);
>  			continue;
>  		}
> +		if (polls[POLLFD_IDLE].fd != -1 &&
> +		    polls[POLLFD_IDLE].revents & POLLIN)
> +			drain_idle_fd(idle_fd);
>  
> -		/* see if a client wants to speak to us */
> -		for (i = POLLFDS_BASE; i < n_pfds; i++) {
> -			if (polls[i].revents & (POLLIN|POLLHUP|POLLERR)) {
> -				c = NULL;
> -				pthread_mutex_lock(&client_lock);
> -				list_for_each_entry(tmp, &clients, node) {
> -					if (tmp->fd == polls[i].fd) {
> -						c = tmp;
> -						break;
> -					}
> -				}
> -				pthread_mutex_unlock(&client_lock);
> -				if (!c) {
> -					condlog(4, "cli%d: new fd %d",
> -						i, polls[i].fd);
> -					continue;
> -				}
> -				if (polls[i].revents & (POLLHUP|POLLERR)) {
> -					condlog(4, "cli[%d]: Disconnected",
> -						c->fd);
> -					dead_client(c);
> -					continue;
> -				}
> -				handle_client(c, trigger_data);
> -				if (c->error == -ECONNRESET)
> -					dead_client(c);
> +		/* see if a client needs handling */
> +		list_for_each_entry_safe(c, tmp, &clients, node) {
> +			short revents = 0;
> +
> +			for (i = POLLFDS_BASE; i < n_pfds; i++) {
> +                                if (polls[i].fd == c->fd) {
> +                                        revents = polls[i].revents;
> +                                        break;
> +                                }
> +                        }
> +
> +			handle_client(c, trigger_data, revents);
> +
> +			if (c->error == -ECONNRESET) {
> +				condlog(4, "cli[%d]: disconnected", c->fd);
> +				dead_client(c);
> +				if (i < n_pfds)
> +					polls[i].fd = -1;
>  			}
>  		}
>  		/* see if we got a non-fatal signal */
> @@ -646,5 +720,6 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
>  			handle_inotify(notify_fd, &wds);
>  	}
>  
> +	pthread_cleanup_pop(1);
>  	return NULL;
>  }
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 32/35] multipathd: uxlsnr: add timeout handling
  2021-09-10 11:41 ` [dm-devel] [PATCH 32/35] multipathd: uxlsnr: add timeout handling mwilck
@ 2021-09-16  4:17   ` Benjamin Marzinski
  2021-09-16  8:58     ` Martin Wilck
  0 siblings, 1 reply; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16  4:17 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:41:17PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> Our ppoll() call needs to wake up when a client request times out.
> This logic can be added by determining the first client that's about
> to time out. The logic in handle_client() will then cause a timeout
> reply to be sent to the client. This is more client-friendly
> as the client timing out without receiving a reply.
> 
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/uxlsnr.c | 58 +++++++++++++++++++++++++++++++++++++++++----
>  1 file changed, 53 insertions(+), 5 deletions(-)
> 
> diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
> index 4637954..1bf4126 100644
> --- a/multipathd/uxlsnr.c
> +++ b/multipathd/uxlsnr.c
> @@ -320,6 +320,35 @@ static void handle_inotify(int fd, struct watch_descriptors *wds)
>  }
>  
>  static const struct timespec ts_zero = { .tv_sec = 0, };
> +static const struct timespec ts_max = { .tv_sec = LONG_MAX, .tv_nsec = 999999999 };
> +
> +/* call with clients lock held */
> +static struct timespec *__get_soonest_timeout(struct timespec *ts)
> +{
> +	struct timespec ts_min = ts_max, now;
> +	bool any = false;
> +	struct client *c;
> +
> +	list_for_each_entry(c, &clients, node) {
> +		if (timespeccmp(&c->expires, &ts_zero) != 0 &&
> +		    timespeccmp(&c->expires, &ts_min) < 0) {
> +			ts_min = c->expires;
> +			any = true;
> +		}
> +	}
> +
> +	if (!any)
> +		return NULL;
> +
> +	get_monotonic_time(&now);
> +	timespecsub(&ts_min, &now, ts);
> +	if (timespeccmp(ts, &ts_zero) < 0)
> +		*ts = ts_zero;
> +
> +	condlog(4, "%s: next client expires in %ld.%03lds", __func__,
> +		(long)ts->tv_sec, ts->tv_nsec / 1000000);
> +	return ts;
> +}
>  
>  /* call with clients lock held */
>  static bool __need_lock(void)
> @@ -422,6 +451,24 @@ static void set_client_state(struct client *c, int state)
>  	c->state = state;
>  }
>  
> +static void check_timeout(struct client *c)
> +{
> +	struct timespec now;
> +
> +	if (timespeccmp(&c->expires, &ts_zero) == 0)
> +		return;
> +
> +	get_monotonic_time(&now);
> +	if (timespeccmp(&c->expires, &now) > 0)
> +		return;
> +
> +	condlog(2, "%s: cli[%d]: timed out at %ld.%03ld", __func__,
> +		c->fd, (long)c->expires.tv_sec, c->expires.tv_nsec / 1000000);
> +
> +	c->error = -ETIMEDOUT;
> +	set_client_state(c, CLT_SEND);
> +}
> +
>  static void handle_client(struct client *c, struct vectors *vecs, short revents)
>  {
>  	ssize_t n;
> @@ -435,6 +482,8 @@ static void handle_client(struct client *c, struct vectors *vecs, short revents)
>  		return;
>  	}
>  
> +	check_timeout(c);
> +
>  	condlog(4, "%s: cli[%d] poll=%x state=%d cmd=\"%s\" repl \"%s\"", __func__,
>  		c->fd, revents, c->state, c->cmd, get_strbuf_str(&c->reply));
>  
> @@ -594,6 +643,7 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
>  	while (1) {
>  		struct client *c, *tmp;
>  		int i, n_pfds, poll_count, num_clients;
> +		struct timespec __timeout, *timeout;

Maybe it's just too late to be looking at code, but I'm missing why we
need a separate variable that it a pointer to __timeout.

-Ben

>  
>  		/* setup for a poll */
>  		pthread_mutex_lock(&client_lock);
> @@ -661,10 +711,12 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
>                                  break;
>                  }
>                  n_pfds = i;
> +		timeout = __get_soonest_timeout(&__timeout);
> +
>  		pthread_cleanup_pop(1);
>  
>  		/* most of our life is spent in this call */
> -		poll_count = ppoll(polls, n_pfds, NULL, &mask);
> +		poll_count = ppoll(polls, n_pfds, timeout, &mask);
>  
>  		handle_signals(false);
>  		if (poll_count == -1) {
> @@ -679,10 +731,6 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
>  			break;
>  		}
>  
> -		if (poll_count == 0) {
> -			handle_signals(true);
> -			continue;
> -		}
>  		if (polls[POLLFD_IDLE].fd != -1 &&
>  		    polls[POLLFD_IDLE].revents & POLLIN)
>  			drain_idle_fd(idle_fd);
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 33/35] multipathd: uxlsnr: use poll loop for sending, too
  2021-09-10 11:41 ` [dm-devel] [PATCH 33/35] multipathd: uxlsnr: use poll loop for sending, too mwilck
@ 2021-09-16  4:22   ` Benjamin Marzinski
  2021-09-16  9:33     ` Martin Wilck
  0 siblings, 1 reply; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16  4:22 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:41:18PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> send_packet() may busy-loop. By polling for POLLOUT, we can
> avoid that, even if it's very unlikely in practice.
> 
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/uxlsnr.c | 39 ++++++++++++++++++++++++++++++++-------
>  1 file changed, 32 insertions(+), 7 deletions(-)
> 
> diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
> index 1bf4126..c18b2c4 100644
> --- a/multipathd/uxlsnr.c
> +++ b/multipathd/uxlsnr.c
> @@ -588,15 +588,37 @@ static void handle_client(struct client *c, struct vectors *vecs, short revents)
>  		if (get_strbuf_len(&c->reply) == 0)
>  			default_reply(c, c->error);
>  
> -		const char *buf = get_strbuf_str(&c->reply);
> +		if (c->cmd_len == 0) {
> +			size_t len = get_strbuf_len(&c->reply) + 1;
>  
> -		if (send_packet(c->fd, buf) != 0)
> -			dead_client(c);
> -		else
> -			condlog(4, "cli[%d]: Reply [%zu bytes]", c->fd,
> -				get_strbuf_len(&c->reply) + 1);
> -		reset_strbuf(&c->reply);
> +			if (send(c->fd, &len, sizeof(len), MSG_NOSIGNAL)
> +			    != sizeof(len)) {

This assumes that failing to send the size is always an error. What
about if we get EINTR/EAGAIN? Also, it seems pretty likely that we will
either send all of the size or none of it, but I'm not sure we can
guarantee that.  send_packet() handled partitial writes of the length.
Actually, mpath_recv_reply_len() which is still used by CLT_RECV still
uses read_all(), instead of just polling again on partial reads.

Also, the fd is not set to be non-blocking. and if we fall through to
CLT_SEND, we haven't checked for a POLLOUT revent, so technically, I
believe the write could block here.

-Ben

> +				c->error = -ECONNRESET;
> +				return;
> +			}
> +			c->cmd_len = len;
> +			return;
> +		}
>  
> +		if (c->len < c->cmd_len) {
> +			const char *buf = get_strbuf_str(&c->reply);
> +
> +			n = send(c->fd, buf + c->len, c->cmd_len, MSG_NOSIGNAL);
> +			if (n == -1) {
> +				if (errno == EAGAIN || errno == EINTR)
> +					return;
> +				else {
> +					c->error = -ECONNRESET;
> +					return;
> +				}
> +			}
> +			c->len += n;
> +		}
> +		if (c->len < c->cmd_len)
> +			/* continue polling */
> +			return;
> +
> +		condlog(4, "cli[%d]: Reply [%zu bytes]", c->fd, c->cmd_len);
>  		set_client_state(c, CLT_RECV);
>  		break;
>  
> @@ -701,6 +723,9 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
>                          case CLT_RECV:
>                                  polls[i].events = POLLIN;
>                                  break;
> +			case CLT_SEND:
> +				polls[i].events = POLLOUT;
> +				break;
>                          default:
>  				/* don't poll for this client */
>                                  continue;
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 34/35] multipathd: uxlsnr: drop client_lock
  2021-09-10 11:41 ` [dm-devel] [PATCH 34/35] multipathd: uxlsnr: drop client_lock mwilck
@ 2021-09-16  4:24   ` Benjamin Marzinski
  2021-09-16  9:34     ` Martin Wilck
  0 siblings, 1 reply; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16  4:24 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:41:19PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> The list of clients is never changed anywhere except in
> uxsock_listen(). No need to lock.
> 
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/uxlsnr.c | 21 ++-------------------
>  1 file changed, 2 insertions(+), 19 deletions(-)
> 
> diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
> index c18b2c4..7b763b6 100644
> --- a/multipathd/uxlsnr.c
> +++ b/multipathd/uxlsnr.c
> @@ -89,7 +89,6 @@ enum {
>  static __attribute__((unused)) char ___a[-(MIN_POLLS <= 0)];
>  
>  static LIST_HEAD(clients);
> -static pthread_mutex_t client_lock = PTHREAD_MUTEX_INITIALIZER;
>  static struct pollfd *polls;
>  static int notify_fd = -1;
>  static int idle_fd = -1;
> @@ -150,15 +149,13 @@ static void new_client(int ux_sock)
>  	c->is_root = _socket_client_is_root(c->fd);
>  
>  	/* put it in our linked list */
> -	pthread_mutex_lock(&client_lock);
>  	list_add_tail(&c->node, &clients);
> -	pthread_mutex_unlock(&client_lock);
>  }
>  
>  /*
>   * kill off a dead client
>   */
> -static void _dead_client(struct client *c)
> +static void dead_client(struct client *c)

There are also leading underscores and comments about the clients lock
for __get_soonest_timeout() and __need_lock(). We should probably remove
those as well.

-Ben

>  {
>  	int fd = c->fd;
>  	list_del_init(&c->node);
> @@ -170,14 +167,6 @@ static void _dead_client(struct client *c)
>  	close(fd);
>  }
>  
> -static void dead_client(struct client *c)
> -{
> -	pthread_cleanup_push(cleanup_mutex, &client_lock);
> -	pthread_mutex_lock(&client_lock);
> -	_dead_client(c);
> -	pthread_cleanup_pop(1);
> -}
> -
>  static void free_polls (void)
>  {
>  	if (polls)
> @@ -194,11 +183,9 @@ void uxsock_cleanup(void *arg)
>  	close(notify_fd);
>  	free(watch_config_dir);
>  
> -	pthread_mutex_lock(&client_lock);
>  	list_for_each_entry_safe(client_loop, client_tmp, &clients, node) {
> -		_dead_client(client_loop);
> +		dead_client(client_loop);
>  	}
> -	pthread_mutex_unlock(&client_lock);
>  
>  	cli_exit();
>  	free_polls();
> @@ -668,8 +655,6 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
>  		struct timespec __timeout, *timeout;
>  
>  		/* setup for a poll */
> -		pthread_mutex_lock(&client_lock);
> -		pthread_cleanup_push(cleanup_mutex, &client_lock);
>  		num_clients = 0;
>  		list_for_each_entry(c, &clients, node) {
>  			num_clients++;
> @@ -738,8 +723,6 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
>                  n_pfds = i;
>  		timeout = __get_soonest_timeout(&__timeout);
>  
> -		pthread_cleanup_pop(1);
> -
>  		/* most of our life is spent in this call */
>  		poll_count = ppoll(polls, n_pfds, timeout, &mask);
>  
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 35/35] multipathd: uxclt: allow client mode for non-root, too
  2021-09-10 11:41 ` [dm-devel] [PATCH 35/35] multipathd: uxclt: allow client mode for non-root, too mwilck
@ 2021-09-16  4:24   ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16  4:24 UTC (permalink / raw)
  To: mwilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Fri, Sep 10, 2021 at 01:41:20PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> The server checks for root permissions anyway. "multipathd -k"
> should work for ordinary users as long as no priviledged commands
> are executed.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/main.c | 10 +++++-----
>  1 file changed, 5 insertions(+), 5 deletions(-)
> 
> diff --git a/multipathd/main.c b/multipathd/main.c
> index ec4bcc3..ff5b600 100644
> --- a/multipathd/main.c
> +++ b/multipathd/main.c
> @@ -3329,11 +3329,6 @@ main (int argc, char *argv[])
>  
>  	logsink = LOGSINK_SYSLOG;
>  
> -	if (getuid() != 0) {
> -		fprintf(stderr, "need to be root\n");
> -		exit(1);
> -	}
> -
>  	/* make sure we don't lock any path */
>  	if (chdir("/") < 0)
>  		fprintf(stderr, "can't chdir to root directory : %s\n",
> @@ -3420,6 +3415,11 @@ main (int argc, char *argv[])
>  		return err;
>  	}
>  
> +	if (getuid() != 0) {
> +		fprintf(stderr, "need to be root\n");
> +		exit(1);
> +	}
> +
>  	if (foreground) {
>  		if (!isatty(fileno(stdout)))
>  			setbuf(stdout, NULL);
> -- 
> 2.33.0

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 05/35] libmultipath: improve cleanup of uevent queues on exit
  2021-09-15 22:20   ` Benjamin Marzinski
@ 2021-09-16  7:10     ` Martin Wilck
  2021-09-16 14:26       ` Benjamin Marzinski
  0 siblings, 1 reply; 89+ messages in thread
From: Martin Wilck @ 2021-09-16  7:10 UTC (permalink / raw)
  To: Benjamin Marzinski; +Cc: lixiaokeng, Chongyun Wu, dm-devel

On Wed, 2021-09-15 at 17:20 -0500, Benjamin Marzinski wrote:
> On Fri, Sep 10, 2021 at 01:40:50PM +0200, mwilck@suse.com wrote:
> > From: Martin Wilck <mwilck@suse.com>
> > 
> > uevents listed on merge_node must be cleaned up, too. uevents
> > cancelled while being serviced and temporary queues, likewise.
> > The global uevq must be cleaned out in the uevent listener thread,
> > because it might have added events after the dispatcher thread
> > had already finished.
> > 
> 
> There's nothing wrong with this, but for the global list, wouldn't it
> be
> easier to just wait till after cleanup_child() calls
> cleanup_threads(),
> and then call cleanup_global_uevq(). That way you know nothing else
> is
> running.

That would be possible. If I understand your proposal correctly, that
would require calling cleanup_global_uevq() from cleanup_child(), i.e.
from the main process. Currently uevq and the functions handling it are
static and only visible in uevent.c.

By taking the lock in cleanup_global_uevq() and calling it from the
listener on cleanup, I can be sure that the dispatcher won't see any
more uevents if it hasn't terminated yet, and that no no events will be
added to the queue after cleanup. So I think with this patch I can also
be certain that uevq is cleaned up for good, without a need to export
the cleanup function, and without enforcing a certain order of thread
shutdowns on exit. Do you disagree?

Thanks,
Martin



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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 07/35] multipathd: improve delayed reconfigure
  2021-09-15 23:00   ` Benjamin Marzinski
@ 2021-09-16  7:16     ` Martin Wilck
  0 siblings, 0 replies; 89+ messages in thread
From: Martin Wilck @ 2021-09-16  7:16 UTC (permalink / raw)
  To: Benjamin Marzinski; +Cc: lixiaokeng, Chongyun Wu, dm-devel

On Wed, 2021-09-15 at 18:00 -0500, Benjamin Marzinski wrote:
> On Fri, Sep 10, 2021 at 01:40:52PM +0200, mwilck@suse.com wrote:
> > From: Martin Wilck <mwilck@suse.com>
> > 
> > When a reconfigure operation is requested, either by the admin
> > or by some condition multipathd encounters, the current code
> > attempts to set DAEMON_CONFIGURE state and gives up after a second
> > if it doesn't succeed. Apart from shutdown, this happens only
> > if multipathd is either already reconfiguring, or busy in the
> > path checker loop.
> > 
> > This patch modifies the logic as follows: rather than waiting,
> > we set a flag that requests a reconfigure operation asap, i.e.
> > when the current operation is finished and the status switched
> > to DAEMON_IDLE. In this case, multipathd will not switch to IDLE
> > but start another reconfigure cycle.
> > 
> > This assumes that if a reconfigure is requested while one is
> > already
> > running, the admin has made some (additional) changes and wants
> > multipathd to pull them in. As we can't be sure that the currently
> > running reconfigure has seen the configuration changes, we need
> > to start over again.
> > 
> > A positive side effect is less waiting in clients and multipathd.
> > 
> > After this change, the only caller of set_config_state() is
> > checkerloop(). Waking up every second just to see that
> > DAEMON_RUNNING
> > couldn't be set makes no sense. Therefore set_config_state() is
> > changed to wait "forever", or until shutdown is requested. Unless
> > multipathd completely hangs, the wait will terminate sooner or
> > later.
> > 
> > Signed-off-by: Martin Wilck <mwilck@suse.com>
> > ---
> >  multipathd/cli_handlers.c | 10 +----
> >  multipathd/main.c         | 92 +++++++++++++++++++++++++++++------
> > ----
> >  multipathd/main.h         |  3 +-
> >  3 files changed, 71 insertions(+), 34 deletions(-)
> > 
> > diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
> > index 6d3a0ae..44f76ee 100644
> > 

> >                 condlog(2, "reset log (signal)");
> > diff --git a/multipathd/main.h b/multipathd/main.h
> > index bc1f938..23ce919 100644
> > --- a/multipathd/main.h
> > +++ b/multipathd/main.h
> > @@ -37,6 +37,7 @@ void exit_daemon(void);
> >  const char * daemon_status(void);
> >  enum daemon_status wait_for_state_change_if(enum daemon_status
> > oldstate,
> >                                             unsigned long ms);
> > +void schedule_reconfigure(void);
> >  int need_to_delay_reconfig (struct vectors *);
> >  int reconfigure (struct vectors *);
> >  int ev_add_path (struct path *, struct vectors *, int);
> > @@ -44,7 +45,7 @@ int ev_remove_path (struct path *, struct vectors
> > *, int);
> >  int ev_add_map (char *, const char *, struct vectors *);
> >  int ev_remove_map (char *, char *, int, struct vectors *);
> >  int flush_map(struct multipath *, struct vectors *, int);
> > -int set_config_state(enum daemon_status);
> > +enum daemon_status set_config_state(enum daemon_status);
> 
> Can't we just remove set_config_state from main.h, and make it
> static?
> Other than that, everything looks fine.

Good point, will do.

Thanks, Martin



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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 12/35] multipathd: add and set cli_handlers in a single step
  2021-09-16  0:01   ` Benjamin Marzinski
@ 2021-09-16  7:22     ` Martin Wilck
  2021-11-12 21:45     ` Martin Wilck
  1 sibling, 0 replies; 89+ messages in thread
From: Martin Wilck @ 2021-09-16  7:22 UTC (permalink / raw)
  To: Benjamin Marzinski; +Cc: lixiaokeng, Chongyun Wu, dm-devel

On Wed, 2021-09-15 at 19:01 -0500, Benjamin Marzinski wrote:
> On Fri, Sep 10, 2021 at 01:40:57PM +0200, mwilck@suse.com wrote:
> > From: Martin Wilck <mwilck@suse.com>
> > 
> > Modify set_handler_callback() such that a missing slot is created
> > if no matching slot is found. This way, we can skip the
> > initialization
> > with NULL handlers on startup.
> > 
> > Signed-off-by: Martin Wilck <mwilck@suse.com>
> > ---
> >  multipathd/cli.c | 85 +++++++-------------------------------------
> > ----
> >  multipathd/cli.h |  6 ++--
> >  2 files changed, 15 insertions(+), 76 deletions(-)
> > 
> > diff --git a/multipathd/cli.c b/multipathd/cli.c
> > index 5213813..7020d2b 100644
> > --- a/multipathd/cli.c
> > +++ b/multipathd/cli.c
> > @@ -100,26 +100,20 @@ find_handler (uint64_t fp)
> >  }
> >  
> >  int
> > -set_handler_callback (uint64_t fp, cli_handler *fn)
> > +__set_handler_callback (uint64_t fp, cli_handler *fn, bool locked)
> >  {
> > -       struct handler * h = find_handler(fp);
> > +       struct handler *h = find_handler(fp);
> >  
> 
> Wouldn't it be a bug if we reset the handler? Is this really
> something
> we need to check for? Also, if add_handler() just returned a pointer
> to
> the handler, we wouldn't need to call fail_handler() immediately
> after
> creating it.

That makes sense. I'll resubmit.

Regards,
Martin

> 
> -Ben
> 
> > -       if (!h)
> > +       if (!h) {
> > +               add_handler(fp, fn);
> > +               h = find_handler(fp);
> > +       }
> > +       if (!h) {
> > +               condlog(0, "%s: failed to set handler for code
> > %"PRIu64,
> > +                       __func__, fp);
> >                 return 1;
> > -       h->fn = fn;
> > -       h->locked = 1;
> > -       return 0;
> > -}
> > -
> > -int
> > -set_unlocked_handler_callback (uint64_t fp, cli_handler *fn)
> > -{
> > -       struct handler * h = find_handler(fp);
> > -
> > -       if (!h)
> > -               return 1;
> > -       h->fn = fn;
> > -       h->locked = 0;
> > +       }
> > +       h->locked = locked;
> >         return 0;
> >  }
> >  
> > @@ -513,63 +507,6 @@ cli_init (void) {
> >         if (alloc_handlers())
> >                 return 1;
> >  
> > -       add_handler(LIST+PATHS, NULL);
> > -       add_handler(LIST+PATHS+FMT, NULL);
> > -       add_handler(LIST+PATHS+RAW+FMT, NULL);
> > -       add_handler(LIST+PATH, NULL);
> > -       add_handler(LIST+STATUS, NULL);
> > -       add_handler(LIST+DAEMON, NULL);
> > -       add_handler(LIST+MAPS, NULL);
> > -       add_handler(LIST+MAPS+STATUS, NULL);
> > -       add_handler(LIST+MAPS+STATS, NULL);
> > -       add_handler(LIST+MAPS+FMT, NULL);
> > -       add_handler(LIST+MAPS+RAW+FMT, NULL);
> > -       add_handler(LIST+MAPS+TOPOLOGY, NULL);
> > -       add_handler(LIST+MAPS+JSON, NULL);
> > -       add_handler(LIST+TOPOLOGY, NULL);
> > -       add_handler(LIST+MAP+TOPOLOGY, NULL);
> > -       add_handler(LIST+MAP+JSON, NULL);
> > -       add_handler(LIST+MAP+FMT, NULL);
> > -       add_handler(LIST+MAP+RAW+FMT, NULL);
> > -       add_handler(LIST+CONFIG, NULL);
> > -       add_handler(LIST+CONFIG+LOCAL, NULL);
> > -       add_handler(LIST+BLACKLIST, NULL);
> > -       add_handler(LIST+DEVICES, NULL);
> > -       add_handler(LIST+WILDCARDS, NULL);
> > -       add_handler(RESET+MAPS+STATS, NULL);
> > -       add_handler(RESET+MAP+STATS, NULL);
> > -       add_handler(ADD+PATH, NULL);
> > -       add_handler(DEL+PATH, NULL);
> > -       add_handler(ADD+MAP, NULL);
> > -       add_handler(DEL+MAP, NULL);
> > -       add_handler(DEL+MAPS, NULL);
> > -       add_handler(SWITCH+MAP+GROUP, NULL);
> > -       add_handler(RECONFIGURE, NULL);
> > -       add_handler(SUSPEND+MAP, NULL);
> > -       add_handler(RESUME+MAP, NULL);
> > -       add_handler(RESIZE+MAP, NULL);
> > -       add_handler(RESET+MAP, NULL);
> > -       add_handler(RELOAD+MAP, NULL);
> > -       add_handler(DISABLEQ+MAP, NULL);
> > -       add_handler(RESTOREQ+MAP, NULL);
> > -       add_handler(DISABLEQ+MAPS, NULL);
> > -       add_handler(RESTOREQ+MAPS, NULL);
> > -       add_handler(REINSTATE+PATH, NULL);
> > -       add_handler(FAIL+PATH, NULL);
> > -       add_handler(QUIT, NULL);
> > -       add_handler(SHUTDOWN, NULL);
> > -       add_handler(GETPRSTATUS+MAP, NULL);
> > -       add_handler(SETPRSTATUS+MAP, NULL);
> > -       add_handler(UNSETPRSTATUS+MAP, NULL);
> > -       add_handler(GETPRKEY+MAP, NULL);
> > -       add_handler(SETPRKEY+MAP+KEY, NULL);
> > -       add_handler(UNSETPRKEY+MAP, NULL);
> > -       add_handler(FORCEQ+DAEMON, NULL);
> > -       add_handler(RESTOREQ+DAEMON, NULL);
> > -       add_handler(SETMARGINAL+PATH, NULL);
> > -       add_handler(UNSETMARGINAL+PATH, NULL);
> > -       add_handler(UNSETMARGINAL+MAP, NULL);
> > -
> >         return 0;
> >  }
> >  
> > diff --git a/multipathd/cli.h b/multipathd/cli.h
> > index 3dac1b4..dbb75be 100644
> > --- a/multipathd/cli.h
> > +++ b/multipathd/cli.h
> > @@ -134,8 +134,10 @@ struct handler {
> >  
> >  int alloc_handlers (void);
> >  int add_handler (uint64_t fp, cli_handler *fn);
> > -int set_handler_callback (uint64_t fp, cli_handler *fn);
> > -int set_unlocked_handler_callback (uint64_t fp, cli_handler *fn);
> > +int __set_handler_callback (uint64_t fp, cli_handler *fn, bool
> > locked);
> > +#define set_handler_callback(fp, fn) __set_handler_callback(fp,
> > fn, true)
> > +#define set_unlocked_handler_callback(fp, fn)
> > __set_handler_callback(fp, fn, false)
> > +
> >  int parse_cmd (char * cmd, char ** reply, int * len, void *, int);
> >  int load_keys (void);
> >  char * get_keyparam (vector v, uint64_t code);
> > -- 
> > 2.33.0
> 
> --
> dm-devel mailing list
> dm-devel@redhat.com
> https://listman.redhat.com/mailman/listinfo/dm-devel
> 



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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 14/35] multipathd: add "force_reconfigure" option
  2021-09-16  0:13   ` Benjamin Marzinski
@ 2021-09-16  7:34     ` Martin Wilck
  2021-09-16 14:32       ` Benjamin Marzinski
  0 siblings, 1 reply; 89+ messages in thread
From: Martin Wilck @ 2021-09-16  7:34 UTC (permalink / raw)
  To: Benjamin Marzinski; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Wed, 2021-09-15 at 19:13 -0500, Benjamin Marzinski wrote:
> On Fri, Sep 10, 2021 at 01:40:59PM +0200, mwilck@suse.com wrote:
> > From: Martin Wilck <mwilck@suse.com>
> > 
> > Since e3270f7 ("multipathd: use weaker "force_reload" at startup"),
> > (multipath-tools 0.7.0), we only reload those maps that must be
> > reloaded during startup. "multipath reconfigure", OTOH, reloads
> > every map, which may take a long time on systems with lots of
> > storage devices, as every DM_DEVICE_RELOAD ioctl involves a
> > suspend/resume cycle.
> > 
> > The logic we use during startup is actually very robust and catches
> > all cases in which a reload is necessary. "reconfigure" operations
> > are often done because of configuration changes, and usually don't
> > require a full reload of every map.
> > 
> > This patch changes the default behavior of "multipath reconfigure"
> > to "weak" reload, like we do on startup since e3270f7. The behavior
> > can be changed by setting the configuration option
> > "force_reconfigure yes" before starting the reconfigure operation.
> > "multipath -r" is also affected, but "multipath -D -r" is not.
> > 
> > It would have been nice to have introduced a new cli command
> > "reconfigure force" instead, but the way "reconfigure" is
> > implemented, that would have required a major rewrite of the code.
> 
> This looks o.k. But I don't think it would be that hard to add a new
> multipathd command to reconfigure all the devices.  My personal
> preference would be to leave force_reconfigure off by default, so that
> we keep the same behavior, and add a command to force a full reconfig.
> I'll try to work up a patch with my idea that can apply on top of this.

The problem I see is with the "delayed reconfigure" approach. We
notify child() about a pending reconfiguration by setting
DAEMON_RECONFIGURE. If clients can request different "types" of
reconfiguration, we would either need an additional flag, or a new
state (e.g. DAEMON_RECONFIGURE_FORCE) to indicate which type of
reconfiguration was requested. And then what to we do if while one
reconfigure is running, the admin runs both "reconfigure force" and
"reconfigure"? We can only schedule only one reconfigure operation. We
should probably assume "force" in that rather unlikely case.

IMO we should not implement _both_ a configuration option
"force_reconfigure" _and_ an additional command "reconfigure force". We
should decide which one we want. If you can come up with a reasonable
logic for the latter, I agree it's better.

Regards,
Martin



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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 30/35] multipathd: uxlsnr: merge uxsock_trigger() into state machine
  2021-09-16  3:32   ` Benjamin Marzinski
@ 2021-09-16  8:02     ` Martin Wilck
  2021-11-12 22:07     ` Martin Wilck
  1 sibling, 0 replies; 89+ messages in thread
From: Martin Wilck @ 2021-09-16  8:02 UTC (permalink / raw)
  To: Benjamin Marzinski; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Wed, 2021-09-15 at 22:32 -0500, Benjamin Marzinski wrote:
> On Fri, Sep 10, 2021 at 01:41:15PM +0200, mwilck@suse.com wrote:
> > From: Martin Wilck <mwilck@suse.com>
> > 
> > This patch sets up the bulk of the state machine. The idea is to
> > fall through the case labels as long as possible (when steps
> > succeed)
> > and return to the caller if either an error occurs, or it becomes
> > necessary to wait for some pollable condition.
> > 
> > While doing this, switch to negative error codes for the functions
> > in uxlsnr.c (e.g. parse_cmd()). Positive return codes are reserved
> > for the cli_handler functions themselves. This way we can clearly
> > distinguish the error source, and avoid confusion and misleading
> > error messages. No cli_handler returns negative values.
> > 
> > Note: with this patch applied, clients may hang and time out if
> > the handler fails to acquire the vecs lock. This will be fixed in
> > the
> > follow-up patch "multipathd: uxlsnr: add idle notification".
> > 
> > Signed-off-by: Martin Wilck <mwilck@suse.com>
> > ---
> >  multipathd/uxlsnr.c | 145 ++++++++++++++++++++++++----------------
> > ----
> >  1 file changed, 80 insertions(+), 65 deletions(-)
> > 
> > 

> > +               set_client_state(c, CLT_WAIT_LOCK);
> 
> I don't have strong feelings about this, but this state machine
> doesn't
> want to always fall through. sometimes, like if you get -EPERM, you
> want
> to swith from CLT_PARSE to CLT_SEND.  If instead of fallthroughs, you
> just put the switch statement in a loop, and simply returned when you
> wanted to break out to uxsock_listen, you could jump from any state
> to
> any other state, and wouldn't need to have code to skip the actions
> of
> some states, to enable the follow throughs. Just a thought.

Good point. I'll give it a shot and see how it plays out. The end
result will probably be better readable.

I'll also work on your other points.

Regards
Martin



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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 31/35] multipathd: uxlsnr: add idle notification
  2021-09-16  4:14   ` Benjamin Marzinski
@ 2021-09-16  8:54     ` Martin Wilck
  2021-09-16 15:06       ` Benjamin Marzinski
  0 siblings, 1 reply; 89+ messages in thread
From: Martin Wilck @ 2021-09-16  8:54 UTC (permalink / raw)
  To: Benjamin Marzinski; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Wed, 2021-09-15 at 23:14 -0500, Benjamin Marzinski wrote:
> On Fri, Sep 10, 2021 at 01:41:16PM +0200, mwilck@suse.com wrote:
> > From: Martin Wilck <mwilck@suse.com>
> > 
> > The previous patches added the state machine and the timeout
> > handling,
> > but there was no wakeup mechanism for the uxlsnr for cases where
> > client connections were waiting for the vecs lock.
> > 
> > This patch uses the previously introduced wakeup mechanism of
> > struct mutex_lock for this purpose. Processes which unlock the
> > "global" vecs lock send an event in an eventfd which the uxlsnr
> > loop is polling for.
> > 
> > As we are now woken up for servicing client handlers that don't
> > wait for input but for the lock, we need to set up the pollfds
> > differently, and iterate over all clients when handling events,
> > not only over the ones that are receiving. The hangup handling
> > is changed, too. We have to look at every client, even if one has
> > hung up. Note that I don't take client_lock for the loop in
> > uxsock_listen(), it's not necessary and will be removed elsewhere
> > in a follow-up patch.
> > 
> > With this in place, the lock need not be taken in execute_handler()
> > any more. The uxlsnr only ever calls trylock() on the vecs lock,
> > avoiding any waiting for other threads to finish.
> > 
> > Signed-off-by: Martin Wilck <mwilck@suse.com>
> > ---
> >  multipathd/uxlsnr.c | 211 ++++++++++++++++++++++++++++++------------
> > --
> >  1 file changed, 143 insertions(+), 68 deletions(-)
> > 

> 
> I do worry that if there are, for instance, a lot of uevents coming in,
> this could starve the uxlsnr thread, since other threads could be
> grabbing and releasing the vecs lock, but if it's usually being held,
> then the uxlsnr thread might never try to grab it when it's free, and
> it
> will keep losing its place in line. Also, every time that the vecs lock
> is dropped between ppoll() calls, a wakeup will get triggered, even if
> the lock was grabbed by something else before the ppoll thread runs.

I've thought about this too. It's true that the ppoll ->
pthread_mutex_trylock() sequence will never acquire the lock if some
other thread calls lock() at the same time.

If multiple processes call lock(), the "winner" of the lock is random.
Thus in a way this change actually adds some predictablity: the uxlsnr
will step back if some other process is actively trying to grab the
lock. IMO that the right thing to do in almost all situations.

We don't need to worry about "thundering herd" issues because the
number of threads that might wait on the lock is rather small. In the
worst case, 3 threads (checker, dmevents handler and uevent dispatcher,
plus the uxlsnr in ppoll()) wait for the lock at the same time. Usually
one of them will have it grabbed. On systems that lack dmevent polling,
the number of waiter threads may be higher, but AFAICS it's a very rare
condition to have hundreds of dmevents delivered to different maps
simultaneously, and if it happens, it's probably correct to have them
serviced quickly.

The uevent dispatcher doesn't hold the lock, it's taken and released
for every event handled. Thus uxlsnr has a real chance to jump in
between uevents. The same holds for the dmevents thread, it takes the
lock separately for every map affected. The only piece of code that
holds the lock for an extended period of time (except reconfigure(),
where it's unavoidable) is the path checker (that's bad, and next on
the todo list).

The really "important" commands (shutdown, reconfigure) don't take the
lock and return immediately; the lock is no issue for them. I don't see
any other cli command that needs to be served before uevents or dm
events.

I haven't been able to test this on huge configurations with 1000s of
LUNs, but I tested with artificial delays in checker loop, uevent
handlers, and dmevent handler, and lots of clients querying the daemon
in parallel, and saw that clients were handled very nicely. Some
timeouts are inevitable (e.g. if the checker simply holds the lock
longer than the uxsock_timeout), but that is no regression.

Bottom line: I believe that because this patch reduces the busy-wait
time, clients will be served more reliably and more quickly than before
(more precisely: both average and standard deviation of the service
delay will be improved wrt before, and timeouts occur less frequently).
I encourage everyone to experiment and see if reality shows that I'm
wrong.

> I suppose the only way to deal with that would be to move the locking
> commands to a list handled by a separate thread, so that it could block
> without stalling the non-locking commands.

Not sure if I understand correctly, just in case: non-locking commands
are never stalled with my patch.

Introducing a new thread is something I wouldn't want to do. We can
discuss adding ticketing to the lock, but I am not convinced that it'd
be worth the effort, and I'm not even sure it's what we want (we'd need
to make sure the uevent dispatcher isn't starved, which would be worse
than starved clients). If you want ticketing, I'd like to put it in a
patch on top rather than merging it into this one (it would need very
careful engineering and take time).

> 
> > +                       if (!pl.held) {
> > +                               condlog(4, "%s: cli[%d] waiting for
> > lock",
> > +                                       __func__, c->fd);
> > +                               return;
> > +                       } else
> > +                               condlog(4, "%s: cli[%d] grabbed
> > lock",
> > +                                       __func__, c->fd);
> > +               }
> >                 set_client_state(c, CLT_WORK);
> >                 /* fallthrough */
> >  
> 
> We should never return to uxsock_listen() while the lock is held. The
> code doesn't, but the fact that CLT_WORK is a separate state makes it
> look like this could be possible.  Since we must never be in CLT_WORK
> without first being in CLT_WAIT_LOCK, I don't see any point for having
> a
> separate CLT_WAIT_LOCK state.  CLT_WORK should do both the locking if
> necessary, and calling the handler.

The reason for adding this state was that non-locking commands switch
to CLT_WORK immediately, and locking commands don't. But it's true that
this state is never visible outside the state machine, so we can drop
it. Actually the state that's never visible is CLT_WORK, because it
switches to CLT_SEND immediately. I'll think about it and drop one.

Regards
Martin



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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 32/35] multipathd: uxlsnr: add timeout handling
  2021-09-16  4:17   ` Benjamin Marzinski
@ 2021-09-16  8:58     ` Martin Wilck
  2021-09-16 15:08       ` Benjamin Marzinski
  0 siblings, 1 reply; 89+ messages in thread
From: Martin Wilck @ 2021-09-16  8:58 UTC (permalink / raw)
  To: Benjamin Marzinski; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Wed, 2021-09-15 at 23:17 -0500, Benjamin Marzinski wrote:
> On Fri, Sep 10, 2021 at 01:41:17PM +0200, mwilck@suse.com wrote:
> > From: Martin Wilck <mwilck@suse.com>
> > 
> > Our ppoll() call needs to wake up when a client request times out.
> > This logic can be added by determining the first client that's
> > about
> > to time out. The logic in handle_client() will then cause a timeout
> > reply to be sent to the client. This is more client-friendly
> > as the client timing out without receiving a reply.
> > 
> > Signed-off-by: Martin Wilck <mwilck@suse.com>
> > ---
> >  multipathd/uxlsnr.c | 58
> > +++++++++++++++++++++++++++++++++++++++++----
> >  1 file changed, 53 insertions(+), 5 deletions(-)
> > 
> >  
> > @@ -594,6 +643,7 @@ void *uxsock_listen(long ux_sock, void
> > *trigger_data)
> >         while (1) {
> >                 struct client *c, *tmp;
> >                 int i, n_pfds, poll_count, num_clients;
> > +               struct timespec __timeout, *timeout;
> 
> Maybe it's just too late to be looking at code, but I'm missing why
> we
> need a separate variable that it a pointer to __timeout.

This way __get_soonest_timeout() can return either NULL or &__timeout,
and we can simply pass the return value to ppoll().

Martin



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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 33/35] multipathd: uxlsnr: use poll loop for sending, too
  2021-09-16  4:22   ` Benjamin Marzinski
@ 2021-09-16  9:33     ` Martin Wilck
  2021-09-16 15:26       ` Benjamin Marzinski
  0 siblings, 1 reply; 89+ messages in thread
From: Martin Wilck @ 2021-09-16  9:33 UTC (permalink / raw)
  To: Benjamin Marzinski; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Wed, 2021-09-15 at 23:22 -0500, Benjamin Marzinski wrote:
> On Fri, Sep 10, 2021 at 01:41:18PM +0200, mwilck@suse.com wrote:
> > From: Martin Wilck <mwilck@suse.com>
> > 
> > send_packet() may busy-loop. By polling for POLLOUT, we can
> > avoid that, even if it's very unlikely in practice.
> > 
> > Signed-off-by: Martin Wilck <mwilck@suse.com>
> > ---
> >  multipathd/uxlsnr.c | 39 ++++++++++++++++++++++++++++++++-------
> >  1 file changed, 32 insertions(+), 7 deletions(-)
> > 
> > diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
> > index 1bf4126..c18b2c4 100644
> > --- a/multipathd/uxlsnr.c
> > +++ b/multipathd/uxlsnr.c
> > @@ -588,15 +588,37 @@ static void handle_client(struct client *c,
> > struct vectors *vecs, short revents)
> >                 if (get_strbuf_len(&c->reply) == 0)
> >                         default_reply(c, c->error);
> >  
> > -               const char *buf = get_strbuf_str(&c->reply);
> > +               if (c->cmd_len == 0) {
> > +                       size_t len = get_strbuf_len(&c->reply) + 1;
> >  
> > -               if (send_packet(c->fd, buf) != 0)
> > -                       dead_client(c);
> > -               else
> > -                       condlog(4, "cli[%d]: Reply [%zu bytes]", c-
> > >fd,
> > -                               get_strbuf_len(&c->reply) + 1);
> > -               reset_strbuf(&c->reply);
> > +                       if (send(c->fd, &len, sizeof(len),
> > MSG_NOSIGNAL)
> > +                           != sizeof(len)) {
> 
> This assumes that failing to send the size is always an error. What
> about if we get EINTR/EAGAIN? Also, it seems pretty likely that we will
> either send all of the size or none of it, but I'm not sure we can
> guarantee that.  send_packet() handled partitial writes of the length.
> Actually, mpath_recv_reply_len() which is still used by CLT_RECV still
> uses read_all(), instead of just polling again on partial reads.

That was intentional. I couldn't imagine that reading or writing 8
bytes would block (after all, we received POLLIN / POLLOUT for the
socket). Note the minimum socket buffer size that the kernel uses is
larger than 2048 bytes.

https://elixir.bootlin.com/linux/latest/source/include/net/sock.h#L2339

When we send the size value, the send buffer is empty by definition
(even for interactive sessions, if the client uses libmpathcmd, it will
have fetched the response before sending new requests). Likewise for
receiving, the size will be received in one piece if the client uses
libmpathcmd.

I take it that some malicious or badly designed client could access the
socket bypassing libmpathcmd, sending or receiving the size byte-by-
byte, or in other strange patterns. But I think we are within our
rights to error out in such cases; it may actually be the right thing
to do that, as we don't really want to service clients doing tricks
like that. I will do some tests.

> Also, the fd is not set to be non-blocking. and if we fall through to
> CLT_SEND, we haven't checked for a POLLOUT revent, so technically, I
> believe the write could block here.

Right, we have to do that. Thanks for pointing it out. This also means
that polling in read_all() and send_packet() was pointless in the first
place ...

Thanks!
Martin

PS: I was wondering why we haven't been using SOCK_DGRAM for the
multipath socket in the first place. It would have avoided all this
hassle...



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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 34/35] multipathd: uxlsnr: drop client_lock
  2021-09-16  4:24   ` Benjamin Marzinski
@ 2021-09-16  9:34     ` Martin Wilck
  0 siblings, 0 replies; 89+ messages in thread
From: Martin Wilck @ 2021-09-16  9:34 UTC (permalink / raw)
  To: Benjamin Marzinski; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Wed, 2021-09-15 at 23:24 -0500, Benjamin Marzinski wrote:
> >  
> >  /*
> >   * kill off a dead client
> >   */
> > -static void _dead_client(struct client *c)
> > +static void dead_client(struct client *c)
> 
> There are also leading underscores and comments about the clients
> lock
> for __get_soonest_timeout() and __need_lock(). We should probably
> remove
> those as well.

Ok,

Martin





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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 05/35] libmultipath: improve cleanup of uevent queues on exit
  2021-09-16  7:10     ` Martin Wilck
@ 2021-09-16 14:26       ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16 14:26 UTC (permalink / raw)
  To: Martin Wilck; +Cc: lixiaokeng, Chongyun Wu, dm-devel

On Thu, Sep 16, 2021 at 09:10:57AM +0200, Martin Wilck wrote:
> On Wed, 2021-09-15 at 17:20 -0500, Benjamin Marzinski wrote:
> > On Fri, Sep 10, 2021 at 01:40:50PM +0200, mwilck@suse.com wrote:
> > > From: Martin Wilck <mwilck@suse.com>
> > > 
> > > uevents listed on merge_node must be cleaned up, too. uevents
> > > cancelled while being serviced and temporary queues, likewise.
> > > The global uevq must be cleaned out in the uevent listener thread,
> > > because it might have added events after the dispatcher thread
> > > had already finished.
> > > 
> > 
> > There's nothing wrong with this, but for the global list, wouldn't it
> > be
> > easier to just wait till after cleanup_child() calls
> > cleanup_threads(),
> > and then call cleanup_global_uevq(). That way you know nothing else
> > is
> > running.
> 
> That would be possible. If I understand your proposal correctly, that
> would require calling cleanup_global_uevq() from cleanup_child(), i.e.
> from the main process. Currently uevq and the functions handling it are
> static and only visible in uevent.c.
> 
> By taking the lock in cleanup_global_uevq() and calling it from the
> listener on cleanup, I can be sure that the dispatcher won't see any
> more uevents if it hasn't terminated yet, and that no no events will be
> added to the queue after cleanup. So I think with this patch I can also
> be certain that uevq is cleaned up for good, without a need to export
> the cleanup function, and without enforcing a certain order of thread
> shutdowns on exit. Do you disagree?

Yeah, that's why I said that there's nothing wrong with the patch. It
just seemed more self-evidently correct to do it after stopping all the
threads. But, regardless

Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>

> 
> Thanks,
> Martin
> 

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 14/35] multipathd: add "force_reconfigure" option
  2021-09-16  7:34     ` Martin Wilck
@ 2021-09-16 14:32       ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16 14:32 UTC (permalink / raw)
  To: Martin Wilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Thu, Sep 16, 2021 at 09:34:46AM +0200, Martin Wilck wrote:
> On Wed, 2021-09-15 at 19:13 -0500, Benjamin Marzinski wrote:
> > On Fri, Sep 10, 2021 at 01:40:59PM +0200, mwilck@suse.com wrote:
> > > From: Martin Wilck <mwilck@suse.com>
> > > 
> > > Since e3270f7 ("multipathd: use weaker "force_reload" at startup"),
> > > (multipath-tools 0.7.0), we only reload those maps that must be
> > > reloaded during startup. "multipath reconfigure", OTOH, reloads
> > > every map, which may take a long time on systems with lots of
> > > storage devices, as every DM_DEVICE_RELOAD ioctl involves a
> > > suspend/resume cycle.
> > > 
> > > The logic we use during startup is actually very robust and catches
> > > all cases in which a reload is necessary. "reconfigure" operations
> > > are often done because of configuration changes, and usually don't
> > > require a full reload of every map.
> > > 
> > > This patch changes the default behavior of "multipath reconfigure"
> > > to "weak" reload, like we do on startup since e3270f7. The behavior
> > > can be changed by setting the configuration option
> > > "force_reconfigure yes" before starting the reconfigure operation.
> > > "multipath -r" is also affected, but "multipath -D -r" is not.
> > > 
> > > It would have been nice to have introduced a new cli command
> > > "reconfigure force" instead, but the way "reconfigure" is
> > > implemented, that would have required a major rewrite of the code.
> > 
> > This looks o.k. But I don't think it would be that hard to add a new
> > multipathd command to reconfigure all the devices.  My personal
> > preference would be to leave force_reconfigure off by default, so that
> > we keep the same behavior, and add a command to force a full reconfig.
> > I'll try to work up a patch with my idea that can apply on top of this.
> 
> The problem I see is with the "delayed reconfigure" approach. We
> notify child() about a pending reconfiguration by setting
> DAEMON_RECONFIGURE. If clients can request different "types" of
> reconfiguration, we would either need an additional flag, or a new
> state (e.g. DAEMON_RECONFIGURE_FORCE) to indicate which type of
> reconfiguration was requested. And then what to we do if while one
> reconfigure is running, the admin runs both "reconfigure force" and
> "reconfigure"? We can only schedule only one reconfigure operation. We
> should probably assume "force" in that rather unlikely case.

Yeah, that was the plan.

> IMO we should not implement _both_ a configuration option
> "force_reconfigure" _and_ an additional command "reconfigure force". We
> should decide which one we want. If you can come up with a reasonable
> logic for the latter, I agree it's better.

I'm fine with that. I'll send a patch to replace this one, then.

-Ben

> Regards,
> Martin
> 

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 31/35] multipathd: uxlsnr: add idle notification
  2021-09-16  8:54     ` Martin Wilck
@ 2021-09-16 15:06       ` Benjamin Marzinski
  2021-09-16 15:54         ` Martin Wilck
  0 siblings, 1 reply; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16 15:06 UTC (permalink / raw)
  To: Martin Wilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Thu, Sep 16, 2021 at 10:54:19AM +0200, Martin Wilck wrote:
> On Wed, 2021-09-15 at 23:14 -0500, Benjamin Marzinski wrote:
> > On Fri, Sep 10, 2021 at 01:41:16PM +0200, mwilck@suse.com wrote:
> > > From: Martin Wilck <mwilck@suse.com>
> > > 
> > > The previous patches added the state machine and the timeout
> > > handling,
> > > but there was no wakeup mechanism for the uxlsnr for cases where
> > > client connections were waiting for the vecs lock.
> > > 
> > > This patch uses the previously introduced wakeup mechanism of
> > > struct mutex_lock for this purpose. Processes which unlock the
> > > "global" vecs lock send an event in an eventfd which the uxlsnr
> > > loop is polling for.
> > > 
> > > As we are now woken up for servicing client handlers that don't
> > > wait for input but for the lock, we need to set up the pollfds
> > > differently, and iterate over all clients when handling events,
> > > not only over the ones that are receiving. The hangup handling
> > > is changed, too. We have to look at every client, even if one has
> > > hung up. Note that I don't take client_lock for the loop in
> > > uxsock_listen(), it's not necessary and will be removed elsewhere
> > > in a follow-up patch.
> > > 
> > > With this in place, the lock need not be taken in execute_handler()
> > > any more. The uxlsnr only ever calls trylock() on the vecs lock,
> > > avoiding any waiting for other threads to finish.
> > > 
> > > Signed-off-by: Martin Wilck <mwilck@suse.com>
> > > ---
> > >  multipathd/uxlsnr.c | 211 ++++++++++++++++++++++++++++++------------
> > > --
> > >  1 file changed, 143 insertions(+), 68 deletions(-)
> > > 
> 
> > 
> > I do worry that if there are, for instance, a lot of uevents coming in,
> > this could starve the uxlsnr thread, since other threads could be
> > grabbing and releasing the vecs lock, but if it's usually being held,
> > then the uxlsnr thread might never try to grab it when it's free, and
> > it
> > will keep losing its place in line. Also, every time that the vecs lock
> > is dropped between ppoll() calls, a wakeup will get triggered, even if
> > the lock was grabbed by something else before the ppoll thread runs.
> 
> I've thought about this too. It's true that the ppoll ->
> pthread_mutex_trylock() sequence will never acquire the lock if some
> other thread calls lock() at the same time.
> 
> If multiple processes call lock(), the "winner" of the lock is random.
> Thus in a way this change actually adds some predictablity: the uxlsnr
> will step back if some other process is actively trying to grab the
> lock. IMO that the right thing to do in almost all situations.
> 
> We don't need to worry about "thundering herd" issues because the
> number of threads that might wait on the lock is rather small. In the
> worst case, 3 threads (checker, dmevents handler and uevent dispatcher,
> plus the uxlsnr in ppoll()) wait for the lock at the same time. Usually
> one of them will have it grabbed. On systems that lack dmevent polling,
> the number of waiter threads may be higher, but AFAICS it's a very rare
> condition to have hundreds of dmevents delivered to different maps
> simultaneously, and if it happens, it's probably correct to have them
> serviced quickly.
> 
> The uevent dispatcher doesn't hold the lock, it's taken and released
> for every event handled. Thus uxlsnr has a real chance to jump in
> between uevents. The same holds for the dmevents thread, it takes the
> lock separately for every map affected. The only piece of code that
> holds the lock for an extended period of time (except reconfigure(),
> where it's unavoidable) is the path checker (that's bad, and next on
> the todo list).
> 
> The really "important" commands (shutdown, reconfigure) don't take the
> lock and return immediately; the lock is no issue for them. I don't see
> any other cli command that needs to be served before uevents or dm
> events.
> 
> I haven't been able to test this on huge configurations with 1000s of
> LUNs, but I tested with artificial delays in checker loop, uevent
> handlers, and dmevent handler, and lots of clients querying the daemon
> in parallel, and saw that clients were handled very nicely. Some
> timeouts are inevitable (e.g. if the checker simply holds the lock
> longer than the uxsock_timeout), but that is no regression.
> 
> Bottom line: I believe that because this patch reduces the busy-wait
> time, clients will be served more reliably and more quickly than before
> (more precisely: both average and standard deviation of the service
> delay will be improved wrt before, and timeouts occur less frequently).
> I encourage everyone to experiment and see if reality shows that I'm
> wrong.
> 
> > I suppose the only way to deal with that would be to move the locking
> > commands to a list handled by a separate thread, so that it could block
> > without stalling the non-locking commands.
> 
> Not sure if I understand correctly, just in case: non-locking commands
> are never stalled with my patch.

I realize. I was saying that you could avoid starvation while still
allowing non-locking commands to complete by moving the locking commands
to a seperate thread, which did block on the lock. I didn't consider a
ticketing system. Ideally, the checker loop would have the lowest
priority, Since it isn't responding to any event, and ususally is just
verifiying that nothing has changed.  But you do make a good point that
when we are getting a lot of events, and the uxlsnr loop has a chance of
getting starved, we probably want to prioritize the event handling
anyway.

-Ben

> 
> Introducing a new thread is something I wouldn't want to do. We can
> discuss adding ticketing to the lock, but I am not convinced that it'd
> be worth the effort, and I'm not even sure it's what we want (we'd need
> to make sure the uevent dispatcher isn't starved, which would be worse
> than starved clients). If you want ticketing, I'd like to put it in a
> patch on top rather than merging it into this one (it would need very
> careful engineering and take time).
> 
> > 
> > > +                       if (!pl.held) {
> > > +                               condlog(4, "%s: cli[%d] waiting for
> > > lock",
> > > +                                       __func__, c->fd);
> > > +                               return;
> > > +                       } else
> > > +                               condlog(4, "%s: cli[%d] grabbed
> > > lock",
> > > +                                       __func__, c->fd);
> > > +               }
> > >                 set_client_state(c, CLT_WORK);
> > >                 /* fallthrough */
> > >  
> > 
> > We should never return to uxsock_listen() while the lock is held. The
> > code doesn't, but the fact that CLT_WORK is a separate state makes it
> > look like this could be possible.  Since we must never be in CLT_WORK
> > without first being in CLT_WAIT_LOCK, I don't see any point for having
> > a
> > separate CLT_WAIT_LOCK state.  CLT_WORK should do both the locking if
> > necessary, and calling the handler.
> 
> The reason for adding this state was that non-locking commands switch
> to CLT_WORK immediately, and locking commands don't. But it's true that
> this state is never visible outside the state machine, so we can drop
> it. Actually the state that's never visible is CLT_WORK, because it
> switches to CLT_SEND immediately. I'll think about it and drop one.
> 
> Regards
> Martin
> 

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 32/35] multipathd: uxlsnr: add timeout handling
  2021-09-16  8:58     ` Martin Wilck
@ 2021-09-16 15:08       ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16 15:08 UTC (permalink / raw)
  To: Martin Wilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Thu, Sep 16, 2021 at 10:58:36AM +0200, Martin Wilck wrote:
> On Wed, 2021-09-15 at 23:17 -0500, Benjamin Marzinski wrote:
> > On Fri, Sep 10, 2021 at 01:41:17PM +0200, mwilck@suse.com wrote:
> > > From: Martin Wilck <mwilck@suse.com>
> > > 
> > > Our ppoll() call needs to wake up when a client request times out.
> > > This logic can be added by determining the first client that's
> > > about
> > > to time out. The logic in handle_client() will then cause a timeout
> > > reply to be sent to the client. This is more client-friendly
> > > as the client timing out without receiving a reply.
> > > 
> > > Signed-off-by: Martin Wilck <mwilck@suse.com>
> > > ---
> > >  multipathd/uxlsnr.c | 58
> > > +++++++++++++++++++++++++++++++++++++++++----
> > >  1 file changed, 53 insertions(+), 5 deletions(-)
> > > 
> > >  
> > > @@ -594,6 +643,7 @@ void *uxsock_listen(long ux_sock, void
> > > *trigger_data)
> > >         while (1) {
> > >                 struct client *c, *tmp;
> > >                 int i, n_pfds, poll_count, num_clients;
> > > +               struct timespec __timeout, *timeout;
> > 
> > Maybe it's just too late to be looking at code, but I'm missing why
> > we
> > need a separate variable that it a pointer to __timeout.
> 
> This way __get_soonest_timeout() can return either NULL or &__timeout,
> and we can simply pass the return value to ppoll().

Ah. Yep. Too late to be reviewing code.

-Ben

> 
> Martin
> 

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 33/35] multipathd: uxlsnr: use poll loop for sending, too
  2021-09-16  9:33     ` Martin Wilck
@ 2021-09-16 15:26       ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16 15:26 UTC (permalink / raw)
  To: Martin Wilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Thu, Sep 16, 2021 at 11:33:29AM +0200, Martin Wilck wrote:
> On Wed, 2021-09-15 at 23:22 -0500, Benjamin Marzinski wrote:
> > On Fri, Sep 10, 2021 at 01:41:18PM +0200, mwilck@suse.com wrote:
> > > From: Martin Wilck <mwilck@suse.com>
> > > 
> > > send_packet() may busy-loop. By polling for POLLOUT, we can
> > > avoid that, even if it's very unlikely in practice.
> > > 
> > > Signed-off-by: Martin Wilck <mwilck@suse.com>
> > > ---
> > >  multipathd/uxlsnr.c | 39 ++++++++++++++++++++++++++++++++-------
> > >  1 file changed, 32 insertions(+), 7 deletions(-)
> > > 
> > > diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
> > > index 1bf4126..c18b2c4 100644
> > > --- a/multipathd/uxlsnr.c
> > > +++ b/multipathd/uxlsnr.c
> > > @@ -588,15 +588,37 @@ static void handle_client(struct client *c,
> > > struct vectors *vecs, short revents)
> > >                 if (get_strbuf_len(&c->reply) == 0)
> > >                         default_reply(c, c->error);
> > >  
> > > -               const char *buf = get_strbuf_str(&c->reply);
> > > +               if (c->cmd_len == 0) {
> > > +                       size_t len = get_strbuf_len(&c->reply) + 1;
> > >  
> > > -               if (send_packet(c->fd, buf) != 0)
> > > -                       dead_client(c);
> > > -               else
> > > -                       condlog(4, "cli[%d]: Reply [%zu bytes]", c-
> > > >fd,
> > > -                               get_strbuf_len(&c->reply) + 1);
> > > -               reset_strbuf(&c->reply);
> > > +                       if (send(c->fd, &len, sizeof(len),
> > > MSG_NOSIGNAL)
> > > +                           != sizeof(len)) {
> > 
> > This assumes that failing to send the size is always an error. What
> > about if we get EINTR/EAGAIN? Also, it seems pretty likely that we will
> > either send all of the size or none of it, but I'm not sure we can
> > guarantee that.  send_packet() handled partitial writes of the length.
> > Actually, mpath_recv_reply_len() which is still used by CLT_RECV still
> > uses read_all(), instead of just polling again on partial reads.
> 
> That was intentional. I couldn't imagine that reading or writing 8
> bytes would block (after all, we received POLLIN / POLLOUT for the
> socket). Note the minimum socket buffer size that the kernel uses is
> larger than 2048 bytes.
> 
> https://elixir.bootlin.com/linux/latest/source/include/net/sock.h#L2339
> 
> When we send the size value, the send buffer is empty by definition
> (even for interactive sessions, if the client uses libmpathcmd, it will
> have fetched the response before sending new requests). Likewise for
> receiving, the size will be received in one piece if the client uses
> libmpathcmd.
> 
> I take it that some malicious or badly designed client could access the
> socket bypassing libmpathcmd, sending or receiving the size byte-by-
> byte, or in other strange patterns. But I think we are within our
> rights to error out in such cases; it may actually be the right thing
> to do that, as we don't really want to service clients doing tricks
> like that. I will do some tests.

In this case, calling mpath_recv_reply_len() is probably overkill.
 
> > Also, the fd is not set to be non-blocking. and if we fall through to
> > CLT_SEND, we haven't checked for a POLLOUT revent, so technically, I
> > believe the write could block here.
> 
> Right, we have to do that. Thanks for pointing it out. This also means
> that polling in read_all() and send_packet() was pointless in the first
> place ...
> 
> Thanks!
> Martin
> 
> PS: I was wondering why we haven't been using SOCK_DGRAM for the
> multipath socket in the first place. It would have avoided all this
> hassle...

That design decision predates me working on the code.

-Ben

> 

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 31/35] multipathd: uxlsnr: add idle notification
  2021-09-16 15:06       ` Benjamin Marzinski
@ 2021-09-16 15:54         ` Martin Wilck
  2021-09-16 16:10           ` Benjamin Marzinski
  0 siblings, 1 reply; 89+ messages in thread
From: Martin Wilck @ 2021-09-16 15:54 UTC (permalink / raw)
  To: Benjamin Marzinski; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Thu, 2021-09-16 at 10:06 -0500, Benjamin Marzinski wrote:
> On Thu, Sep 16, 2021 at 10:54:19AM +0200, Martin Wilck wrote:
> > On Wed, 2021-09-15 at 23:14 -0500, Benjamin Marzinski wrote:
> > > On Fri, Sep 10, 2021 at 01:41:16PM +0200, mwilck@suse.com wrote:
> > > > From: Martin Wilck <mwilck@suse.com>
> > > > 
> > > > The previous patches added the state machine and the timeout
> > > > handling,
> > > > but there was no wakeup mechanism for the uxlsnr for cases
> > > > where
> > > > client connections were waiting for the vecs lock.
> > > > 
> > > > This patch uses the previously introduced wakeup mechanism of
> > > > struct mutex_lock for this purpose. Processes which unlock the
> > > > "global" vecs lock send an event in an eventfd which the uxlsnr
> > > > loop is polling for.
> > > > 
> > > > As we are now woken up for servicing client handlers that don't
> > > > wait for input but for the lock, we need to set up the pollfds
> > > > differently, and iterate over all clients when handling events,
> > > > not only over the ones that are receiving. The hangup handling
> > > > is changed, too. We have to look at every client, even if one
> > > > has
> > > > hung up. Note that I don't take client_lock for the loop in
> > > > uxsock_listen(), it's not necessary and will be removed
> > > > elsewhere
> > > > in a follow-up patch.
> > > > 
> > > > With this in place, the lock need not be taken in
> > > > execute_handler()
> > > > any more. The uxlsnr only ever calls trylock() on the vecs
> > > > lock,
> > > > avoiding any waiting for other threads to finish.
> > > > 
> > > > Signed-off-by: Martin Wilck <mwilck@suse.com>
> > > > ---
> > > >  multipathd/uxlsnr.c | 211 ++++++++++++++++++++++++++++++------
> > > > ------
> > > > --
> > > >  1 file changed, 143 insertions(+), 68 deletions(-)
> > > > 
> > 
> > > 
> > > I do worry that if there are, for instance, a lot of uevents
> > > coming in,
> > > this could starve the uxlsnr thread, since other threads could be
> > > grabbing and releasing the vecs lock, but if it's usually being
> > > held,
> > > then the uxlsnr thread might never try to grab it when it's free,
> > > and
> > > it
> > > will keep losing its place in line. Also, every time that the
> > > vecs lock
> > > is dropped between ppoll() calls, a wakeup will get triggered,
> > > even if
> > > the lock was grabbed by something else before the ppoll thread
> > > runs.
> > 
> > I've thought about this too. It's true that the ppoll ->
> > pthread_mutex_trylock() sequence will never acquire the lock if
> > some
> > other thread calls lock() at the same time.
> > 
> > If multiple processes call lock(), the "winner" of the lock is
> > random.
> > Thus in a way this change actually adds some predictablity: the
> > uxlsnr
> > will step back if some other process is actively trying to grab the
> > lock. IMO that the right thing to do in almost all situations.
> > 
> > We don't need to worry about "thundering herd" issues because the
> > number of threads that might wait on the lock is rather small. In
> > the
> > worst case, 3 threads (checker, dmevents handler and uevent
> > dispatcher,
> > plus the uxlsnr in ppoll()) wait for the lock at the same time.
> > Usually
> > one of them will have it grabbed. On systems that lack dmevent
> > polling,
> > the number of waiter threads may be higher, but AFAICS it's a very
> > rare
> > condition to have hundreds of dmevents delivered to different maps
> > simultaneously, and if it happens, it's probably correct to have
> > them
> > serviced quickly.
> > 
> > The uevent dispatcher doesn't hold the lock, it's taken and
> > released
> > for every event handled. Thus uxlsnr has a real chance to jump in
> > between uevents. The same holds for the dmevents thread, it takes
> > the
> > lock separately for every map affected. The only piece of code that
> > holds the lock for an extended period of time (except
> > reconfigure(),
> > where it's unavoidable) is the path checker (that's bad, and next
> > on
> > the todo list).
> > 
> > The really "important" commands (shutdown, reconfigure) don't take
> > the
> > lock and return immediately; the lock is no issue for them. I don't
> > see
> > any other cli command that needs to be served before uevents or dm
> > events.
> > 
> > I haven't been able to test this on huge configurations with 1000s
> > of
> > LUNs, but I tested with artificial delays in checker loop, uevent
> > handlers, and dmevent handler, and lots of clients querying the
> > daemon
> > in parallel, and saw that clients were handled very nicely. Some
> > timeouts are inevitable (e.g. if the checker simply holds the lock
> > longer than the uxsock_timeout), but that is no regression.
> > 
> > Bottom line: I believe that because this patch reduces the busy-
> > wait
> > time, clients will be served more reliably and more quickly than
> > before
> > (more precisely: both average and standard deviation of the service
> > delay will be improved wrt before, and timeouts occur less
> > frequently).
> > I encourage everyone to experiment and see if reality shows that
> > I'm
> > wrong.
> > 
> > > I suppose the only way to deal with that would be to move the
> > > locking
> > > commands to a list handled by a separate thread, so that it could
> > > block
> > > without stalling the non-locking commands.
> > 
> > Not sure if I understand correctly, just in case: non-locking
> > commands
> > are never stalled with my patch.
> 
> I realize. I was saying that you could avoid starvation while still
> allowing non-locking commands to complete by moving the locking
> commands
> to a seperate thread, which did block on the lock. I didn't consider
> a
> ticketing system. Ideally, the checker loop would have the lowest
> priority, Since it isn't responding to any event, and ususally is
> just
> verifiying that nothing has changed.  But you do make a good point
> that
> when we are getting a lot of events, and the uxlsnr loop has a chance
> of
> getting starved, we probably want to prioritize the event handling
> anyway.
> 

I have also thought about using additional threads for handling cli
commands. One could either use a single thread, similar to the udev
listener/dispatcher pair (your suggestion IIUC), or one thread per
(blocking) client.

Moving client handling into separate thread(s) avoids the complexity of
the state machine and the eventfd-based wakeup. But on the back side,
it introduces new multithreading-related complexity (of which we
already have our fair share). Client tasks running lock(&vecs->lock) in
order to serve commands like "multipathd show paths" might now starve
event handling, which would be worse than vice versa, IMO.

Eventually, I found the idea of the poll/wakeup loop with no additional
threads more appealing, and more suitable for the task. But I admit
that it's a matter of personal taste. I tend to try to use pthreads as
little as possible ;-).

So how do we proceed? 

Regards,
Martin



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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 31/35] multipathd: uxlsnr: add idle notification
  2021-09-16 15:54         ` Martin Wilck
@ 2021-09-16 16:10           ` Benjamin Marzinski
  0 siblings, 0 replies; 89+ messages in thread
From: Benjamin Marzinski @ 2021-09-16 16:10 UTC (permalink / raw)
  To: Martin Wilck; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Thu, Sep 16, 2021 at 05:54:14PM +0200, Martin Wilck wrote:
> On Thu, 2021-09-16 at 10:06 -0500, Benjamin Marzinski wrote:
> > On Thu, Sep 16, 2021 at 10:54:19AM +0200, Martin Wilck wrote:
> > > On Wed, 2021-09-15 at 23:14 -0500, Benjamin Marzinski wrote:
> > > > On Fri, Sep 10, 2021 at 01:41:16PM +0200, mwilck@suse.com wrote:
> > > > > From: Martin Wilck <mwilck@suse.com>
> > > > > 
> > > > > The previous patches added the state machine and the timeout
> > > > > handling,
> > > > > but there was no wakeup mechanism for the uxlsnr for cases
> > > > > where
> > > > > client connections were waiting for the vecs lock.
> > > > > 
> > > > > This patch uses the previously introduced wakeup mechanism of
> > > > > struct mutex_lock for this purpose. Processes which unlock the
> > > > > "global" vecs lock send an event in an eventfd which the uxlsnr
> > > > > loop is polling for.
> > > > > 
> > > > > As we are now woken up for servicing client handlers that don't
> > > > > wait for input but for the lock, we need to set up the pollfds
> > > > > differently, and iterate over all clients when handling events,
> > > > > not only over the ones that are receiving. The hangup handling
> > > > > is changed, too. We have to look at every client, even if one
> > > > > has
> > > > > hung up. Note that I don't take client_lock for the loop in
> > > > > uxsock_listen(), it's not necessary and will be removed
> > > > > elsewhere
> > > > > in a follow-up patch.
> > > > > 
> > > > > With this in place, the lock need not be taken in
> > > > > execute_handler()
> > > > > any more. The uxlsnr only ever calls trylock() on the vecs
> > > > > lock,
> > > > > avoiding any waiting for other threads to finish.
> > > > > 
> > > > > Signed-off-by: Martin Wilck <mwilck@suse.com>
> > > > > ---
> > > > >  multipathd/uxlsnr.c | 211 ++++++++++++++++++++++++++++++------
> > > > > ------
> > > > > --
> > > > >  1 file changed, 143 insertions(+), 68 deletions(-)
> > > > > 
> > > 
> > > > 
> > > > I do worry that if there are, for instance, a lot of uevents
> > > > coming in,
> > > > this could starve the uxlsnr thread, since other threads could be
> > > > grabbing and releasing the vecs lock, but if it's usually being
> > > > held,
> > > > then the uxlsnr thread might never try to grab it when it's free,
> > > > and
> > > > it
> > > > will keep losing its place in line. Also, every time that the
> > > > vecs lock
> > > > is dropped between ppoll() calls, a wakeup will get triggered,
> > > > even if
> > > > the lock was grabbed by something else before the ppoll thread
> > > > runs.
> > > 
> > > I've thought about this too. It's true that the ppoll ->
> > > pthread_mutex_trylock() sequence will never acquire the lock if
> > > some
> > > other thread calls lock() at the same time.
> > > 
> > > If multiple processes call lock(), the "winner" of the lock is
> > > random.
> > > Thus in a way this change actually adds some predictablity: the
> > > uxlsnr
> > > will step back if some other process is actively trying to grab the
> > > lock. IMO that the right thing to do in almost all situations.
> > > 
> > > We don't need to worry about "thundering herd" issues because the
> > > number of threads that might wait on the lock is rather small. In
> > > the
> > > worst case, 3 threads (checker, dmevents handler and uevent
> > > dispatcher,
> > > plus the uxlsnr in ppoll()) wait for the lock at the same time.
> > > Usually
> > > one of them will have it grabbed. On systems that lack dmevent
> > > polling,
> > > the number of waiter threads may be higher, but AFAICS it's a very
> > > rare
> > > condition to have hundreds of dmevents delivered to different maps
> > > simultaneously, and if it happens, it's probably correct to have
> > > them
> > > serviced quickly.
> > > 
> > > The uevent dispatcher doesn't hold the lock, it's taken and
> > > released
> > > for every event handled. Thus uxlsnr has a real chance to jump in
> > > between uevents. The same holds for the dmevents thread, it takes
> > > the
> > > lock separately for every map affected. The only piece of code that
> > > holds the lock for an extended period of time (except
> > > reconfigure(),
> > > where it's unavoidable) is the path checker (that's bad, and next
> > > on
> > > the todo list).
> > > 
> > > The really "important" commands (shutdown, reconfigure) don't take
> > > the
> > > lock and return immediately; the lock is no issue for them. I don't
> > > see
> > > any other cli command that needs to be served before uevents or dm
> > > events.
> > > 
> > > I haven't been able to test this on huge configurations with 1000s
> > > of
> > > LUNs, but I tested with artificial delays in checker loop, uevent
> > > handlers, and dmevent handler, and lots of clients querying the
> > > daemon
> > > in parallel, and saw that clients were handled very nicely. Some
> > > timeouts are inevitable (e.g. if the checker simply holds the lock
> > > longer than the uxsock_timeout), but that is no regression.
> > > 
> > > Bottom line: I believe that because this patch reduces the busy-
> > > wait
> > > time, clients will be served more reliably and more quickly than
> > > before
> > > (more precisely: both average and standard deviation of the service
> > > delay will be improved wrt before, and timeouts occur less
> > > frequently).
> > > I encourage everyone to experiment and see if reality shows that
> > > I'm
> > > wrong.
> > > 
> > > > I suppose the only way to deal with that would be to move the
> > > > locking
> > > > commands to a list handled by a separate thread, so that it could
> > > > block
> > > > without stalling the non-locking commands.
> > > 
> > > Not sure if I understand correctly, just in case: non-locking
> > > commands
> > > are never stalled with my patch.
> > 
> > I realize. I was saying that you could avoid starvation while still
> > allowing non-locking commands to complete by moving the locking
> > commands
> > to a seperate thread, which did block on the lock. I didn't consider
> > a
> > ticketing system. Ideally, the checker loop would have the lowest
> > priority, Since it isn't responding to any event, and ususally is
> > just
> > verifiying that nothing has changed.  But you do make a good point
> > that
> > when we are getting a lot of events, and the uxlsnr loop has a chance
> > of
> > getting starved, we probably want to prioritize the event handling
> > anyway.
> > 
> 
> I have also thought about using additional threads for handling cli
> commands. One could either use a single thread, similar to the udev
> listener/dispatcher pair (your suggestion IIUC), or one thread per
> (blocking) client.
> 
> Moving client handling into separate thread(s) avoids the complexity of
> the state machine and the eventfd-based wakeup. But on the back side,
> it introduces new multithreading-related complexity (of which we
> already have our fair share). Client tasks running lock(&vecs->lock) in
> order to serve commands like "multipathd show paths" might now starve
> event handling, which would be worse than vice versa, IMO.
> 
> Eventually, I found the idea of the poll/wakeup loop with no additional
> threads more appealing, and more suitable for the task. But I admit
> that it's a matter of personal taste. I tend to try to use pthreads as
> little as possible ;-).
> 
> So how do we proceed? 

I think your argument that we'll only risk starving the uxlsnr thread
when it makes sense to prioritize other threads is a good one. So, I'm
o.k. with this trylock() solution.

-Ben

> 
> Regards,
> Martin
> 

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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 12/35] multipathd: add and set cli_handlers in a single step
  2021-09-16  0:01   ` Benjamin Marzinski
  2021-09-16  7:22     ` Martin Wilck
@ 2021-11-12 21:45     ` Martin Wilck
  1 sibling, 0 replies; 89+ messages in thread
From: Martin Wilck @ 2021-11-12 21:45 UTC (permalink / raw)
  To: Benjamin Marzinski; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Wed, 2021-09-15 at 19:01 -0500, Benjamin Marzinski wrote:
> On Fri, Sep 10, 2021 at 01:40:57PM +0200, mwilck@suse.com wrote:
> > From: Martin Wilck <mwilck@suse.com>
> > 
> > Modify set_handler_callback() such that a missing slot is created
> > if no matching slot is found. This way, we can skip the
> > initialization
> > with NULL handlers on startup.
> > 
> > Signed-off-by: Martin Wilck <mwilck@suse.com>
> > ---
> >  multipathd/cli.c | 85 +++++++-------------------------------------
> > ----
> >  multipathd/cli.h |  6 ++--
> >  2 files changed, 15 insertions(+), 76 deletions(-)
> > 
> > diff --git a/multipathd/cli.c b/multipathd/cli.c
> > index 5213813..7020d2b 100644
> > --- a/multipathd/cli.c
> > +++ b/multipathd/cli.c
> > @@ -100,26 +100,20 @@ find_handler (uint64_t fp)
> >  }
> >  
> >  int
> > -set_handler_callback (uint64_t fp, cli_handler *fn)
> > +__set_handler_callback (uint64_t fp, cli_handler *fn, bool locked)
> >  {
> > -       struct handler * h = find_handler(fp);
> > +       struct handler *h = find_handler(fp);
> >  
> 
> Wouldn't it be a bug if we reset the handler? Is this really
> something
> we need to check for? Also, if add_handler() just returned a pointer
> to
> the handler, we wouldn't need to call fail_handler() immediately
> after
> creating it.

I'll fix this in an add-on patch in v2.

Regards
Martin


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


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [dm-devel] [PATCH 30/35] multipathd: uxlsnr: merge uxsock_trigger() into state machine
  2021-09-16  3:32   ` Benjamin Marzinski
  2021-09-16  8:02     ` Martin Wilck
@ 2021-11-12 22:07     ` Martin Wilck
  1 sibling, 0 replies; 89+ messages in thread
From: Martin Wilck @ 2021-11-12 22:07 UTC (permalink / raw)
  To: Benjamin Marzinski; +Cc: lixiaokeng, dm-devel, Chongyun Wu

On Wed, 2021-09-15 at 22:32 -0500, Benjamin Marzinski wrote:
> On Fri, Sep 10, 2021 at 01:41:15PM +0200, mwilck@suse.com wrote:
> > From: Martin Wilck <mwilck@suse.com>
> > 
> > This patch sets up the bulk of the state machine. The idea is to
> > fall through the case labels as long as possible (when steps
> > succeed)
> > and return to the caller if either an error occurs, or it becomes
> > necessary to wait for some pollable condition.
> > 
> > While doing this, switch to negative error codes for the functions
> > in uxlsnr.c (e.g. parse_cmd()). Positive return codes are reserved
> > for the cli_handler functions themselves. This way we can clearly
> > distinguish the error source, and avoid confusion and misleading
> > error messages. No cli_handler returns negative values.
> > 
> > Note: with this patch applied, clients may hang and time out if
> > the handler fails to acquire the vecs lock. This will be fixed in
> > the
> > follow-up patch "multipathd: uxlsnr: add idle notification".
> > 
> > Signed-off-by: Martin Wilck <mwilck@suse.com>
> > ---
> >  multipathd/uxlsnr.c | 145 ++++++++++++++++++++++++----------------
> > ----
> >  1 file changed, 80 insertions(+), 65 deletions(-)
> > 
> > diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
> > index ff9604f..553274b 100644
> > --- a/multipathd/uxlsnr.c
> > +++ b/multipathd/uxlsnr.c
> >  
> > -static int uxsock_trigger(struct client *c, void *trigger_data)
> > +void default_reply(struct client *c, int r)
> >  {
> > -       struct vectors * vecs;
> > -       int r = 1;
> > -
> > -       vecs = (struct vectors *)trigger_data;
> > -
> > -       r = parse_cmd(c);
> > -
> > -       if (r == 0 && c->cmdvec && VECTOR_SIZE(c->cmdvec) > 0) {
> > -               struct key *kw = VECTOR_SLOT(c->cmdvec, 0);
> > -
> > -               if (!c->is_root && kw->code != LIST)
> > -                       r = EPERM;
> > -       }
> > -
> > -       if (r == 0 && c->handler)
> > -               r = execute_handler(c, vecs, uxsock_timeout /
> > 1000);
> > -
> > -       if (c->cmdvec) {
> > -               free_keys(c->cmdvec);
> > -               c->cmdvec = NULL;
> > -       }
> > -
> > -       if (r > 0) {
> > -               switch(r) {
> > -               case ETIMEDOUT:
> > -                       append_strbuf_str(&c->reply, "timeout\n");
> > -                       break;
> > -               case EPERM:
> > -                       append_strbuf_str(&c->reply,
> > -                                         "permission deny: need to
> > be root\n");
> > -                       break;
> > -               default:
> > -                       append_strbuf_str(&c->reply, "fail\n");
> > -                       break;
> > -               }
> > -       }
> > -       else if (!r && get_strbuf_len(&c->reply) == 0) {
> > +       switch(r) {
> > +       case -EINVAL:
> > +       case -ESRCH:
> > +       case -ENOMEM:
> 
> get_cmdvec() returns positive errors and do_genhelp() expects
> positive
> errors, but this expects negative errors.

parse_cmd() already negates the return value of get_cmdvec().
But you're right wrt do_genhelp(). I'll fix it.

(FTR, when I worked on the patch series, I considered fixing up the
return codes of all cli_handler functions, but I gave up for now, it
was too much work for no real benefit).

Regards
Martin


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


^ permalink raw reply	[flat|nested] 89+ messages in thread

end of thread, other threads:[~2021-11-12 22:07 UTC | newest]

Thread overview: 89+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-09-10 11:40 [dm-devel] [PATCH 00/35] multipathd: uxlsnr overhaul mwilck
2021-09-10 11:40 ` [dm-devel] [PATCH 01/35] libmultipath: add timespeccmp() utility function mwilck
2021-09-15 22:07   ` Benjamin Marzinski
2021-09-10 11:40 ` [dm-devel] [PATCH 02/35] libmultipath: add trylock() helper mwilck
2021-09-15 22:07   ` Benjamin Marzinski
2021-09-10 11:40 ` [dm-devel] [PATCH 03/35] libmultipath: add optional wakeup functionality to lock.c mwilck
2021-09-15 22:13   ` Benjamin Marzinski
2021-09-10 11:40 ` [dm-devel] [PATCH 04/35] libmultipath: print: add __snprint_config() mwilck
2021-09-15 22:14   ` Benjamin Marzinski
2021-09-10 11:40 ` [dm-devel] [PATCH 05/35] libmultipath: improve cleanup of uevent queues on exit mwilck
2021-09-15 22:20   ` Benjamin Marzinski
2021-09-16  7:10     ` Martin Wilck
2021-09-16 14:26       ` Benjamin Marzinski
2021-09-10 11:40 ` [dm-devel] [PATCH 06/35] multipathd: fix systemd notification when stopping while reloading mwilck
2021-09-15 22:55   ` Benjamin Marzinski
2021-09-10 11:40 ` [dm-devel] [PATCH 07/35] multipathd: improve delayed reconfigure mwilck
2021-09-15 23:00   ` Benjamin Marzinski
2021-09-16  7:16     ` Martin Wilck
2021-09-10 11:40 ` [dm-devel] [PATCH 08/35] multipathd: cli.h: formatting improvements mwilck
2021-09-15 23:01   ` Benjamin Marzinski
2021-09-10 11:40 ` [dm-devel] [PATCH 09/35] multipathd: cli_del_map: fix reply for delayed action mwilck
2021-09-15 23:40   ` Benjamin Marzinski
2021-09-10 11:40 ` [dm-devel] [PATCH 10/35] multipathd: add prototype for cli_handler functions mwilck
2021-09-15 23:53   ` Benjamin Marzinski
2021-09-10 11:40 ` [dm-devel] [PATCH 11/35] multipathd: make all cli_handlers static mwilck
2021-09-15 23:53   ` Benjamin Marzinski
2021-09-10 11:40 ` [dm-devel] [PATCH 12/35] multipathd: add and set cli_handlers in a single step mwilck
2021-09-16  0:01   ` Benjamin Marzinski
2021-09-16  7:22     ` Martin Wilck
2021-11-12 21:45     ` Martin Wilck
2021-09-10 11:40 ` [dm-devel] [PATCH 13/35] multipathd: cli.c: use ESRCH for "command not found" mwilck
2021-09-16  0:02   ` Benjamin Marzinski
2021-09-10 11:40 ` [dm-devel] [PATCH 14/35] multipathd: add "force_reconfigure" option mwilck
2021-09-16  0:13   ` Benjamin Marzinski
2021-09-16  7:34     ` Martin Wilck
2021-09-16 14:32       ` Benjamin Marzinski
2021-09-10 11:41 ` [dm-devel] [PATCH 15/35] multipathd: uxlsnr: avoid stalled clients during reconfigure mwilck
2021-09-16  2:17   ` Benjamin Marzinski
2021-09-10 11:41 ` [dm-devel] [PATCH 16/35] multipathd: uxlsnr: handle client HUP mwilck
2021-09-16  2:17   ` Benjamin Marzinski
2021-09-10 11:41 ` [dm-devel] [PATCH 17/35] multipathd: uxlsnr: use symbolic values for pollfd indices mwilck
2021-09-16  2:18   ` Benjamin Marzinski
2021-09-10 11:41 ` [dm-devel] [PATCH 18/35] multipathd: uxlsnr: avoid using fd -1 in ppoll() mwilck
2021-09-16  2:18   ` Benjamin Marzinski
2021-09-10 11:41 ` [dm-devel] [PATCH 19/35] multipathd: uxlsnr: data structure for stateful client connection mwilck
2021-09-16  2:19   ` Benjamin Marzinski
2021-09-10 11:41 ` [dm-devel] [PATCH 20/35] multipathd: move uxsock_trigger() to uxlsnr.c mwilck
2021-09-16  2:19   ` Benjamin Marzinski
2021-09-10 11:41 ` [dm-devel] [PATCH 21/35] multipathd: move parse_cmd() " mwilck
2021-09-16  2:19   ` Benjamin Marzinski
2021-09-10 11:41 ` [dm-devel] [PATCH 22/35] multipathd: uxlsnr: remove check_timeout() mwilck
2021-09-16  2:21   ` Benjamin Marzinski
2021-09-10 11:41 ` [dm-devel] [PATCH 23/35] multipathd: uxlsnr: move client handling to separate function mwilck
2021-09-16  2:21   ` Benjamin Marzinski
2021-09-10 11:41 ` [dm-devel] [PATCH 24/35] multipathd: uxlsnr: use main poll loop for receiving mwilck
2021-09-16  2:22   ` Benjamin Marzinski
2021-09-10 11:41 ` [dm-devel] [PATCH 25/35] multipathd: use strbuf in cli_handler functions mwilck
2021-09-16  2:23   ` Benjamin Marzinski
2021-09-10 11:41 ` [dm-devel] [PATCH 26/35] multipathd: uxlsnr: check root on connection startup mwilck
2021-09-16  2:23   ` Benjamin Marzinski
2021-09-10 11:41 ` [dm-devel] [PATCH 27/35] multipathd: uxlsnr: pass struct client to uxsock_trigger() and parse_cmd() mwilck
2021-09-16  2:28   ` Benjamin Marzinski
2021-09-10 11:41 ` [dm-devel] [PATCH 28/35] multipathd: uxlsnr: move handler execution to separate function mwilck
2021-09-16  2:28   ` Benjamin Marzinski
2021-09-10 11:41 ` [dm-devel] [PATCH 29/35] multipathd: uxlsnr: use parser to determine non-root commands mwilck
2021-09-16  2:29   ` Benjamin Marzinski
2021-09-10 11:41 ` [dm-devel] [PATCH 30/35] multipathd: uxlsnr: merge uxsock_trigger() into state machine mwilck
2021-09-16  3:32   ` Benjamin Marzinski
2021-09-16  8:02     ` Martin Wilck
2021-11-12 22:07     ` Martin Wilck
2021-09-10 11:41 ` [dm-devel] [PATCH 31/35] multipathd: uxlsnr: add idle notification mwilck
2021-09-16  4:14   ` Benjamin Marzinski
2021-09-16  8:54     ` Martin Wilck
2021-09-16 15:06       ` Benjamin Marzinski
2021-09-16 15:54         ` Martin Wilck
2021-09-16 16:10           ` Benjamin Marzinski
2021-09-10 11:41 ` [dm-devel] [PATCH 32/35] multipathd: uxlsnr: add timeout handling mwilck
2021-09-16  4:17   ` Benjamin Marzinski
2021-09-16  8:58     ` Martin Wilck
2021-09-16 15:08       ` Benjamin Marzinski
2021-09-10 11:41 ` [dm-devel] [PATCH 33/35] multipathd: uxlsnr: use poll loop for sending, too mwilck
2021-09-16  4:22   ` Benjamin Marzinski
2021-09-16  9:33     ` Martin Wilck
2021-09-16 15:26       ` Benjamin Marzinski
2021-09-10 11:41 ` [dm-devel] [PATCH 34/35] multipathd: uxlsnr: drop client_lock mwilck
2021-09-16  4:24   ` Benjamin Marzinski
2021-09-16  9:34     ` Martin Wilck
2021-09-10 11:41 ` [dm-devel] [PATCH 35/35] multipathd: uxclt: allow client mode for non-root, too mwilck
2021-09-16  4:24   ` Benjamin Marzinski

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).