<div dir="ltr">Applied.<div>Thanks.</div></div><div class="gmail_extra"><br><div class="gmail_quote">On Tue, Feb 28, 2017 at 8:05 AM,  <span dir="ltr"><<a href="mailto:tang.junhui@zte.com.cn" target="_blank">tang.junhui@zte.com.cn</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">From: "tang.junhui" <<a href="mailto:tang.junhui@zte.com.cn">tang.junhui@zte.com.cn</a>><br>
<br>
This patch used to improve processing efficiency for addition and deletion<br>
of multipath devices.<br>
<br>
This patch is tested pass by ZTE multipath automatic testing system.<br>
The modification reduces the system consumption(such as CPU) and shortens<br>
the processing time obviously in scene of massive multipath devices<br>
addition or deletion.<br>
<br>
The main processing flow of code is:<br>
1) add uid_attrs configuration in the defaults section:<br>
   It is configured udev attribute which providing a unique path identifier<br>
   for corresponding type of path devices. If this field is configured and<br>
   matched with type of device, it would override any other methods<br>
   providing for device unique identifier in config file, and it would<br>
   activate merging uevents according to the identifier to promote effiecncy<br>
   in processing uevents. Tt has no default value, so defaultly only uevents<br>
   filtering works, and uevents merging does not works, if users want to<br>
   identify path by udev attribute and to activate merging uevents for SCSI<br>
   and DAS device, they can set it's value as:<br>
   "sd:ID_SERIAL dasd:ID_UID"<br>
2) uevents accumulation in uevents burst scene:<br>
   wait one seconds for more uevents in uevent_listen() in uevents burst<br>
   situations<br>
3) uevents preparing, filtering and merging:<br>
   discard unuse uevents and fetch path idendifier from uevents;<br>
   filter uevents;<br>
   merge uevents.<br>
4) uevents proccessing:<br>
   proccess the merged uevents in uev->merge_node list without calling<br>
   domap();<br>
   proccess the last uevents uev with calling domap().<br>
<br>
Signed-off-by: tang.junhui <<a href="mailto:tang.junhui@zte.com.cn">tang.junhui@zte.com.cn</a>><br>
---<br>
 libmultipath/config.c      |   3 +<br>
 libmultipath/config.h      |   1 +<br>
 libmultipath/dict.c        |   3 +<br>
 libmultipath/discovery.c   |   5 +-<br>
 libmultipath/discovery.h   |   2 +-<br>
 libmultipath/list.h        |  41 ++++++<br>
 libmultipath/propsel.c     |   7 +<br>
 libmultipath/uevent.c      | 320 ++++++++++++++++++++++++++++++<wbr>+++++++++++++--<br>
 libmultipath/uevent.h      |   2 +<br>
 libmultipath/util.c        |  42 ++++++<br>
 libmultipath/util.h        |   1 +<br>
 multipath/multipath.conf.5 |  18 +++<br>
 multipathd/cli_handlers.c  |   4 +-<br>
 multipathd/main.c          |  93 +++++--------<br>
 multipathd/main.h          |   4 +-<br>
 15 files changed, 471 insertions(+), 75 deletions(-)<br>
<br>
diff --git a/libmultipath/config.c b/libmultipath/config.c<br>
index 9d3f3e1..bb6619b 100644<br>
--- a/libmultipath/config.c<br>
+++ b/libmultipath/config.c<br>
@@ -493,6 +493,9 @@ free_config (struct config * conf)<br>
        if (conf->uid_attribute)<br>
                FREE(conf->uid_attribute);<br>
<br>
+       if (conf->uid_attrs)<br>
+               FREE(conf->uid_attrs);<br>
+<br>
        if (conf->getuid)<br>
                FREE(conf->getuid);<br>
