device-mapper-multipath-0.8.7-43
Add 0198-multipathd-print-path-offline-message-even-without-a.patch
* Fixes RHEL-133814 ("log_checker_err is not printing messages
repeatedly for failed path [rhel-9]")
Add 0199-libmultipath-improve-cleanup-of-uevent-queues-on-exi.patch
Add 0200-uevent_dispatch-use-while-in-wait-loop.patch
Add 0201-libmultipath-uevent_dispatch-process-uevents-one-by-.patch
Add 0202-libmultipath-uevent_listen-don-t-delay-uevents.patch
Add 0203-libmultipath-uevent-use-struct-to-pass-parameters-ar.patch
Add 0204-libmultipath-is_uevent_busy-check-servicing_uev-unde.patch
Add 0205-multipathd-make-multipathd-show-status-busy-checker-.patch
* Fixes RHEL-135904 (VM reboot in RHOSP environment fails with error
"Could not open '/dev/dm-95': No such file or directory")
Resolves: RHEL-133814
Resolves: RHEL-135904
This commit is contained in:
parent
c6e35bf163
commit
51ade5ca63
@ -0,0 +1,36 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Date: Wed, 21 Jan 2026 16:03:13 -0500
|
||||
Subject: [PATCH] multipathd: print path offline message even without a checker
|
||||
|
||||
If a path has a checker selected and is offline, multipathd will print a
|
||||
"path offline" message. However if the checker isn't selected, for
|
||||
instance because multipathd was started or reconfigured while the path
|
||||
was offline, multipathd was not printing the "path offline" message.
|
||||
Fix that.
|
||||
|
||||
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Reviewed-by: Martin Wilck <mwilck@suse.com>
|
||||
---
|
||||
multipathd/main.c | 5 ++---
|
||||
1 file changed, 2 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/multipathd/main.c b/multipathd/main.c
|
||||
index a85c0db4..9beb0e06 100644
|
||||
--- a/multipathd/main.c
|
||||
+++ b/multipathd/main.c
|
||||
@@ -97,12 +97,11 @@ mpath_pr_event_handle(struct path *pp, unsigned int nr_keys_needed,
|
||||
|
||||
#define LOG_MSG(lvl, pp) \
|
||||
do { \
|
||||
- if (pp->mpp && checker_selected(&pp->checker) && \
|
||||
- lvl <= libmp_verbosity) { \
|
||||
+ if (pp->mpp && lvl <= libmp_verbosity) { \
|
||||
if (pp->offline) \
|
||||
condlog(lvl, "%s: %s - path offline", \
|
||||
pp->mpp->alias, pp->dev); \
|
||||
- else { \
|
||||
+ else if (checker_selected(&pp->checker)) { \
|
||||
const char *__m = \
|
||||
checker_message(&pp->checker); \
|
||||
\
|
||||
133
0199-libmultipath-improve-cleanup-of-uevent-queues-on-exi.patch
Normal file
133
0199-libmultipath-improve-cleanup-of-uevent-queues-on-exi.patch
Normal file
@ -0,0 +1,133 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Martin Wilck <mwilck@suse.com>
|
||||
Date: Thu, 9 Sep 2021 23:59:42 +0200
|
||||
Subject: [PATCH] libmultipath: improve cleanup of uevent queues on exit
|
||||
|
||||
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.
|
||||
|
||||
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
---
|
||||
libmultipath/uevent.c | 49 ++++++++++++++++++++++++++++++++-----------
|
||||
1 file changed, 37 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c
|
||||
index 4265904b..80941f87 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:
|
||||
38
0200-uevent_dispatch-use-while-in-wait-loop.patch
Normal file
38
0200-uevent_dispatch-use-while-in-wait-loop.patch
Normal file
@ -0,0 +1,38 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Martin Wilck <mwilck@suse.com>
|
||||
Date: Tue, 29 Mar 2022 16:06:25 +0200
|
||||
Subject: [PATCH] uevent_dispatch(): use while in wait loop
|
||||
|
||||
Callers of pthread_cond_wait() should generally use a while loop
|
||||
to test the condition. Also, remove the misleading comment.
|
||||
Condition variables aren't unreliable, they're just not strictly
|
||||
tied to the condition tested.
|
||||
|
||||
Signed-off-by: Martin Wilck <mwilck@suse.com>
|
||||
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
---
|
||||
libmultipath/uevent.c | 9 +++------
|
||||
1 file changed, 3 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c
|
||||
index 80941f87..e3ec1ac1 100644
|
||||
--- a/libmultipath/uevent.c
|
||||
+++ b/libmultipath/uevent.c
|
||||
@@ -445,13 +445,10 @@ int uevent_dispatch(int (*uev_trigger)(struct uevent *, void * trigger_data),
|
||||
pthread_cleanup_push(cleanup_mutex, uevq_lockp);
|
||||
pthread_mutex_lock(uevq_lockp);
|
||||
servicing_uev = 0;
|
||||
- /*
|
||||
- * Condition signals are unreliable,
|
||||
- * so make sure we only wait if we have to.
|
||||
- */
|
||||
- if (list_empty(&uevq)) {
|
||||
+
|
||||
+ while (list_empty(&uevq))
|
||||
pthread_cond_wait(uev_condp, uevq_lockp);
|
||||
- }
|
||||
+
|
||||
servicing_uev = 1;
|
||||
list_splice_init(&uevq, &uevq_tmp);
|
||||
pthread_cleanup_pop(1);
|
||||
319
0201-libmultipath-uevent_dispatch-process-uevents-one-by-.patch
Normal file
319
0201-libmultipath-uevent_dispatch-process-uevents-one-by-.patch
Normal file
@ -0,0 +1,319 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Martin Wilck <mwilck@suse.com>
|
||||
Date: Tue, 29 Mar 2022 18:04:42 +0200
|
||||
Subject: [PATCH] libmultipath: uevent_dispatch(): process uevents one by one
|
||||
|
||||
The main rationale for delaying uevents is that the
|
||||
uevent dispatcher may have to wait for other threads to release the
|
||||
vecs lock, may the vecs lock for an extended amount of time, and
|
||||
even sleep occasionally. By delaying them, we have the chance
|
||||
to accumulate events for the same path device ("filtering") or
|
||||
WWID ("merging"), thus avoiding duplicate work if we merge these
|
||||
into one.
|
||||
|
||||
A similar effect can be obtained in the uevent dispatcher itself
|
||||
by looking for new uevents after each dispatched event, and trying
|
||||
to merge the newly arrived events with those that remained
|
||||
in the queue.
|
||||
|
||||
When uevq_work is non-empty and we append a list of new events,
|
||||
we don't need to check the entire list for filterable and mergeable
|
||||
uevents. uevq_work had been filtered and merged already. So we just
|
||||
need to check the newly appended events. These must of course be
|
||||
checked for merges with earlier events, too.
|
||||
|
||||
We must deal with some special cases here, like previously merged
|
||||
uevents being filtered later.
|
||||
|
||||
Signed-off-by: Martin Wilck <mwilck@suse.com>
|
||||
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
---
|
||||
libmultipath/list.h | 53 +++++++++++++++++
|
||||
libmultipath/uevent.c | 129 ++++++++++++++++++++++++++++++------------
|
||||
2 files changed, 147 insertions(+), 35 deletions(-)
|
||||
|
||||
diff --git a/libmultipath/list.h b/libmultipath/list.h
|
||||
index ced021f5..248f72bc 100644
|
||||
--- a/libmultipath/list.h
|
||||
+++ b/libmultipath/list.h
|
||||
@@ -246,6 +246,35 @@ static inline void list_splice_tail_init(struct list_head *list,
|
||||
#define list_entry(ptr, type, member) \
|
||||
container_of(ptr, type, member)
|
||||
|
||||
+
|
||||
+/**
|
||||
+ * list_pop - unlink and return the first list element
|
||||
+ * @head: the &struct list_head pointer.
|
||||
+ */
|
||||
+static inline struct list_head *list_pop(struct list_head *head)
|
||||
+{
|
||||
+ struct list_head *tmp;
|
||||
+
|
||||
+ if (list_empty(head))
|
||||
+ return NULL;
|
||||
+ tmp = head->next;
|
||||
+ list_del_init(tmp);
|
||||
+ return tmp;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * list_pop_entry - unlink and return the entry of the first list element
|
||||
+ * @head: the &struct list_head pointer.
|
||||
+ * @type: the type of the struct this is embedded in.
|
||||
+ * @member: the name of the list_struct within the struct.
|
||||
+ */
|
||||
+#define list_pop_entry(head, type, member) \
|
||||
+({ \
|
||||
+ struct list_head *__h = list_pop(head); \
|
||||
+ \
|
||||
+ (__h ? container_of(__h, type, member) : NULL); \
|
||||
+})
|
||||
+
|
||||
/**
|
||||
* list_for_each - iterate over a list
|
||||
* @pos: the &struct list_head to use as a loop counter.
|
||||
@@ -334,6 +363,30 @@ static inline void list_splice_tail_init(struct list_head *list,
|
||||
&pos->member != (head); \
|
||||
pos = n, n = list_entry(n->member.prev, typeof(*n), member))
|
||||
|
||||
+/**
|
||||
+ * list_for_some_entry - iterate list from the given begin node to the given end node
|
||||
+ * @pos: the type * to use as a loop counter.
|
||||
+ * @from: the begin node of the iteration.
|
||||
+ * @to: the end node of the iteration.
|
||||
+ * @member: the name of the list_struct within the struct.
|
||||
+ */
|
||||
+#define list_for_some_entry(pos, from, to, member) \
|
||||
+ for (pos = list_entry((from)->next, typeof(*pos), member); \
|
||||
+ &pos->member != (to); \
|
||||
+ pos = list_entry(pos->member.next, typeof(*pos), member))
|
||||
+
|
||||
+/**
|
||||
+ * list_for_some_entry_reverse - iterate backwards list from the given begin node to the given end node
|
||||
+ * @pos: the type * to use as a loop counter.
|
||||
+ * @from: the begin node of the iteration.
|
||||
+ * @to: the end node of the iteration.
|
||||
+ * @member: the name of the list_struct within the struct.
|
||||
+ */
|
||||
+#define list_for_some_entry_reverse(pos, from, to, member) \
|
||||
+ for (pos = list_entry((from)->prev, typeof(*pos), member); \
|
||||
+ &pos->member != (to); \
|
||||
+ pos = list_entry(pos->member.prev, typeof(*pos), member))
|
||||
+
|
||||
/**
|
||||
* list_for_some_entry_safe - iterate list from the given begin node to the given end node safe against removal of list entry
|
||||
* @pos: the type * to use as a loop counter.
|
||||
diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c
|
||||
index e3ec1ac1..2198e254 100644
|
||||
--- a/libmultipath/uevent.c
|
||||
+++ b/libmultipath/uevent.c
|
||||
@@ -308,17 +308,64 @@ uevent_can_merge(struct uevent *earlier, struct uevent *later)
|
||||
return false;
|
||||
}
|
||||
|
||||
+static void uevent_delete_from_list(struct uevent *to_delete,
|
||||
+ struct uevent **previous,
|
||||
+ struct list_head **old_tail)
|
||||
+{
|
||||
+ /*
|
||||
+ * "old_tail" is the list_head before the last list element to which
|
||||
+ * the caller iterates (the list anchor if the caller iterates over
|
||||
+ * the entire list). If this element is removed (which can't happen
|
||||
+ * for the anchor), "old_tail" must be moved. It can happen that
|
||||
+ * "old_tail" ends up pointing at the anchor.
|
||||
+ */
|
||||
+ if (*old_tail == &to_delete->node)
|
||||
+ *old_tail = to_delete->node.prev;
|
||||
+
|
||||
+ list_del_init(&to_delete->node);
|
||||
+
|
||||
+ /*
|
||||
+ * The "to_delete" uevent has been merged with other uevents
|
||||
+ * previously. Re-insert them into the list, at the point we're
|
||||
+ * currently at. This must be done after the list_del_init() above,
|
||||
+ * otherwise previous->next would still point to to_delete.
|
||||
+ */
|
||||
+ if (!list_empty(&to_delete->merge_node)) {
|
||||
+ struct uevent *last = list_entry(to_delete->merge_node.prev,
|
||||
+ typeof(*last), node);
|
||||
+
|
||||
+ list_splice(&to_delete->merge_node, &(*previous)->node);
|
||||
+ *previous = last;
|
||||
+ }
|
||||
+ if (to_delete->udev)
|
||||
+ udev_device_unref(to_delete->udev);
|
||||
+
|
||||
+ free(to_delete);
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Use this function to delete events that are known not to
|
||||
+ * be equal to old_tail, and have an empty merge_node list.
|
||||
+ * For others, use uevent_delete_from_list().
|
||||
+ */
|
||||
+static void uevent_delete_simple(struct uevent *to_delete)
|
||||
+{
|
||||
+ list_del_init(&to_delete->node);
|
||||
+
|
||||
+ if (to_delete->udev)
|
||||
+ udev_device_unref(to_delete->udev);
|
||||
+
|
||||
+ free(to_delete);
|
||||
+}
|
||||
+
|
||||
static void
|
||||
-uevent_prepare(struct list_head *tmpq)
|
||||
+uevent_prepare(struct list_head *tmpq, const struct list_head *stop)
|
||||
{
|
||||
struct uevent *uev, *tmp;
|
||||
|
||||
- list_for_each_entry_reverse_safe(uev, tmp, tmpq, node) {
|
||||
+ list_for_some_entry_reverse_safe(uev, tmp, tmpq, stop, node) {
|
||||
if (uevent_can_discard(uev)) {
|
||||
- list_del_init(&uev->node);
|
||||
- if (uev->udev)
|
||||
- udev_device_unref(uev->udev);
|
||||
- FREE(uev);
|
||||
+ uevent_delete_simple(uev);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -329,7 +376,7 @@ uevent_prepare(struct list_head *tmpq)
|
||||
}
|
||||
|
||||
static void
|
||||
-uevent_filter(struct uevent *later, struct list_head *tmpq)
|
||||
+uevent_filter(struct uevent *later, struct list_head *tmpq, struct list_head **stop)
|
||||
{
|
||||
struct uevent *earlier, *tmp;
|
||||
|
||||
@@ -343,16 +390,13 @@ uevent_filter(struct uevent *later, struct list_head *tmpq)
|
||||
earlier->kernel, earlier->action,
|
||||
later->kernel, later->action);
|
||||
|
||||
- list_del_init(&earlier->node);
|
||||
- if (earlier->udev)
|
||||
- udev_device_unref(earlier->udev);
|
||||
- FREE(earlier);
|
||||
+ uevent_delete_from_list(earlier, &tmp, stop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
-uevent_merge(struct uevent *later, struct list_head *tmpq)
|
||||
+uevent_merge(struct uevent *later, struct list_head *tmpq, struct list_head **stop)
|
||||
{
|
||||
struct uevent *earlier, *tmp;
|
||||
|
||||
@@ -367,37 +411,42 @@ uevent_merge(struct uevent *later, struct list_head *tmpq)
|
||||
earlier->action, earlier->kernel, earlier->wwid,
|
||||
later->action, later->kernel, later->wwid);
|
||||
|
||||
+ /* See comment in uevent_delete_from_list() */
|
||||
+ if (&earlier->node == *stop)
|
||||
+ *stop = earlier->node.prev;
|
||||
+
|
||||
list_move(&earlier->node, &later->merge_node);
|
||||
+ list_splice_init(&earlier->merge_node,
|
||||
+ &later->merge_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
-merge_uevq(struct list_head *tmpq)
|
||||
+merge_uevq(struct list_head *tmpq, struct list_head *stop)
|
||||
{
|
||||
struct uevent *later;
|
||||
|
||||
- uevent_prepare(tmpq);
|
||||
- list_for_each_entry_reverse(later, tmpq, node) {
|
||||
- uevent_filter(later, tmpq);
|
||||
+ uevent_prepare(tmpq, stop);
|
||||
+ list_for_some_entry_reverse(later, tmpq, stop, node) {
|
||||
+ uevent_filter(later, tmpq, &stop);
|
||||
if(uevent_need_merge())
|
||||
- uevent_merge(later, tmpq);
|
||||
+ uevent_merge(later, tmpq, &stop);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
service_uevq(struct list_head *tmpq)
|
||||
{
|
||||
- struct uevent *uev, *tmp;
|
||||
-
|
||||
- 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");
|
||||
- pthread_cleanup_pop(1);
|
||||
- }
|
||||
+ struct uevent *uev = list_pop_entry(tmpq, typeof(*uev), node);
|
||||
+
|
||||
+ if (uev == NULL)
|
||||
+ return;
|
||||
+ condlog(4, "servicing uevent '%s %s'", uev->action, uev->kernel);
|
||||
+ pthread_cleanup_push(cleanup_uev, uev);
|
||||
+ if (my_uev_trigger && my_uev_trigger(uev, my_trigger_data))
|
||||
+ condlog(0, "uevent trigger error");
|
||||
+ pthread_cleanup_pop(1);
|
||||
}
|
||||
|
||||
static void uevent_cleanup(void *arg)
|
||||
@@ -434,33 +483,43 @@ static void cleanup_global_uevq(void *arg __attribute__((unused)))
|
||||
int uevent_dispatch(int (*uev_trigger)(struct uevent *, void * trigger_data),
|
||||
void * trigger_data)
|
||||
{
|
||||
+ LIST_HEAD(uevq_work);
|
||||
+
|
||||
my_uev_trigger = uev_trigger;
|
||||
my_trigger_data = trigger_data;
|
||||
|
||||
mlockall(MCL_CURRENT | MCL_FUTURE);
|
||||
|
||||
+ pthread_cleanup_push(cleanup_uevq, &uevq_work);
|
||||
while (1) {
|
||||
- LIST_HEAD(uevq_tmp);
|
||||
+ struct list_head *stop;
|
||||
|
||||
pthread_cleanup_push(cleanup_mutex, uevq_lockp);
|
||||
pthread_mutex_lock(uevq_lockp);
|
||||
- servicing_uev = 0;
|
||||
|
||||
- while (list_empty(&uevq))
|
||||
+ servicing_uev = !list_empty(&uevq_work);
|
||||
+
|
||||
+ while (list_empty(&uevq_work) && list_empty(&uevq))
|
||||
pthread_cond_wait(uev_condp, uevq_lockp);
|
||||
|
||||
servicing_uev = 1;
|
||||
- list_splice_init(&uevq, &uevq_tmp);
|
||||
+ /*
|
||||
+ * "stop" is the list element towards which merge_uevq()
|
||||
+ * will iterate: the last element of uevq_work before
|
||||
+ * appending new uevents. If uveq_is empty, uevq_work.prev
|
||||
+ * equals &uevq_work, which is what we need.
|
||||
+ */
|
||||
+ stop = uevq_work.prev;
|
||||
+ list_splice_tail_init(&uevq, &uevq_work);
|
||||
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);
|
||||
+ merge_uevq(&uevq_work, stop);
|
||||
+ service_uevq(&uevq_work);
|
||||
}
|
||||
+ pthread_cleanup_pop(1);
|
||||
condlog(3, "Terminating uev service queue");
|
||||
return 0;
|
||||
}
|
||||
204
0202-libmultipath-uevent_listen-don-t-delay-uevents.patch
Normal file
204
0202-libmultipath-uevent_listen-don-t-delay-uevents.patch
Normal file
@ -0,0 +1,204 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Martin Wilck <mwilck@suse.com>
|
||||
Date: Tue, 29 Mar 2022 23:25:48 +0200
|
||||
Subject: [PATCH] libmultipath: uevent_listen(): don't delay uevents
|
||||
|
||||
When multipathd starts up early, basically all devices are added
|
||||
through uevent processing. This takes much more time than necessary
|
||||
because of the artificial delays introduced for passing uevents
|
||||
between the listener and the receiver thread in ee8888f
|
||||
("multipath-tools: improve processing efficiency for addition and deletion of
|
||||
multipath devices"). This delay could be up to 30s.
|
||||
|
||||
It's generally not a good idea to delay uevent processing in multipathd.
|
||||
ADD events must normally be handled ASAP in order to avoid maps entering
|
||||
queueing mode or eventually failing. Handling REMOVE events quickly is
|
||||
also important to make multipathd aware of deleted devices and keep
|
||||
kernel and multipathd state in sync.
|
||||
|
||||
If uevents arrive quickly, the assumption is that the dispatcher will process
|
||||
them more slowly than the listener. This was the idea of commit ee8888f,
|
||||
AFAIU: if a queue of unprocessed events piles up because the dispatcher is
|
||||
too slow, use filtering and merging to reduce the length of the queue, and
|
||||
thus the work to be done for the uevent dispatcher, especially the work
|
||||
that needs to be done while holding the vecs lock. In ee8888f, the
|
||||
queue was created by allowing uevents to accumulate in the listener.
|
||||
|
||||
This patch changes the logic of ee8888f, while keeping the uevent
|
||||
filtering and discarding features. The idea is that the uevent dispatcher
|
||||
shouldn't be idle if there are uevents to process. Therefore uevents
|
||||
are passed to it immediately. But it now checks for new uevents after
|
||||
processing every individual event, before processing the entire queue,
|
||||
and it applies filtering and merging to the queue as it grows.
|
||||
|
||||
This patch set avoids any delay when the uevent dispatcher is idle or
|
||||
able to keep up with the rate of incoming uevents, while applying an
|
||||
increasing amount of filtering and merging as pressure on the uevent
|
||||
dispatcher increases. It's reasonable to assume that filtering and
|
||||
merging get more efficient with increasing queue length, because the
|
||||
probability of finding matching events will increase.
|
||||
|
||||
Signed-off-by: Martin Wilck <mwilck@suse.com>
|
||||
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
---
|
||||
libmultipath/uevent.c | 108 +++++++++++++++---------------------------
|
||||
1 file changed, 37 insertions(+), 71 deletions(-)
|
||||
|
||||
diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c
|
||||
index 2198e254..c3984fef 100644
|
||||
--- a/libmultipath/uevent.c
|
||||
+++ b/libmultipath/uevent.c
|
||||
@@ -54,10 +54,6 @@
|
||||
#include "blacklist.h"
|
||||
#include "devmapper.h"
|
||||
|
||||
-#define MAX_ACCUMULATION_COUNT 2048
|
||||
-#define MAX_ACCUMULATION_TIME 30*1000
|
||||
-#define MIN_BURST_SPEED 10
|
||||
-
|
||||
typedef int (uev_trigger)(struct uevent *, void * trigger_data);
|
||||
|
||||
static LIST_HEAD(uevq);
|
||||
@@ -586,44 +582,43 @@ static struct uevent *uevent_from_udev_device(struct udev_device *dev)
|
||||
return uev;
|
||||
}
|
||||
|
||||
-static bool uevent_burst(struct timeval *start_time, int events)
|
||||
+#define MAX_UEVENTS 1000
|
||||
+static int uevent_receive_events(int fd, struct list_head *tmpq,
|
||||
+ struct udev_monitor *monitor)
|
||||
{
|
||||
- struct timeval diff_time, end_time;
|
||||
- unsigned long speed;
|
||||
- unsigned long eclipse_ms;
|
||||
-
|
||||
- if(events > MAX_ACCUMULATION_COUNT) {
|
||||
- condlog(2, "burst got %u uevents, too much uevents, stopped", events);
|
||||
- return false;
|
||||
- }
|
||||
+ struct pollfd ev_poll;
|
||||
+ int n = 0;
|
||||
|
||||
- gettimeofday(&end_time, NULL);
|
||||
- timersub(&end_time, start_time, &diff_time);
|
||||
+ do {
|
||||
+ struct uevent *uev;
|
||||
+ struct udev_device *dev;
|
||||
|
||||
- eclipse_ms = diff_time.tv_sec * 1000 + diff_time.tv_usec / 1000;
|
||||
+ dev = udev_monitor_receive_device(monitor);
|
||||
+ if (!dev) {
|
||||
+ condlog(0, "failed getting udev device");
|
||||
+ break;
|
||||
+ }
|
||||
+ uev = uevent_from_udev_device(dev);
|
||||
+ if (!uev)
|
||||
+ break;
|
||||
|
||||
- if (eclipse_ms == 0)
|
||||
- return true;
|
||||
+ list_add_tail(&uev->node, tmpq);
|
||||
+ n++;
|
||||
+ condlog(4, "received uevent \"%s %s\"", uev->action, uev->kernel);
|
||||
|
||||
- if (eclipse_ms > MAX_ACCUMULATION_TIME) {
|
||||
- condlog(2, "burst continued %lu ms, too long time, stopped", eclipse_ms);
|
||||
- return false;
|
||||
- }
|
||||
+ ev_poll.fd = fd;
|
||||
+ ev_poll.events = POLLIN;
|
||||
|
||||
- speed = (events * 1000) / eclipse_ms;
|
||||
- if (speed > MIN_BURST_SPEED)
|
||||
- return true;
|
||||
+ } while (n < MAX_UEVENTS && poll(&ev_poll, 1, 0) > 0);
|
||||
|
||||
- return false;
|
||||
+ return n;
|
||||
}
|
||||
|
||||
int uevent_listen(struct udev *udev)
|
||||
{
|
||||
int err = 2;
|
||||
struct udev_monitor *monitor = NULL;
|
||||
- int fd, socket_flags, events;
|
||||
- struct timeval start_time;
|
||||
- int timeout = 30;
|
||||
+ int fd, socket_flags;
|
||||
LIST_HEAD(uevlisten_tmp);
|
||||
|
||||
/*
|
||||
@@ -675,59 +670,30 @@ int uevent_listen(struct udev *udev)
|
||||
goto out;
|
||||
}
|
||||
|
||||
- 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;
|
||||
- struct pollfd ev_poll;
|
||||
- int poll_timeout;
|
||||
- int fdcount;
|
||||
+ int fdcount, events;
|
||||
+ struct pollfd ev_poll = { .fd = fd, .events = POLLIN, };
|
||||
|
||||
- memset(&ev_poll, 0, sizeof(struct pollfd));
|
||||
- ev_poll.fd = fd;
|
||||
- ev_poll.events = POLLIN;
|
||||
- poll_timeout = timeout * 1000;
|
||||
- errno = 0;
|
||||
- fdcount = poll(&ev_poll, 1, poll_timeout);
|
||||
- if (fdcount > 0 && ev_poll.revents & POLLIN) {
|
||||
- timeout = uevent_burst(&start_time, events + 1) ? 1 : 0;
|
||||
- dev = udev_monitor_receive_device(monitor);
|
||||
- if (!dev) {
|
||||
- condlog(0, "failed getting udev device");
|
||||
- continue;
|
||||
- }
|
||||
- uev = uevent_from_udev_device(dev);
|
||||
- if (!uev)
|
||||
- continue;
|
||||
- list_add_tail(&uev->node, &uevlisten_tmp);
|
||||
- events++;
|
||||
- continue;
|
||||
- }
|
||||
+ fdcount = poll(&ev_poll, 1, -1);
|
||||
if (fdcount < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
- condlog(0, "error receiving "
|
||||
- "uevent message: %m");
|
||||
+ condlog(0, "error receiving uevent message: %m");
|
||||
err = -errno;
|
||||
break;
|
||||
}
|
||||
- if (!list_empty(&uevlisten_tmp)) {
|
||||
- /*
|
||||
- * Queue uevents and poke service pthread.
|
||||
- */
|
||||
- condlog(3, "Forwarding %d uevents", events);
|
||||
- pthread_mutex_lock(uevq_lockp);
|
||||
- list_splice_tail_init(&uevlisten_tmp, &uevq);
|
||||
- pthread_cond_signal(uev_condp);
|
||||
- pthread_mutex_unlock(uevq_lockp);
|
||||
- events = 0;
|
||||
- }
|
||||
- gettimeofday(&start_time, NULL);
|
||||
- timeout = 30;
|
||||
+ events = uevent_receive_events(fd, &uevlisten_tmp, monitor);
|
||||
+ if (events <= 0)
|
||||
+ continue;
|
||||
+
|
||||
+ condlog(4, "Forwarding %d uevents", events);
|
||||
+ pthread_mutex_lock(uevq_lockp);
|
||||
+ list_splice_tail_init(&uevlisten_tmp, &uevq);
|
||||
+ pthread_cond_signal(uev_condp);
|
||||
+ pthread_mutex_unlock(uevq_lockp);
|
||||
}
|
||||
pthread_cleanup_pop(1);
|
||||
pthread_cleanup_pop(1);
|
||||
356
0203-libmultipath-uevent-use-struct-to-pass-parameters-ar.patch
Normal file
356
0203-libmultipath-uevent-use-struct-to-pass-parameters-ar.patch
Normal file
@ -0,0 +1,356 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Martin Wilck <mwilck@suse.com>
|
||||
Date: Wed, 30 Mar 2022 00:06:15 +0200
|
||||
Subject: [PATCH] libmultipath: uevent: use struct to pass parameters around
|
||||
|
||||
libmultipath: uevent_dispatch(): just grab config once
|
||||
|
||||
Introduce struct uevent_filter_state to pass parameters around.
|
||||
This simplifies the function signatures and allows for easy extension
|
||||
later.
|
||||
|
||||
Instead of grabbing multipath config repeatedly, do it just
|
||||
once per dispatcher iteration, and pass the pointer around in
|
||||
struct uevent_filter_state. We shouldn't use different configs
|
||||
for different paths in a single iteration, anyway.
|
||||
|
||||
Also, properly constify get_uid_attribute_by_attrs() and
|
||||
pp->uid_attribute.
|
||||
|
||||
Signed-off-by: Martin Wilck <mwilck@suse.com>
|
||||
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
---
|
||||
libmultipath/config.c | 6 +--
|
||||
libmultipath/config.h | 4 +-
|
||||
libmultipath/discovery.c | 2 +-
|
||||
libmultipath/structs.h | 2 +-
|
||||
libmultipath/uevent.c | 110 +++++++++++++++++----------------------
|
||||
libmultipath/uevent.h | 3 +-
|
||||
tests/uevent.c | 2 +-
|
||||
7 files changed, 58 insertions(+), 71 deletions(-)
|
||||
|
||||
diff --git a/libmultipath/config.c b/libmultipath/config.c
|
||||
index f31200a3..bd8296bf 100644
|
||||
--- a/libmultipath/config.c
|
||||
+++ b/libmultipath/config.c
|
||||
@@ -1112,10 +1112,10 @@ out:
|
||||
return 1;
|
||||
}
|
||||
|
||||
-char *get_uid_attribute_by_attrs(struct config *conf,
|
||||
- const char *path_dev)
|
||||
+const char *get_uid_attribute_by_attrs(const struct config *conf,
|
||||
+ const char *path_dev)
|
||||
{
|
||||
- vector uid_attrs = &conf->uid_attrs;
|
||||
+ const struct _vector *uid_attrs = &conf->uid_attrs;
|
||||
int j;
|
||||
char *att, *col;
|
||||
|
||||
diff --git a/libmultipath/config.h b/libmultipath/config.h
|
||||
index 5807ac68..d3abbaea 100644
|
||||
--- a/libmultipath/config.h
|
||||
+++ b/libmultipath/config.h
|
||||
@@ -329,7 +329,7 @@ void libmp_put_multipath_config(void *);
|
||||
void put_multipath_config(void *);
|
||||
|
||||
int parse_uid_attrs(char *uid_attrs, struct config *conf);
|
||||
-char *get_uid_attribute_by_attrs(struct config *conf,
|
||||
- const char *path_dev);
|
||||
+const char *get_uid_attribute_by_attrs(const struct config *conf,
|
||||
+ const char *path_dev);
|
||||
|
||||
#endif
|
||||
diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
|
||||
index 22d114b3..186423e0 100644
|
||||
--- a/libmultipath/discovery.c
|
||||
+++ b/libmultipath/discovery.c
|
||||
@@ -2071,7 +2071,7 @@ fix_broken_nvme_wwid(struct path *pp, const char *value, size_t size)
|
||||
}
|
||||
|
||||
static int
|
||||
-get_udev_uid(struct path * pp, char *uid_attribute, struct udev_device *udev)
|
||||
+get_udev_uid(struct path * pp, const char *uid_attribute, struct udev_device *udev)
|
||||
{
|
||||
ssize_t len;
|
||||
const char *value;
|
||||
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
|
||||
index 2f69e831..423c8b78 100644
|
||||
--- a/libmultipath/structs.h
|
||||
+++ b/libmultipath/structs.h
|
||||
@@ -350,7 +350,7 @@ struct path {
|
||||
int detect_prio;
|
||||
int detect_checker;
|
||||
int tpgs;
|
||||
- char * uid_attribute;
|
||||
+ const char *uid_attribute;
|
||||
char * getuid;
|
||||
struct prio prio;
|
||||
struct checker checker;
|
||||
diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c
|
||||
index c3984fef..4ef7181c 100644
|
||||
--- a/libmultipath/uevent.c
|
||||
+++ b/libmultipath/uevent.c
|
||||
@@ -65,6 +65,12 @@ static uev_trigger *my_uev_trigger;
|
||||
static void *my_trigger_data;
|
||||
static int servicing_uev;
|
||||
|
||||
+struct uevent_filter_state {
|
||||
+ struct list_head uevq;
|
||||
+ struct list_head *old_tail;
|
||||
+ struct config *conf;
|
||||
+};
|
||||
+
|
||||
int is_uevent_busy(void)
|
||||
{
|
||||
int empty;
|
||||
@@ -160,40 +166,24 @@ int uevent_get_env_positive_int(const struct uevent *uev,
|
||||
}
|
||||
|
||||
void
|
||||
-uevent_get_wwid(struct uevent *uev)
|
||||
+uevent_get_wwid(struct uevent *uev, const struct config *conf)
|
||||
{
|
||||
- char *uid_attribute;
|
||||
+ const char *uid_attribute;
|
||||
const char *val;
|
||||
- struct config * conf;
|
||||
|
||||
- conf = get_multipath_config();
|
||||
- pthread_cleanup_push(put_multipath_config, conf);
|
||||
uid_attribute = get_uid_attribute_by_attrs(conf, uev->kernel);
|
||||
- pthread_cleanup_pop(1);
|
||||
-
|
||||
val = uevent_get_env_var(uev, uid_attribute);
|
||||
if (val)
|
||||
uev->wwid = val;
|
||||
}
|
||||
|
||||
-static bool uevent_need_merge(void)
|
||||
+static bool uevent_need_merge(const struct config *conf)
|
||||
{
|
||||
- struct config * conf;
|
||||
- bool need_merge = false;
|
||||
-
|
||||
- conf = get_multipath_config();
|
||||
- if (VECTOR_SIZE(&conf->uid_attrs) > 0)
|
||||
- need_merge = true;
|
||||
- put_multipath_config(conf);
|
||||
-
|
||||
- return need_merge;
|
||||
+ return VECTOR_SIZE(&conf->uid_attrs) > 0;
|
||||
}
|
||||
|
||||
-static bool uevent_can_discard(struct uevent *uev)
|
||||
+static bool uevent_can_discard(struct uevent *uev, const struct config *conf)
|
||||
{
|
||||
- int invalid = 0;
|
||||
- struct config * conf;
|
||||
-
|
||||
/*
|
||||
* do not filter dm devices by devnode
|
||||
*/
|
||||
@@ -202,15 +192,10 @@ static bool uevent_can_discard(struct uevent *uev)
|
||||
/*
|
||||
* filter paths devices by devnode
|
||||
*/
|
||||
- conf = get_multipath_config();
|
||||
- pthread_cleanup_push(put_multipath_config, conf);
|
||||
if (filter_devnode(conf->blist_devnode, conf->elist_devnode,
|
||||
uev->kernel) > 0)
|
||||
- invalid = 1;
|
||||
- pthread_cleanup_pop(1);
|
||||
-
|
||||
- if (invalid)
|
||||
return true;
|
||||
+
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -354,29 +339,28 @@ static void uevent_delete_simple(struct uevent *to_delete)
|
||||
free(to_delete);
|
||||
}
|
||||
|
||||
-static void
|
||||
-uevent_prepare(struct list_head *tmpq, const struct list_head *stop)
|
||||
+static void uevent_prepare(struct uevent_filter_state *st)
|
||||
{
|
||||
struct uevent *uev, *tmp;
|
||||
|
||||
- list_for_some_entry_reverse_safe(uev, tmp, tmpq, stop, node) {
|
||||
- if (uevent_can_discard(uev)) {
|
||||
+ list_for_some_entry_reverse_safe(uev, tmp, &st->uevq, st->old_tail, node) {
|
||||
+ if (uevent_can_discard(uev, st->conf)) {
|
||||
uevent_delete_simple(uev);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strncmp(uev->kernel, "dm-", 3) &&
|
||||
- uevent_need_merge())
|
||||
- uevent_get_wwid(uev);
|
||||
+ uevent_need_merge(st->conf))
|
||||
+ uevent_get_wwid(uev, st->conf);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
-uevent_filter(struct uevent *later, struct list_head *tmpq, struct list_head **stop)
|
||||
+uevent_filter(struct uevent *later, struct uevent_filter_state *st)
|
||||
{
|
||||
struct uevent *earlier, *tmp;
|
||||
|
||||
- list_for_some_entry_reverse_safe(earlier, tmp, &later->node, tmpq, node) {
|
||||
+ list_for_some_entry_reverse_safe(earlier, tmp, &later->node, &st->uevq, node) {
|
||||
/*
|
||||
* filter unnessary earlier uevents
|
||||
* by the later uevent
|
||||
@@ -386,17 +370,16 @@ uevent_filter(struct uevent *later, struct list_head *tmpq, struct list_head **s
|
||||
earlier->kernel, earlier->action,
|
||||
later->kernel, later->action);
|
||||
|
||||
- uevent_delete_from_list(earlier, &tmp, stop);
|
||||
+ uevent_delete_from_list(earlier, &tmp, &st->old_tail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-static void
|
||||
-uevent_merge(struct uevent *later, struct list_head *tmpq, struct list_head **stop)
|
||||
+static void uevent_merge(struct uevent *later, struct uevent_filter_state *st)
|
||||
{
|
||||
struct uevent *earlier, *tmp;
|
||||
|
||||
- list_for_some_entry_reverse_safe(earlier, tmp, &later->node, tmpq, node) {
|
||||
+ list_for_some_entry_reverse_safe(earlier, tmp, &later->node, &st->uevq, node) {
|
||||
if (merge_need_stop(earlier, later))
|
||||
break;
|
||||
/*
|
||||
@@ -408,8 +391,8 @@ uevent_merge(struct uevent *later, struct list_head *tmpq, struct list_head **st
|
||||
later->action, later->kernel, later->wwid);
|
||||
|
||||
/* See comment in uevent_delete_from_list() */
|
||||
- if (&earlier->node == *stop)
|
||||
- *stop = earlier->node.prev;
|
||||
+ if (&earlier->node == st->old_tail)
|
||||
+ st->old_tail = earlier->node.prev;
|
||||
|
||||
list_move(&earlier->node, &later->merge_node);
|
||||
list_splice_init(&earlier->merge_node,
|
||||
@@ -418,16 +401,15 @@ uevent_merge(struct uevent *later, struct list_head *tmpq, struct list_head **st
|
||||
}
|
||||
}
|
||||
|
||||
-static void
|
||||
-merge_uevq(struct list_head *tmpq, struct list_head *stop)
|
||||
+static void merge_uevq(struct uevent_filter_state *st)
|
||||
{
|
||||
struct uevent *later;
|
||||
|
||||
- uevent_prepare(tmpq, stop);
|
||||
- list_for_some_entry_reverse(later, tmpq, stop, node) {
|
||||
- uevent_filter(later, tmpq, &stop);
|
||||
- if(uevent_need_merge())
|
||||
- uevent_merge(later, tmpq, &stop);
|
||||
+ uevent_prepare(st);
|
||||
+ list_for_some_entry_reverse(later, &st->uevq, st->old_tail, node) {
|
||||
+ uevent_filter(later, st);
|
||||
+ if(uevent_need_merge(st->conf))
|
||||
+ uevent_merge(later, st);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,41 +461,45 @@ static void cleanup_global_uevq(void *arg __attribute__((unused)))
|
||||
int uevent_dispatch(int (*uev_trigger)(struct uevent *, void * trigger_data),
|
||||
void * trigger_data)
|
||||
{
|
||||
- LIST_HEAD(uevq_work);
|
||||
+ struct uevent_filter_state filter_state;
|
||||
|
||||
+ INIT_LIST_HEAD(&filter_state.uevq);
|
||||
my_uev_trigger = uev_trigger;
|
||||
my_trigger_data = trigger_data;
|
||||
|
||||
mlockall(MCL_CURRENT | MCL_FUTURE);
|
||||
|
||||
- pthread_cleanup_push(cleanup_uevq, &uevq_work);
|
||||
+ pthread_cleanup_push(cleanup_uevq, &filter_state.uevq);
|
||||
while (1) {
|
||||
- struct list_head *stop;
|
||||
-
|
||||
pthread_cleanup_push(cleanup_mutex, uevq_lockp);
|
||||
pthread_mutex_lock(uevq_lockp);
|
||||
|
||||
- servicing_uev = !list_empty(&uevq_work);
|
||||
+ servicing_uev = !list_empty(&filter_state.uevq);
|
||||
|
||||
- while (list_empty(&uevq_work) && list_empty(&uevq))
|
||||
+ while (list_empty(&filter_state.uevq) && list_empty(&uevq))
|
||||
pthread_cond_wait(uev_condp, uevq_lockp);
|
||||
|
||||
servicing_uev = 1;
|
||||
/*
|
||||
- * "stop" is the list element towards which merge_uevq()
|
||||
- * will iterate: the last element of uevq_work before
|
||||
- * appending new uevents. If uveq_is empty, uevq_work.prev
|
||||
- * equals &uevq_work, which is what we need.
|
||||
+ * "old_tail" is the list element towards which merge_uevq()
|
||||
+ * will iterate: the last element of uevq before
|
||||
+ * appending new uevents. If uveq empty, uevq.prev
|
||||
+ * equals &uevq, which is what we need.
|
||||
*/
|
||||
- stop = uevq_work.prev;
|
||||
- list_splice_tail_init(&uevq, &uevq_work);
|
||||
+ filter_state.old_tail = filter_state.uevq.prev;
|
||||
+ list_splice_tail_init(&uevq, &filter_state.uevq);
|
||||
pthread_cleanup_pop(1);
|
||||
|
||||
if (!my_uev_trigger)
|
||||
break;
|
||||
|
||||
- merge_uevq(&uevq_work, stop);
|
||||
- service_uevq(&uevq_work);
|
||||
+
|
||||
+ pthread_cleanup_push(put_multipath_config, filter_state.conf);
|
||||
+ filter_state.conf = get_multipath_config();
|
||||
+ merge_uevq(&filter_state);
|
||||
+ pthread_cleanup_pop(1);
|
||||
+
|
||||
+ service_uevq(&filter_state.uevq);
|
||||
}
|
||||
pthread_cleanup_pop(1);
|
||||
condlog(3, "Terminating uev service queue");
|
||||
diff --git a/libmultipath/uevent.h b/libmultipath/uevent.h
|
||||
index 61ca1b56..53a7ca29 100644
|
||||
--- a/libmultipath/uevent.h
|
||||
+++ b/libmultipath/uevent.h
|
||||
@@ -10,6 +10,7 @@
|
||||
#define OBJECT_SIZE 512
|
||||
|
||||
struct udev;
|
||||
+struct config;
|
||||
|
||||
struct uevent {
|
||||
struct list_head node;
|
||||
@@ -31,7 +32,7 @@ int uevent_listen(struct udev *udev);
|
||||
int uevent_dispatch(int (*store_uev)(struct uevent *, void * trigger_data),
|
||||
void * trigger_data);
|
||||
bool uevent_is_mpath(const struct uevent *uev);
|
||||
-void uevent_get_wwid(struct uevent *uev);
|
||||
+void uevent_get_wwid(struct uevent *uev, const struct config *conf);
|
||||
|
||||
int uevent_get_env_positive_int(const struct uevent *uev,
|
||||
const char *attr);
|
||||
diff --git a/tests/uevent.c b/tests/uevent.c
|
||||
index 648ff268..e237a208 100644
|
||||
--- a/tests/uevent.c
|
||||
+++ b/tests/uevent.c
|
||||
@@ -111,7 +111,7 @@ static void test_uid_attrs(void **state)
|
||||
static void test_wwid(void **state)
|
||||
{
|
||||
struct uevent *uev = *state;
|
||||
- uevent_get_wwid(uev);
|
||||
+ uevent_get_wwid(uev, &conf);
|
||||
|
||||
assert_string_equal(uev->wwid, WWID);
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Martin Wilck <mwilck@suse.com>
|
||||
Date: Fri, 5 Jan 2024 18:51:02 +0100
|
||||
Subject: [PATCH] libmultipath: is_uevent_busy(): check servicing_uev under
|
||||
lock
|
||||
|
||||
This fixes a coverity-reported defect (413384 Data race condition).
|
||||
Indeed, we always set servicing_uev with the lock held, so it makes
|
||||
sense to read it with the lock held, too.
|
||||
|
||||
Signed-off-by: Martin Wilck <mwilck@suse.com>
|
||||
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
---
|
||||
libmultipath/uevent.c | 5 +++--
|
||||
1 file changed, 3 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c
|
||||
index 4ef7181c..8cd928a9 100644
|
||||
--- a/libmultipath/uevent.c
|
||||
+++ b/libmultipath/uevent.c
|
||||
@@ -73,12 +73,13 @@ struct uevent_filter_state {
|
||||
|
||||
int is_uevent_busy(void)
|
||||
{
|
||||
- int empty;
|
||||
+ int empty, servicing;
|
||||
|
||||
pthread_mutex_lock(uevq_lockp);
|
||||
empty = list_empty(&uevq);
|
||||
+ servicing = servicing_uev;
|
||||
pthread_mutex_unlock(uevq_lockp);
|
||||
- return (!empty || servicing_uev);
|
||||
+ return (!empty || servicing);
|
||||
}
|
||||
|
||||
struct uevent * alloc_uevent (void)
|
||||
@ -0,0 +1,63 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Date: Wed, 21 Jan 2026 16:03:12 -0500
|
||||
Subject: [PATCH] multipathd: make "multipathd show status" busy checker better
|
||||
|
||||
while uevent_listen() was grabbing new uevents, "multipathd show status"
|
||||
would still show show busy as "False". Add a check there, to make catch
|
||||
multipathd's uevent processing earlier. Also, access servicing_uev (as
|
||||
well as the new variable, adding_uev) atomically, just to make sure that
|
||||
the compiler doesn't do stupid things trying to optimize them.
|
||||
|
||||
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Reviewed-by: Martin Wilck <mwilck@suse.com>
|
||||
---
|
||||
libmultipath/uevent.c | 9 +++++++--
|
||||
1 file changed, 7 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c
|
||||
index 8cd928a9..c230e963 100644
|
||||
--- a/libmultipath/uevent.c
|
||||
+++ b/libmultipath/uevent.c
|
||||
@@ -64,6 +64,7 @@ static pthread_cond_t *uev_condp = &uev_cond;
|
||||
static uev_trigger *my_uev_trigger;
|
||||
static void *my_trigger_data;
|
||||
static int servicing_uev;
|
||||
+static int adding_uev; /* uatomic access only */
|
||||
|
||||
struct uevent_filter_state {
|
||||
struct list_head uevq;
|
||||
@@ -73,13 +74,14 @@ struct uevent_filter_state {
|
||||
|
||||
int is_uevent_busy(void)
|
||||
{
|
||||
- int empty, servicing;
|
||||
+ int empty, servicing, adding;
|
||||
|
||||
pthread_mutex_lock(uevq_lockp);
|
||||
empty = list_empty(&uevq);
|
||||
servicing = servicing_uev;
|
||||
+ adding = uatomic_read(&adding_uev);
|
||||
pthread_mutex_unlock(uevq_lockp);
|
||||
- return (!empty || servicing);
|
||||
+ return (!empty || servicing || adding);
|
||||
}
|
||||
|
||||
struct uevent * alloc_uevent (void)
|
||||
@@ -663,6 +665,7 @@ int uevent_listen(struct udev *udev)
|
||||
int fdcount, events;
|
||||
struct pollfd ev_poll = { .fd = fd, .events = POLLIN, };
|
||||
|
||||
+ uatomic_set(&adding_uev, 0);
|
||||
fdcount = poll(&ev_poll, 1, -1);
|
||||
if (fdcount < 0) {
|
||||
if (errno == EINTR)
|
||||
@@ -672,6 +675,8 @@ int uevent_listen(struct udev *udev)
|
||||
err = -errno;
|
||||
break;
|
||||
}
|
||||
+ uatomic_set(&adding_uev, 1);
|
||||
+
|
||||
events = uevent_receive_events(fd, &uevlisten_tmp, monitor);
|
||||
if (events <= 0)
|
||||
continue;
|
||||
@ -1,6 +1,6 @@
|
||||
Name: device-mapper-multipath
|
||||
Version: 0.8.7
|
||||
Release: 42%{?dist}
|
||||
Release: 43%{?dist}
|
||||
Summary: Tools to manage multipath devices using device-mapper
|
||||
License: GPLv2
|
||||
URL: http://christophe.varoqui.free.fr/
|
||||
@ -207,6 +207,14 @@ Patch0194: 0194-multipathd-Fix-tracking-of-old-PR-key.patch
|
||||
Patch0195: 0195-multipathd-Fix-race-while-registering-PR-key.patch
|
||||
Patch0196: 0196-mpathpersist-Fix-REPORT-CAPABILITIES-output.patch
|
||||
Patch0197: 0197-multipath-tools-update-NFINIDAT-InfiniBox-config-in-.patch
|
||||
Patch0198: 0198-multipathd-print-path-offline-message-even-without-a.patch
|
||||
Patch0199: 0199-libmultipath-improve-cleanup-of-uevent-queues-on-exi.patch
|
||||
Patch0200: 0200-uevent_dispatch-use-while-in-wait-loop.patch
|
||||
Patch0201: 0201-libmultipath-uevent_dispatch-process-uevents-one-by-.patch
|
||||
Patch0202: 0202-libmultipath-uevent_listen-don-t-delay-uevents.patch
|
||||
Patch0203: 0203-libmultipath-uevent-use-struct-to-pass-parameters-ar.patch
|
||||
Patch0204: 0204-libmultipath-is_uevent_busy-check-servicing_uev-unde.patch
|
||||
Patch0205: 0205-multipathd-make-multipathd-show-status-busy-checker-.patch
|
||||
|
||||
# runtime
|
||||
Requires: %{name}-libs = %{version}-%{release}
|
||||
@ -409,6 +417,22 @@ fi
|
||||
%{_pkgconfdir}/libdmmp.pc
|
||||
|
||||
%changelog
|
||||
* Thu Jan 22 2026 Benjamin Marzinski <bmarzins@redhat.com> - 0.8.7-43
|
||||
- Add 0198-multipathd-print-path-offline-message-even-without-a.patch
|
||||
* Fixes RHEL-133814 ("log_checker_err is not printing messages
|
||||
repeatedly for failed path [rhel-9]")
|
||||
- Add 0199-libmultipath-improve-cleanup-of-uevent-queues-on-exi.patch
|
||||
- Add 0200-uevent_dispatch-use-while-in-wait-loop.patch
|
||||
- Add 0201-libmultipath-uevent_dispatch-process-uevents-one-by-.patch
|
||||
- Add 0202-libmultipath-uevent_listen-don-t-delay-uevents.patch
|
||||
- Add 0203-libmultipath-uevent-use-struct-to-pass-parameters-ar.patch
|
||||
- Add 0204-libmultipath-is_uevent_busy-check-servicing_uev-unde.patch
|
||||
- Add 0205-multipathd-make-multipathd-show-status-busy-checker-.patch
|
||||
* Fixes RHEL-135904 (VM reboot in RHOSP environment fails with error
|
||||
"Could not open '/dev/dm-95': No such file or directory")
|
||||
- Resolves: RHEL-133814
|
||||
- Resolves: RHEL-135904
|
||||
|
||||
* Wed Nov 19 2025 Benjamin Marzinski <bmarzins@redhat.com> - 0.8.7-42
|
||||
- Add 0197-multipath-tools-update-NFINIDAT-InfiniBox-config-in-.patch
|
||||
* Fixes RHEL-128396 ("Update the multipath.conf stanza for Infinidat
|
||||
|
||||
Loading…
Reference in New Issue
Block a user