<div class="zcontentRow"> <p>Hello Ben,</p><p><br></p><p>Good catch, you are so nice and responsible person.</p><p>I have modified those bugs and resended the patch just now,</p><p>Please have a review for it.</p><p><br></p><p>Thanks,</p><p>Tang Junhui</p><p><br></p><p><br></p><p><br></p><p><br></p><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"> <bmarzins@redhat.com>;</span></div><div><strong language-data="HistoryTOTxt">收件人:</strong><span class="zreadUserName" style="display: inline-block;">唐军辉10074136;</span></div><div><strong language-data="HistoryCCTxt">抄送人:</strong><span class="zreadUserName" style="display: inline-block;">唐文俊10144149;</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月16日 05:15</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>On Wed, Jan 18, 2017 at 03:38:33PM +0800, tang.junhui@zte.com.cn wrote:<br>> From: "tang.junhui" <tang.junhui@zte.com.cn><br>> <br>> Change-Id: I3f81a55fff389f991f915927000b281d7e263cc5<br>> Signed-off-by: tang.junhui <tang.junhui@zte.com.cn><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>> Any comment will be welcome, and it would be appreciated if these<br>> patches would be considered for inclusion in the upstream<br>> multipath-tools.<br><br>Sorry for letting this slip through the cracks. This patch is really<br>close. I just have a few issues. First off, this is completely<br>superficial, but select_getuid() should use the new style of origin<br>messages. instead of<br><br>origin = "(config file default)";<br><br>it should probably be<br><br>origin = "(setting: multipath.conf defaults/devices section)"<br><br>to match the other messages.<br><br><br>Second, there is a bug in parse_uid_attribute_by_attrs(). get_word()<br>returns the number of characters that the returned word takes up in the<br>sentence, but you keep using it as the offset from the start of<br>uid_attrs.  This doesn't effect the first call to get_word(), since it<br>doesn't use count. The second time you call get_word(), everything still<br>works because the number of characters that the first word took up is<br>also the offset from the start of uid_attrs.  However, on a<br>(hypothetical) third call, count is just the size of the second word,<br>but you use are using it as the offset from the start of uid_attrs (the<br>size of the first and second word combined). This means that the a third<br>call won't start at the correct place, and can get stuck in an endless<br>loop. Obviously, as long as uid_attrs only has two words in the value,<br>this isn't a problem.  But if we ever add another uid attribute to the<br>list, it will break.<br><br><br>Finally, in uevent_get_wwid, the goal is to see an environment variable<br>like<br><br>ID_SERIAL=3600d0230000000000e13955cc3757800<br><br>and correctly grab "3600d0230000000000e13955cc3757800" as the wwid.<br>Unfortunately in the list of environment variables for scsi devices,<br>there is also<br><br>ID_SERIAL_SHORT=600d0230000000000e13955cc3757800<br><br>If multipath happened to see this environment variable first, it would<br>grab "SHORT=600d0230000000000e13955cc3757800" as the wwid. It needs to<br>check that the character after strlen(uid_attribute) is an equals sign.<br>Now, I realize that code all over multipath-tools doesn't bother<br>checking for an equals sign, but in this specific case there is a known<br>evironment variable that could cause problems if we don't check (not<br>that those other cases don't need fixing eventually).<br><br>otherwise, it looks great.<br><br>Thanks<br>-Ben<br><br>> Thank you all,<br>> Tang Junhui<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      | 319 +++++++++++++++++++++++++++++++++++++++++++--<br>>  libmultipath/uevent.h      |   2 +<br>>  libmultipath/util.c        |  40 ++++++<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, 468 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_attrs, set_str)<br>> +declare_def_snprint(uid_attrs, print_str)<br>>  declare_def_handler(uid_attribute, set_str)<br>>  declare_def_snprint_defstr(uid_attribute, print_str, DEFAULT_UID_ATTRIBUTE)<br>>  declare_ovr_handler(uid_attribute, set_str)<br>> @@ -1367,6 +1369,7 @@ init_keywords(vector keywords)<br>>      install_keyword("multipath_dir", &def_multipath_dir_handler, &snprint_def_multipath_dir);<br>>      install_keyword("path_selector", &def_selector_handler, &snprint_def_selector);<br>>      install_keyword("path_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_attribute", &def_uid_attribute_handler, &snprint_def_uid_attribute);<br>>      install_keyword("getuid_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_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_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_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_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..5a58dc0 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(conf->uid_attrs, pp->dev);<br>> +    if (pp->uid_attribute) {<br>> +        origin = "(config file default)";<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..7e4ac04 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,300 @@ 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_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>> +    conf = get_multipath_config();<br>> +    uid_attribute = parse_uid_attribute_by_attrs(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->wwid = uev->envp[i] + strlen(uid_attribute) + 1;<br>> +            break;<br>> +        }<br>> +    }<br>> +    free(uid_attribute);<br>> +}<br>>  <br>> -    condlog(3, "Releasing uevent_listen() resources");<br>> -    udev_unref(udev);<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_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_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_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->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_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(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);<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 +404,7 @@ int uevent_dispatch(int (*uev_trigger)(struct uevent *, void * trigger_data),<br>>          pthread_mutex_unlock(uevq_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 +705,43 @@ struct uevent *uevent_from_udev_device(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 +795,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 +810,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(monitor);<br>>              if (!dev) {<br>>                  condlog(0, "failed getting udev device");<br>> @@ -547,6 +843,7 @@ int uevent_listen(struct udev *udev)<br>>              pthread_mutex_unlock(uevq_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..e5a4a28 100644<br>> --- a/libmultipath/util.c<br>> +++ b/libmultipath/util.c<br>> @@ -261,6 +261,46 @@ dev_t parse_devt(const char *dev_t)<br>>      return makedev(maj, min);<br>>  }<br>>  <br>> +char *parse_uid_attribute_by_attrs(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>> +            count = get_word(uid_attrs + count, &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>> +        count = get_word(uid_attrs + count, &uid_attr_record);<br>> +    }<br>> +    return NULL;<br>> +}<br>> +<br>>  void<br>>  setup_thread_attr(pthread_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(char *uid_attrs, char *path_dev);<br>>  void setup_thread_attr(pthread_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->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_cleanup, NULL);<br>>      pthread_mutex_lock(&config_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_devnode, conf->elist_devnode,<br>> -               uev->kernel) > 0) {<br>> -        put_multipath_config(conf);<br>> -        goto out;<br>> +    list_for_each_entry_safe(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><br>--<br>dm-devel mailing list<br>dm-devel@redhat.com<br>https://www.redhat.com/mailman/listinfo/dm-devel<br></div><p><br></p></div></div></div></div><p><br></p> </div>