<br>
diff --git a/libmultipath/config.h b/libmultipath/config.h<br>
index 9a90745..4f1d596 100644<br>
--- a/libmultipath/config.h<br>
+++ b/libmultipath/config.h<br>
@@ -164,6 +164,7 @@ struct config {<br>
<br>
        char * multipath_dir;<br>
        char * selector;<br>
+       char * uid_attrs;<br>
        char * uid_attribute;<br>
        char * getuid;<br>
        char * features;<br>
diff --git a/libmultipath/dict.c b/libmultipath/dict.c<br>
index bababdb..82066f6 100644<br>
--- a/libmultipath/dict.c<br>
+++ b/libmultipath/dict.c<br>
@@ -249,6 +249,8 @@ declare_ovr_snprint(selector, print_str)<br>
 declare_mp_handler(selector, set_str)<br>
 declare_mp_snprint(selector, print_str)<br>
<br>
+declare_def_handler(uid_<wbr>attrs, set_str)<br>
+declare_def_snprint(uid_<wbr>attrs, print_str)<br>
 declare_def_handler(uid_<wbr>attribute, set_str)<br>
 declare_def_snprint_defstr(<wbr>uid_attribute, print_str, DEFAULT_UID_ATTRIBUTE)<br>
 declare_ovr_handler(uid_<wbr>attribute, set_str)<br>
@@ -1396,6 +1398,7 @@ init_keywords(vector keywords)<br>
        install_keyword("multipath_<wbr>dir", &def_multipath_dir_handler, &snprint_def_multipath_dir);<br>
        install_keyword("path_<wbr>selector", &def_selector_handler, &snprint_def_selector);<br>
        install_keyword("path_<wbr>grouping_policy", &def_pgpolicy_handler, &snprint_def_pgpolicy);<br>
+       install_keyword("uid_attrs", &def_uid_attrs_handler, &snprint_def_uid_attrs);<br>
        install_keyword("uid_<wbr>attribute", &def_uid_attribute_handler, &snprint_def_uid_attribute);<br>
        install_keyword("getuid_<wbr>callout", &def_getuid_handler, &snprint_def_getuid);<br>
        install_keyword("prio", &def_prio_name_handler, &snprint_def_prio_name);<br>
diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c<br>
index 4e99845..7398040 100644<br>
--- a/libmultipath/discovery.c<br>
+++ b/libmultipath/discovery.c<br>
@@ -34,7 +34,7 @@<br>
<br>
 int<br>
 alloc_path_with_pathinfo (struct config *conf, struct udev_device *udevice,<br>
-                         int flag, struct path **pp_ptr)<br>
+                         char *wwid, int flag, struct path **pp_ptr)<br>
 {<br>
        int err = PATHINFO_FAILED;<br>
        struct path * pp;<br>
@@ -52,6 +52,9 @@ alloc_path_with_pathinfo (struct config *conf, struct udev_device *udevice,<br>
        if (!pp)<br>
                return PATHINFO_FAILED;<br>
<br>
+       if(wwid)<br>
+               strncpy(pp->wwid, wwid, sizeof(pp->wwid));<br>
+<br>
        if (safe_sprintf(pp->dev, "%s", devname)) {<br>
                condlog(0, "pp->dev too small");<br>
        } else {<br>
diff --git a/libmultipath/discovery.h b/libmultipath/discovery.h<br>
index 3039268..d16a69a 100644<br>
--- a/libmultipath/discovery.h<br>
+++ b/libmultipath/discovery.h<br>
@@ -37,7 +37,7 @@ int path_offline (struct path *);<br>
 int get_state (struct path * pp, struct config * conf, int daemon);<br>
 int pathinfo (struct path * pp, struct config * conf, int mask);<br>
 int alloc_path_with_pathinfo (struct config *conf, struct udev_device *udevice,<br>
-                             int flag, struct path **pp_ptr);<br>
+                             char *wwid, int flag, struct path **pp_ptr);<br>
 int store_pathinfo (vector pathvec, struct config *conf,<br>
                    struct udev_device *udevice, int flag,<br>
                    struct path **pp_ptr);<br>
diff --git a/libmultipath/list.h b/libmultipath/list.h<br>
index ceaa381..2b1dcf3 100644<br>
--- a/libmultipath/list.h<br>
+++ b/libmultipath/list.h<br>
@@ -317,4 +317,45 @@ static inline void list_splice_tail_init(struct list_head *list,<br>
             &pos->member != (head);                                    \<br>
             pos = n, n = list_entry(n->member.next, typeof(*n), member))<br>
<br>
+/**<br>
+ * list_for_each_entry_reverse_<wbr>safe - iterate backwards over list of given type safe against removal of list entry<br>
+ * @pos:       the type * to use as a loop counter.<br>
+ * @n:         another type * to use as temporary storage<br>
+ * @head:      the head for your list.<br>
+ * @member:    the name of the list_struct within the struct.<br>
+ */<br>
+#define list_for_each_entry_reverse_<wbr>safe(pos, n, head, member)          \<br>
+       for (pos = list_entry((head)->prev, typeof(*pos), member),      \<br>
+                n = list_entry(pos->member.prev, typeof(*pos), member);\<br>
+            &pos->member != (head);                                    \<br>
+            pos = n, n = list_entry(n->member.prev, typeof(*n), member))<br>
+<br>
+/**<br>
+ * list_for_some_entry_safe - iterate list from the given begin node to the given end node safe against removal of list entry<br>
+ * @pos:       the type * to use as a loop counter.<br>
+ * @n:         another type * to use as temporary storage<br>
+ * @from:      the begin node of the iteration.<br>
+ * @to:                the end node of the iteration.<br>
+ * @member:    the name of the list_struct within the struct.<br>
+ */<br>
+#define list_for_some_entry_safe(pos, n, from, to, member)              \<br>
+       for (pos = list_entry((from)->next, typeof(*pos), member),      \<br>
+            n = list_entry(pos->member.next, typeof(*pos), member);    \<br>
+            &pos->member != (to);                                      \<br>
+            pos = n, n = list_entry(n->member.next, typeof(*n), member))<br>
+<br>
+/**<br>
+ * list_for_some_entry_reverse_<wbr>safe - iterate backwards list from the given begin node to the given end node safe against removal of list entry<br>
+ * @pos:       the type * to use as a loop counter.<br>
+ * @n:         another type * to use as temporary storage<br>
+ * @from:      the begin node of the iteration.<br>
+ * @to:                the end node of the iteration.<br>
+ * @member:    the name of the list_struct within the struct.<br>
+ */<br>
+#define list_for_some_entry_reverse_<wbr>safe(pos, n, from, to, member)      \<br>
+       for (pos = list_entry((from)->prev, typeof(*pos), member),      \<br>
+            n = list_entry(pos->member.prev, typeof(*pos), member);    \<br>
+            &pos->member != (to);                                      \<br>
+            pos = n, n = list_entry(n->member.prev, typeof(*n), member))<br>
+<br>
 #endif /* _LIST_H */<br>
diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c<br>
index bba8194..38eb424 100644<br>
--- a/libmultipath/propsel.c<br>
+++ b/libmultipath/propsel.c<br>
@@ -18,6 +18,7 @@<br>
 #include "prio.h"<br>
 #include "discovery.h"<br>
 #include "dict.h"<br>
+#include "util.h"<br>
 #include "prioritizers/alua_rtpg.h"<br>
 #include <inttypes.h><br>
<br>
@@ -344,6 +345,12 @@ int select_getuid(struct config *conf, struct path *pp)<br>
 {<br>
        char *origin;<br>
<br>
+       pp->uid_attribute = parse_uid_attribute_by_attrs(<wbr>conf->uid_attrs, pp->dev);<br>
+       if (pp->uid_attribute) {<br>
+               origin = "(setting: multipath.conf defaults section)";<br>
+               goto out;<br>
+       }<br>
+<br>
        pp_set_ovr(getuid);<br>
        pp_set_ovr(uid_attribute);<br>
        pp_set_hwe(getuid);<br>
diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c<br>
index 7edcce1..6e2527b 100644<br>
--- a/libmultipath/uevent.c<br>
+++ b/libmultipath/uevent.c<br>
@@ -24,6 +24,7 @@<br>
<br>
 #include <unistd.h><br>
 #include <stdio.h><br>
+#include <stdbool.h><br>
 #include <errno.h><br>
 #include <stdlib.h><br>
 #include <stddef.h><br>
@@ -38,6 +39,7 @@<br>
 #include <linux/netlink.h><br>
 #include <pthread.h><br>
 #include <sys/mman.h><br>
+#include <sys/time.h><br>
 #include <libudev.h><br>
 #include <errno.h><br>
<br>
@@ -46,6 +48,14 @@<br>
 #include "list.h"<br>
 #include "uevent.h"<br>
 #include "vector.h"<br>
+#include "structs.h"<br>
+#include "util.h"<br>
+#include "config.h"<br>
+#include "blacklist.h"<br>
+<br>
+#define MAX_ACCUMULATION_COUNT 2048<br>
+#define MAX_ACCUMULATION_TIME 30*1000<br>
+#define MIN_BURST_SPEED 10<br>
<br>
 typedef int (uev_trigger)(struct uevent *, void * trigger_data);<br>
<br>
@@ -72,48 +82,301 @@ struct uevent * alloc_uevent (void)<br>
 {<br>
        struct uevent *uev = MALLOC(sizeof(struct uevent));<br>
<br>
-       if (uev)<br>
+       if (uev) {<br>
                INIT_LIST_HEAD(&uev->node);<br>
+               INIT_LIST_HEAD(&uev->merge_<wbr>node);<br>
+       }<br>
<br>
        return uev;<br>
 }<br>
<br>
 void<br>
-service_uevq(struct list_head *tmpq)<br>
+uevq_cleanup(struct list_head *tmpq)<br>
 {<br>
        struct uevent *uev, *tmp;<br>
<br>
        list_for_each_entry_safe(uev, tmp, tmpq, node) {<br>
                list_del_init(&uev->node);<br>
<br>
-               if (my_uev_trigger && my_uev_trigger(uev, my_trigger_data))<br>
-                       condlog(0, "uevent trigger error");<br>
-<br>
                if (uev->udev)<br>
                        udev_device_unref(uev->udev);<br>
                FREE(uev);<br>
        }<br>
 }<br>
<br>
-static void uevent_cleanup(void *arg)<br>
+void<br>
+uevent_get_wwid(struct uevent *uev)<br>
 {<br>
-       struct udev *udev = arg;<br>
+       int i;<br>
+       char *uid_attribute;<br>
+       struct config * conf;<br>
<br>
-       condlog(3, "Releasing uevent_listen() resources");<br>
-       udev_unref(udev);<br>
+       conf = get_multipath_config();<br>
+       uid_attribute = parse_uid_attribute_by_attrs(<wbr>conf->uid_attrs, uev->kernel);<br>
+       put_multipath_config(conf);<br>
+<br>
+       if (!uid_attribute)<br>
+               return;<br>
+<br>
+       for (i = 0; uev->envp[i] != NULL; i++) {<br>
+               if (!strncmp(uev->envp[i], uid_attribute, strlen(uid_attribute)) &&<br>
+                   strlen(uev->envp[i]) > strlen(uid_attribute) &&<br>
+                   uev->envp[i][strlen(uid_<wbr>attribute)] == '=') {<br>
+                       uev->wwid = uev->envp[i] + strlen(uid_attribute) + 1;<br>
+                       break;<br>
+               }<br>
+       }<br>
+       free(uid_attribute);<br>
+}<br>
+<br>
+bool<br>
+uevent_need_merge(void)<br>
+{<br>
+       struct config * conf;<br>
+       bool need_merge = false;<br>
+<br>
+       conf = get_multipath_config();<br>
+       if (conf->uid_attrs)<br>
+               need_merge = true;<br>
+       put_multipath_config(conf);<br>
+<br>
+       return need_merge;<br>
+}<br>
+<br>
+bool<br>
+uevent_can_discard(struct uevent *uev)<br>
+{<br>
+       char *tmp;<br>
+       char a[11], b[11];<br>
+       struct config * conf;<br>
+<br>
+       /*<br>
+        * keep only block devices, discard partitions<br>
+        */<br>
+       tmp = strstr(uev->devpath, "/block/");<br>
+       if (tmp == NULL){<br>
+               condlog(4, "no /block/ in '%s'", uev->devpath);<br>
+               return true;<br>
+       }<br>
+       if (sscanf(tmp, "/block/%10s", a) != 1 ||<br>
+           sscanf(tmp, "/block/%10[^/]/%10s", a, b) == 2) {<br>
+               condlog(4, "discard event on %s", uev->devpath);<br>
+               return true;<br>
+       }<br>
+<br>
+       /*<br>
+        * do not filter dm devices by devnode<br>
+        */<br>
+       if (!strncmp(uev->kernel, "dm-", 3))<br>
+               return false;<br>
+       /*<br>
+        * filter paths devices by devnode<br>
+        */<br>
+       conf = get_multipath_config();<br>
+       if (filter_devnode(conf->blist_<wbr>devnode, conf->elist_devnode,<br>
+                          uev->kernel) > 0) {<br>
+               put_multipath_config(conf);<br>
+               return true;<br>
+       }<br>
+       put_multipath_config(conf);<br>
+<br>
+       return false;<br>
+}<br>
+<br>
+bool<br>
+uevent_can_filter(struct uevent *earlier, struct uevent *later)<br>
+{<br>
+<br>
+       /*<br>
+        * filter earlier uvents if path has removed later. Eg:<br>
+        * "add path1 |chang path1 |add path2 |remove path1"<br>
+        * can filter as:<br>
+        * "add path2 |remove path1"<br>
+        * uevents "add path1" and "chang path1" are filtered out<br>
+        */<br>
+       if (!strcmp(earlier->kernel, later->kernel) &&<br>
+               !strcmp(later->action, "remove") &&<br>
+               strncmp(later->kernel, "dm-", 3)) {<br>
+               return true;<br>
+       }<br>
+<br>
+       /*<br>
+        * filter change uvents if add uevents exist. Eg:<br>
+        * "change path1| add path1 |add path2"<br>
+        * can filter as:<br>
+        * "add path1 |add path2"<br>
+        * uevent "chang path1" is filtered out<br>
+        */<br>
+       if (!strcmp(earlier->kernel, later->kernel) &&<br>
+               !strcmp(earlier->action, "change") &&<br>
+               !strcmp(later->action, "add") &&<br>
+               strncmp(later->kernel, "dm-", 3)) {<br>
+               return true;<br>
+       }<br>
+<br>
+       return false;<br>
+}<br>
+<br>
+bool<br>
+merge_need_stop(struct uevent *earlier, struct uevent *later)<br>
+{<br>
+       /*<br>
+        * dm uevent do not try to merge with left uevents<br>
+        */<br>
+       if (!strncmp(later->kernel, "dm-", 3))<br>
+               return true;<br>
+<br>
+       /*<br>
+        * we can not make a jugement without wwid,<br>
+        * so it is sensible to stop merging<br>
+        */<br>
+       if (!earlier->wwid || !later->wwid)<br>
+               return true;<br>
+       /*<br>
+        * uevents merging stoped<br>
+        * when we meet an opposite action uevent from the same LUN to AVOID<br>
+        * "add path1 |remove path1 |add path2 |remove path2 |add path3"<br>
+        * to merge as "remove path1, path2" and "add path1, path2, path3"<br>
+        * OR<br>
+        * "remove path1 |add path1 |remove path2 |add path2 |remove path3"<br>
+        * to merge as "add path1, path2" and "remove path1, path2, path3"<br>
+        * SO<br>
+        * when we meet a non-change uevent from the same LUN<br>
+        * with the same wwid and different action<br>
+        * it would be better to stop merging.<br>
+        */<br>
+       if (!strcmp(earlier->wwid, later->wwid) &&<br>
+           strcmp(earlier->action, later->action) &&<br>
+           strcmp(earlier->action, "change") &&<br>
+           strcmp(later->action, "change"))<br>
+               return true;<br>
+<br>
+       return false;<br>
+}<br>
+<br>
+bool<br>
+uevent_can_merge(struct uevent *earlier, struct uevent *later)<br>
+{<br>
+       /* merge paths uevents<br>
+        * whose wwids exsit and are same<br>
+        * and actions are same,<br>
+        * and actions are addition or deletion<br>
+        */<br>
+       if (earlier->wwid && later->wwid &&<br>
+           !strcmp(earlier->wwid, later->wwid) &&<br>
+           !strcmp(earlier->action, later->action) &&<br>
+           strncmp(earlier->action, "change", 6) &&<br>
+           strncmp(earlier->kernel, "dm-", 3)) {<br>
+               return true;<br>
+       }<br>
+<br>
+       return false;<br>
 }<br>
<br>
 void<br>
-uevq_cleanup(struct list_head *tmpq)<br>
+uevent_prepare(struct list_head *tmpq)<br>
+{<br>
+       struct uevent *uev, *tmp;<br>
+<br>
+       list_for_each_entry_reverse_<wbr>safe(uev, tmp, tmpq, node) {<br>
+               if (uevent_can_discard(uev)) {<br>
+                       list_del_init(&uev->node);<br>
+                       if (uev->udev)<br>
+                               udev_device_unref(uev->udev);<br>
+                       FREE(uev);<br>
+                       continue;<br>
+               }<br>
+<br>
+               if (strncmp(uev->kernel, "dm-", 3) &&<br>
+                   uevent_need_merge())<br>
+                       uevent_get_wwid(uev);<br>
+       }<br>
+}<br>
+<br>
+void<br>
+uevent_filter(struct uevent *later, struct list_head *tmpq)<br>
+{<br>
+       struct uevent *earlier, *tmp;<br>
+<br>
+       list_for_some_entry_reverse_<wbr>safe(earlier, tmp, &later->node, tmpq, node) {<br>
+               /*<br>
+                * filter unnessary earlier uevents<br>
+                * by the later uevent<br>
+                */<br>
+               if (uevent_can_filter(earlier, later)) {<br>
+                       condlog(2, "uevent: %s-%s has filtered by uevent: %s-%s",<br>
+                               earlier->kernel, earlier->action,<br>
+                               later->kernel, later->action);<br>
+<br>
+                       list_del_init(&earlier->node);<br>
+                       if (earlier->udev)<br>
+                               udev_device_unref(earlier-><wbr>udev);<br>
+                       FREE(earlier);<br>
+               }<br>
+       }<br>
+}<br>
+<br>
+void<br>
+uevent_merge(struct uevent *later, struct list_head *tmpq)<br>
+{<br>
+       struct uevent *earlier, *tmp;<br>
+<br>
+       list_for_some_entry_reverse_<wbr>safe(earlier, tmp, &later->node, tmpq, node) {<br>
+               if (merge_need_stop(earlier, later))<br>
+                       break;<br>
+               /*<br>
+                * merge earlier uevents to the later uevent<br>
+                */<br>
+               if (uevent_can_merge(earlier, later)) {<br>
+                       condlog(2, "merged uevent: %s-%s-%s with uevent: %s-%s-%s",<br>
+                               earlier->action, earlier->kernel, earlier->wwid,<br>
+                               later->action, later->kernel, later->wwid);<br>
+<br>
+                       list_move(&earlier->node, &later->merge_node);<br>
+               }<br>
+       }<br>
+}<br>
+<br>
+void<br>
+merge_uevq(struct list_head *tmpq)<br>
+{<br>
+       struct uevent *later;<br>
+<br>
+       uevent_prepare(tmpq);<br>
+       list_for_each_entry_reverse(<wbr>later, tmpq, node) {<br>
+               uevent_filter(later, tmpq);<br>
+               if(uevent_need_merge())<br>
+                       uevent_merge(later, tmpq);<br>
+       }<br>
+}<br>
+<br>
+void<br>
+service_uevq(struct list_head *tmpq)<br>
 {<br>
        struct uevent *uev, *tmp;<br>
<br>
        list_for_each_entry_safe(uev, tmp, tmpq, node) {<br>
                list_del_init(&uev->node);<br>
+<br>
+               if (my_uev_trigger && my_uev_trigger(uev, my_trigger_data))<br>
+                       condlog(0, "uevent trigger error");<br>
+<br>
+               uevq_cleanup(&uev->merge_node)<wbr>;<br>
+<br>
+               if (uev->udev)<br>
+                       udev_device_unref(uev->udev);<br>
                FREE(uev);<br>
        }<br>
 }<br>
<br>
+static void uevent_cleanup(void *arg)<br>
+{<br>
+       struct udev *udev = arg;<br>
+<br>
+       condlog(3, "Releasing uevent_listen() resources");<br>
+       udev_unref(udev);<br>
+}<br>
+<br>
 /*<br>
  * Service the uevent queue.<br>
  */<br>
@@ -142,6 +405,7 @@ int uevent_dispatch(int (*uev_trigger)(struct uevent *, void * trigger_data),<br>
                pthread_mutex_unlock(uevq_<wbr>lockp);<br>
                if (!my_uev_trigger)<br>
                        break;<br>
+               merge_uevq(&uevq_tmp);<br>
                service_uevq(&uevq_tmp);<br>
        }<br>
        condlog(3, "Terminating uev service queue");<br>
@@ -442,11 +706,43 @@ struct uevent *uevent_from_udev_device(<wbr>struct udev_device *dev)<br>
        return uev;<br>
 }<br>
<br>
+bool uevent_burst(struct timeval *start_time, int events)<br>
+{<br>
+       struct timeval diff_time, end_time;<br>
+       unsigned long speed;<br>
+       unsigned long eclipse_ms;<br>
+<br>
+       if(events > MAX_ACCUMULATION_COUNT) {<br>
+               condlog(2, "burst got %u uevents, too much uevents, stopped", events);<br>
+               return false;<br>
+       }<br>
+<br>
+       gettimeofday(&end_time, NULL);<br>
+       timersub(&end_time, start_time, &diff_time);<br>
+<br>
+       eclipse_ms = diff_time.tv_sec * 1000 + diff_time.tv_usec / 1000;<br>
+<br>
+       if (eclipse_ms == 0)<br>
+               return true;<br>
+<br>
+       if (eclipse_ms > MAX_ACCUMULATION_TIME) {<br>
+               condlog(2, "burst continued %lu ms, too long time, stopped", eclipse_ms);<br>
+               return false;<br>
+       }<br>
+<br>
+       speed = (events * 1000) / eclipse_ms;<br>
+       if (speed > MIN_BURST_SPEED)<br>
+               return true;<br>
+<br>
+       return false;<br>
+}<br>
+<br>
 int uevent_listen(struct udev *udev)<br>
 {<br>
        int err = 2;<br>
        struct udev_monitor *monitor = NULL;<br>
        int fd, socket_flags, events;<br>
+       struct timeval start_time;<br>
        int need_failback = 1;<br>
        int timeout = 30;<br>
        LIST_HEAD(uevlisten_tmp);<br>
@@ -500,6 +796,7 @@ int uevent_listen(struct udev *udev)<br>
        }<br>
<br>
        events = 0;<br>
+       gettimeofday(&start_time, NULL);<br>
        while (1) {<br>
                struct uevent *uev;<br>
                struct udev_device *dev;<br>
@@ -514,7 +811,7 @@ int uevent_listen(struct udev *udev)<br>
                errno = 0;<br>
                fdcount = poll(&ev_poll, 1, poll_timeout);<br>
                if (fdcount && ev_poll.revents & POLLIN) {<br>
-                       timeout = 0;<br>
+                       timeout = uevent_burst(&start_time, events + 1) ? 1 : 0;<br>
                        dev = udev_monitor_receive_device(<wbr>monitor);<br>
                        if (!dev) {<br>
                                condlog(0, "failed getting udev device");<br>
@@ -547,6 +844,7 @@ int uevent_listen(struct udev *udev)<br>
                        pthread_mutex_unlock(uevq_<wbr>lockp);<br>
                        events = 0;<br>
                }<br>
+               gettimeofday(&start_time, NULL);<br>
                timeout = 30;<br>
        }<br>
        need_failback = 0;<br>
diff --git a/libmultipath/uevent.h b/libmultipath/uevent.h<br>
index 9d22dcd..61a4207 100644<br>
--- a/libmultipath/uevent.h<br>
+++ b/libmultipath/uevent.h<br>
@@ -17,11 +17,13 @@ struct udev;<br>
<br>
 struct uevent {<br>
        struct list_head node;<br>
+       struct list_head merge_node;<br>
        struct udev_device *udev;<br>
        char buffer[HOTPLUG_BUFFER_SIZE + OBJECT_SIZE];<br>
        char *devpath;<br>
        char *action;<br>
        char *kernel;<br>
+       char *wwid;<br>
        unsigned long seqnum;<br>
        char *envp[HOTPLUG_NUM_ENVP];<br>
 };<br>
diff --git a/libmultipath/util.c b/libmultipath/util.c<br>
index 1841f35..85d21fa 100644<br>
--- a/libmultipath/util.c<br>
+++ b/libmultipath/util.c<br>
@@ -262,6 +262,48 @@ dev_t parse_devt(const char *dev_t)<br>
        return makedev(maj, min);<br>
 }<br>
<br>
+char *parse_uid_attribute_by_attrs(<wbr>char *uid_attrs, char *path_dev)<br>
+{<br>
+       char *uid_attribute;<br>
+       char *uid_attr_record;<br>
+       char *dev;<br>
+       char *attr;<br>
+       char *tmp;<br>
+       int  count;<br>
+<br>
+       if(!uid_attrs || !path_dev)<br>
+               return NULL;<br>
+<br>
+       count = get_word(uid_attrs, &uid_attr_record);<br>
+       while (uid_attr_record) {<br>
+               tmp = strrchr(uid_attr_record, ':');<br>
+               if (!tmp) {<br>
+                       free(uid_attr_record);<br>
+                       if (!count)<br>
+                               break;<br>
+                       uid_attrs += count;<br>
+                       count = get_word(uid_attrs, &uid_attr_record);<br>
+                       continue;<br>
+               }<br>
+               dev = uid_attr_record;<br>
+               attr = tmp + 1;<br>
+               *tmp = '\0';<br>
+<br>
+               if(!strncmp(path_dev, dev, strlen(dev))) {<br>
+                       uid_attribute = STRDUP(attr);<br>
+                       free(uid_attr_record);<br>
+                       return uid_attribute;<br>
+               }<br>
+<br>
+               free(uid_attr_record);<br>
+               if (!count)<br>
+                       break;<br>
+               uid_attrs += count;<br>
+               count = get_word(uid_attrs, &uid_attr_record);<br>
+       }<br>
+       return NULL;<br>
+}<br>
+<br>
 void<br>
 setup_thread_attr(pthread_<wbr>attr_t *attr, size_t stacksize, int detached)<br>
 {<br>
diff --git a/libmultipath/util.h b/libmultipath/util.h<br>
index f3b37ee..793f2b7 100644<br>
--- a/libmultipath/util.h<br>
+++ b/libmultipath/util.h<br>
@@ -12,6 +12,7 @@ size_t strlcat(char *dst, const char *src, size_t size);<br>
 int devt2devname (char *, int, char *);<br>
 dev_t parse_devt(const char *dev_t);<br>
 char *convert_dev(char *dev, int is_path_device);<br>
+char *parse_uid_attribute_by_attrs(<wbr>char *uid_attrs, char *path_dev);<br>
 void setup_thread_attr(pthread_<wbr>attr_t *attr, size_t stacksize, int detached);<br>
<br>
 #define safe_sprintf(var, format, args...)     \<br>
diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5<br>
index b08bda3..efeead5 100644<br>
--- a/multipath/multipath.conf.5<br>
+++ b/multipath/multipath.conf.5<br>
@@ -207,6 +207,24 @@ The default is: \fBfailover\fR<br>
 .<br>
 .<br>
 .TP<br>
+.B uid_attrs<br>
+The udev attribute providing a unique path identifier for corresponding<br>
+type of path devices. If this field is configured and matched with type<br>
+of device, it would override any other methods providing for device<br>
+unique identifier in config file, and it would activate merging uevents<br>
+according to the identifier to promote effiecncy in processing uevents.<br>
+Tt has no default value, if you want to identify path by udev attribute<br>
+and to activate merging uevents for SCSI and DAS device, you can set<br>
+it's value as:<br>
+.RS<br>
+.TP<br>
+\fBuid_attrs "sd:ID_SERIAL dasd:ID_UID"\fR<br>
+.TP<br>
+The default is: \fB<unset>\fR<br>
+.RE<br>
+.<br>
+.<br>
+.TP<br>
 .B uid_attribute<br>
 The udev attribute providing a unique path identifier.<br>
 .RS<br>
diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c<br>
index b20b054..9e90328 100644<br>
--- a/multipathd/cli_handlers.c<br>
+++ b/multipathd/cli_handlers.c<br>
@@ -714,7 +714,7 @@ cli_add_path (void * v, char ** reply, int * len, void * data)<br>
                pp->checkint = conf->checkint;<br>
        }<br>
        put_multipath_config(conf);<br>
-       return ev_add_path(pp, vecs);<br>
+       return ev_add_path(pp, vecs, 1);<br>
 blacklisted:<br>
        *reply = strdup("blacklisted\n");<br>
        *len = strlen(*reply) + 1;<br>
@@ -736,7 +736,7 @@ cli_del_path (void * v, char ** reply, int * len, void * data)<br>
                condlog(0, "%s: path already removed", param);<br>
                return 1;<br>
        }<br>
-       return ev_remove_path(pp, vecs);<br>
+       return ev_remove_path(pp, vecs, 1);<br>
 }<br>
