<div class="zcontentRow"> <p><br></p><p>Hi Christophe,</p><p><br></p><p>OK, I'll post it later.</p><p><br></p><p>regards,</p><p>Tang Junhui</p><p><br></p><p><br></p><div class="zMailSign"></div><div class="zMailFrom"></div><div><div class="zhistoryRow" style="display:block"><div class="zhistoryDes" style="width: 100%; height: 28px; line-height: 28px; background-color: #E0E5E9; color: #1388FF; text-align: center;" language-data="HistoryOrgTxt">原始邮件</div><div id="zwriteHistoryContainer"><div class="control-group zhistoryPanel"><div class="zhistoryHeader" style="padding: 8px; background-color: #F5F6F8;"><div><strong language-data="HistorySenderTxt">发件人:</strong><span class="zreadUserName"> <christophe.varoqui@opensvc.com>;</span></div><div><strong language-data="HistoryTOTxt">收件人:</strong><span class="zreadUserName" style="display: inline-block;"> <bmarzins@redhat.com>;</span></div><div><strong language-data="HistoryCCTxt">抄送人:</strong><span class="zreadUserName" style="display: inline-block;">唐文俊10144149;</span><span class="zreadUserName" style="display: inline-block;">唐军辉10074136;</span><span class="zreadUserName" style="display: inline-block;">张凯10072500;</span><span class="zreadUserName" style="display: inline-block;"> <dm-devel@redhat.com>;</span><span class="zreadUserName" style="display: inline-block;"> <bart.vanassche@sandisk.com>;</span><span class="zreadUserName" style="display: inline-block;"> <mwilck@suse.com>;</span></div><div><strong language-data="HistoryDateTxt">日 期 :</strong><span class="">2017年02月27日 14:22</span></div><div><strong language-data="HistorySubjectTxt">主 题 :</strong><span class="zreadTitle"><strong>Re: [dm-devel] [PATCH] multipath-tools: improve processing efficiency for addition and deletion of multipath devices</strong></span></div></div><p class="zhistoryContent"><br></p><div><div dir="ltr"><div>Hi Junhui,</div><div><br></div>It seems I did not receive the patch version Acked by Ben,<div>Can you rebase and post please ?</div><div><br></div><div>Thanks.</div></div><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Feb 16, 2017 at 5:27 PM, Benjamin Marzinski <span dir="ltr"><<a href="mailto:bmarzins@redhat.com" target="_blank">bmarzins@redhat.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">On Thu, Feb 16, 2017 at 02:54:01PM +0800, <a href="mailto:tang.junhui@zte.com.cn" target="_blank">tang.junhui@zte.com.cn</a> wrote:<br> > From: "tang.junhui" <<a href="mailto:tang.junhui@zte.com.cn" target="_blank">tang.junhui@zte.com.cn</a>><br> <br> </span>ACK<br> <br> Thanks for all your work on this.<br> -Ben<br> <div class="HOEnZb"><div class="h5"><br> ><br> > Change-Id: I3f81a55fff389f991f915927000b2<wbr>81d7e263cc5<br> > Signed-off-by: tang.junhui <<a href="mailto:tang.junhui@zte.com.cn" target="_blank">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 providing<br> >    for device unique identifier in config file, and it would activate merging<br> >    uevents according to the identifier to promote effiecncy in processing<br> >    uevents. Tt has no default value, so defaultly only uevents filtering<br> >    works, and uevents merging does not works, if users want to identify path<br> >    by udev attribute and to activate merging uevents for SCSI and DAS device,<br> >    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> >  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 15ddbd8..765e91d 100644<br> > --- a/libmultipath/config.c<br> > +++ b/libmultipath/config.c<br> > @@ -488,6 +488,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 9670020..ab85930 100644<br> > --- a/libmultipath/config.h<br> > +++ b/libmultipath/config.h<br> > @@ -153,6 +153,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 dc21846..0a531d1 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> > @@ -1367,6 +1369,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 d1aec31..14904f2 100644<br> > --- a/libmultipath/discovery.c<br> > +++ b/libmultipath/discovery.c<br> > @@ -33,7 +33,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> > @@ -51,6 +51,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 c0bc616..c47bd08 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> > @@ -339,6 +340,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 03a5738..e1e83cf 100644<br> > --- a/libmultipath/util.c<br> > +++ b/libmultipath/util.c<br> > @@ -261,6 +261,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 36589f5..63c63c2 100644<br> > --- a/multipath/multipath.conf.5<br> > +++ b/multipath/multipath.conf.5<br> > @@ -209,6 +209,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 b0eeca6..12f85de 100644<br> > --- a/multipathd/cli_handlers.c<br> > +++ b/multipathd/cli_handlers.c<br> > @@ -670,7 +670,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> > @@ -692,7 +692,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 adc3258..e513f7d 100644<br> > --- a/multipathd/main.c<br> > +++ b/multipathd/main.c<br> > @@ -608,7 +608,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> > @@ -641,7 +641,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> > @@ -665,7 +665,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> > @@ -681,7 +681,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> > @@ -699,7 +699,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> > @@ -767,6 +767,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> > @@ -833,7 +840,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> > @@ -844,7 +851,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> > @@ -855,7 +862,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> > @@ -918,6 +925,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> > @@ -995,7 +1004,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> > @@ -1016,7 +1025,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> > @@ -1077,40 +1086,15 @@ uxsock_trigger (char * str, char ** reply, int * len, void * trigger_data)<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> > @@ -1139,28 +1123,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> > @@ -1570,7 +1547,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> > @@ -1686,7 +1663,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> > @@ -1709,7 +1686,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> > --<br> > 2.8.1.windows.1<br> ><br> </div></div></blockquote></div><br></div></div><p><br></p></div></div></div></div><p><br></p> </div>