<br>
 int<br>
diff --git a/multipathd/main.c b/multipathd/main.c<br>
index e317a4c..0c69f3b 100644<br>
--- a/multipathd/main.c<br>
+++ b/multipathd/main.c<br>
@@ -611,7 +611,7 @@ ev_remove_map (char * devname, char * alias, int minor, struct vectors * vecs)<br>
 }<br>
<br>
 static int<br>
-uev_add_path (struct uevent *uev, struct vectors * vecs)<br>
+uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map)<br>
 {<br>
        struct path *pp;<br>
        int ret = 0, i;<br>
@@ -644,7 +644,7 @@ uev_add_path (struct uevent *uev, struct vectors * vecs)<br>
                                     DI_ALL | DI_BLACKLIST);<br>
                        put_multipath_config(conf);<br>
                        if (r == PATHINFO_OK)<br>
-                               ret = ev_add_path(pp, vecs);<br>
+                               ret = ev_add_path(pp, vecs, need_do_map);<br>
                        else if (r == PATHINFO_SKIPPED) {<br>
                                condlog(3, "%s: remove blacklisted path",<br>
                                        uev->kernel);<br>
@@ -668,7 +668,7 @@ uev_add_path (struct uevent *uev, struct vectors * vecs)<br>
         */<br>
        conf = get_multipath_config();<br>
        ret = alloc_path_with_pathinfo(conf, uev->udev,<br>
-                                      DI_ALL, &pp);<br>
+                                      uev->wwid, DI_ALL, &pp);<br>
        put_multipath_config(conf);<br>
        if (!pp) {<br>
                if (ret == PATHINFO_SKIPPED)<br>
@@ -684,7 +684,7 @@ uev_add_path (struct uevent *uev, struct vectors * vecs)<br>
                conf = get_multipath_config();<br>
                pp->checkint = conf->checkint;<br>
                put_multipath_config(conf);<br>
-               ret = ev_add_path(pp, vecs);<br>
+               ret = ev_add_path(pp, vecs, need_do_map);<br>
        } else {<br>
                condlog(0, "%s: failed to store path info, "<br>
                        "dropping event",<br>
@@ -702,7 +702,7 @@ uev_add_path (struct uevent *uev, struct vectors * vecs)<br>
  * 1: error<br>
  */<br>
 int<br>
-ev_add_path (struct path * pp, struct vectors * vecs)<br>
+ev_add_path (struct path * pp, struct vectors * vecs, int need_do_map)<br>
 {<br>
        struct multipath * mpp;<br>
        char params[PARAMS_SIZE] = {0};<br>
@@ -771,6 +771,13 @@ rescan:<br>
        /* persistent reservation check*/<br>
        mpath_pr_event_handle(pp);<br>
<br>
+       if (!need_do_map)<br>
+               return 0;<br>
+<br>
+       if (!dm_map_present(mpp->alias)) {<br>
+               mpp->action = ACT_CREATE;<br>
+               start_waiter = 1;<br>
+       }<br>
        /*<br>
         * push the map to the device-mapper<br>
         */<br>
@@ -837,7 +844,7 @@ fail:<br>
 }<br>
<br>
 static int<br>
-uev_remove_path (struct uevent *uev, struct vectors * vecs)<br>
+uev_remove_path (struct uevent *uev, struct vectors * vecs, int need_do_map)<br>
 {<br>
        struct path *pp;<br>
        int ret;<br>
@@ -848,7 +855,7 @@ uev_remove_path (struct uevent *uev, struct vectors * vecs)<br>
        pthread_testcancel();<br>
        pp = find_path_by_dev(vecs-><wbr>pathvec, uev->kernel);<br>
        if (pp)<br>
-               ret = ev_remove_path(pp, vecs);<br>
+               ret = ev_remove_path(pp, vecs, need_do_map);<br>
        lock_cleanup_pop(vecs->lock);<br>
        if (!pp) {<br>
                /* Not an error; path might have been purged earlier */<br>
@@ -859,7 +866,7 @@ uev_remove_path (struct uevent *uev, struct vectors * vecs)<br>
 }<br>
<br>
 int<br>
-ev_remove_path (struct path *pp, struct vectors * vecs)<br>
+ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map)<br>
 {<br>
        struct multipath * mpp;<br>
        int i, retval = 0;<br>
@@ -922,6 +929,8 @@ ev_remove_path (struct path *pp, struct vectors * vecs)<br>
                        goto out;<br>
                }<br>
<br>
+               if (!need_do_map)<br>
+                       goto out;<br>
                /*<br>
                 * reload the map<br>
                 */<br>
@@ -999,7 +1008,7 @@ uev_update_path (struct uevent *uev, struct vectors * vecs)<br>
                }<br>
<br>
                if (pp->initialized == INIT_REQUESTED_UDEV)<br>
-                       retval = uev_add_path(uev, vecs);<br>
+                       retval = uev_add_path(uev, vecs, 1);<br>
                else if (mpp && ro >= 0) {<br>
                        condlog(2, "%s: update path write_protect to '%d' (uevent)", uev->kernel, ro);<br>
<br>
@@ -1020,7 +1029,7 @@ out:<br>
                        int flag = DI_SYSFS | DI_WWID;<br>
<br>
                        conf = get_multipath_config();<br>
-                       retval = alloc_path_with_pathinfo(conf, uev->udev, flag, NULL);<br>
+                       retval = alloc_path_with_pathinfo(conf, uev->udev, uev->wwid, flag, NULL);<br>
                        put_multipath_config(conf);<br>
<br>
                        if (retval == PATHINFO_SKIPPED) {<br>
@@ -1090,40 +1099,15 @@ uxsock_trigger (char * str, char ** reply, int * len, bool is_root,<br>
        return r;<br>
 }<br>
<br>
-static int<br>
-uev_discard(char * devpath)<br>
-{<br>
-       char *tmp;<br>
-       char a[11], b[11];<br>
-<br>
-       /*<br>
-        * keep only block devices, discard partitions<br>
-        */<br>
-       tmp = strstr(devpath, "/block/");<br>
-       if (tmp == NULL){<br>
-               condlog(4, "no /block/ in '%s'", devpath);<br>
-               return 1;<br>
-       }<br>
-       if (sscanf(tmp, "/block/%10s", a) != 1 ||<br>
-           sscanf(tmp, "/block/%10[^/]/%10s", a, b) == 2) {<br>
-               condlog(4, "discard event on %s", devpath);<br>
-               return 1;<br>
-       }<br>
-       return 0;<br>
-}<br>
-<br>
 int<br>
 uev_trigger (struct uevent * uev, void * trigger_data)<br>
 {<br>
        int r = 0;<br>
        struct vectors * vecs;<br>
-       struct config *conf;<br>
+       struct uevent *merge_uev, *tmp;<br>
<br>
        vecs = (struct vectors *)trigger_data;<br>
<br>
-       if (uev_discard(uev->devpath))<br>
-               return 0;<br>
-<br>
        pthread_cleanup_push(config_<wbr>cleanup, NULL);<br>
        pthread_mutex_lock(&config_<wbr>lock);<br>
        if (running_state != DAEMON_IDLE &&<br>
@@ -1152,28 +1136,21 @@ uev_trigger (struct uevent * uev, void * trigger_data)<br>
        }<br>
<br>
        /*<br>
-        * path add/remove event<br>
+        * path add/remove/change event, add/remove maybe merged<br>
         */<br>
-       conf = get_multipath_config();<br>
-       if (filter_devnode(conf->blist_<wbr>devnode, conf->elist_devnode,<br>
-                          uev->kernel) > 0) {<br>
-               put_multipath_config(conf);<br>
-               goto out;<br>
+       list_for_each_entry_safe(<wbr>merge_uev, tmp, &uev->merge_node, node) {<br>
+               if (!strncmp(merge_uev->action, "add", 3))<br>
+                       r += uev_add_path(merge_uev, vecs, 0);<br>
+               if (!strncmp(merge_uev->action, "remove", 6))<br>
+                       r += uev_remove_path(merge_uev, vecs, 0);<br>
        }<br>
-       put_multipath_config(conf);<br>
<br>
-       if (!strncmp(uev->action, "add", 3)) {<br>
-               r = uev_add_path(uev, vecs);<br>
-               goto out;<br>
-       }<br>
-       if (!strncmp(uev->action, "remove", 6)) {<br>
-               r = uev_remove_path(uev, vecs);<br>
-               goto out;<br>
-       }<br>
-       if (!strncmp(uev->action, "change", 6)) {<br>
-               r = uev_update_path(uev, vecs);<br>
-               goto out;<br>
-       }<br>
+       if (!strncmp(uev->action, "add", 3))<br>
+               r += uev_add_path(uev, vecs, 1);<br>
+       if (!strncmp(uev->action, "remove", 6))<br>
+               r += uev_remove_path(uev, vecs, 1);<br>
+       if (!strncmp(uev->action, "change", 6))<br>
+               r += uev_update_path(uev, vecs);<br>
<br>
 out:<br>
        return r;<br>
@@ -1663,7 +1640,7 @@ check_path (struct vectors * vecs, struct path * pp, int ticks)<br>
                        conf = get_multipath_config();<br>
                        ret = pathinfo(pp, conf, DI_ALL | DI_BLACKLIST);<br>
                        if (ret == PATHINFO_OK) {<br>
-                               ev_add_path(pp, vecs);<br>
+                               ev_add_path(pp, vecs, 1);<br>
                                pp->tick = 1;<br>
                        } else if (ret == PATHINFO_SKIPPED) {<br>
                                put_multipath_config(conf);<br>
@@ -1785,7 +1762,7 @@ check_path (struct vectors * vecs, struct path * pp, int ticks)<br>
                }<br>
                if (!disable_reinstate && reinstate_path(pp, add_active)) {<br>
                        condlog(3, "%s: reload map", pp->dev);<br>
-                       ev_add_path(pp, vecs);<br>
+                       ev_add_path(pp, vecs, 1);<br>
                        pp->tick = 1;<br>
                        return 0;<br>
                }<br>
@@ -1808,7 +1785,7 @@ check_path (struct vectors * vecs, struct path * pp, int ticks)<br>
                        /* Clear IO errors */<br>
                        if (reinstate_path(pp, 0)) {<br>
                                condlog(3, "%s: reload map", pp->dev);<br>
-                               ev_add_path(pp, vecs);<br>
+                               ev_add_path(pp, vecs, 1);<br>
                                pp->tick = 1;<br>
                                return 0;<br>
                        }<br>
diff --git a/multipathd/main.h b/multipathd/main.h<br>
index f72580d..094b04f 100644<br>
--- a/multipathd/main.h<br>
+++ b/multipathd/main.h<br>
@@ -22,8 +22,8 @@ void exit_daemon(void);<br>
 const char * daemon_status(void);<br>
 int need_to_delay_reconfig (struct vectors *);<br>
 int reconfigure (struct vectors *);<br>
-int ev_add_path (struct path *, struct vectors *);<br>
-int ev_remove_path (struct path *, struct vectors *);<br>
+int ev_add_path (struct path *, struct vectors *, int);<br>
+int ev_remove_path (struct path *, struct vectors *, int);<br>
 int ev_add_map (char *, char *, struct vectors *);<br>
 int ev_remove_map (char *, char *, int, struct vectors *);<br>
 void sync_map_state (struct multipath *);<br>
<span class="HOEnZb"><font color="#888888">--<br>
2.8.1.windows.1<br>
<br>
<br>
</font></span></blockquote></div><br></div>