@@ -89,6 +89,20 @@ struct uevent * alloc_uevent (void)
return uev;
}
+void
+uevq_cleanup(struct list_head *tmpq)
+{
+ struct uevent *uev, *tmp;
+
+ list_for_each_entry_safe(uev, tmp, tmpq, node) {
+ list_del_init(&uev->node);
+
+ if (uev->udev)
+ udev_device_unref(uev->udev);
+ FREE(uev);
+ }
+}
+
bool
uevent_can_discard(struct uevent *uev)
{
@@ -129,6 +143,62 @@ uevent_can_discard(struct uevent *uev)
return false;
}
+bool
+merge_need_stop(struct uevent *earlier, struct uevent *later)
+{
+ /*
+ * dm uevent do not try to merge with left uevents
+ */
+ if (!strncmp(later->kernel, "dm-", 3))
+ return true;
+
+ /*
+ * we can not make a jugement without merge_id,
+ * so it is sensible to stop merging
+ */
+ if (!earlier->merge_id || !later->merge_id)
+ return true;
+ /*
+ * uevents merging stoped
+ * when we meet an opposite action uevent from the same LUN to AVOID
+ * "add path1 |remove path1 |add path2 |remove path2 |add path3"
+ * to merge as "remove path1, path2" and "add path1, path2, path3"
+ * OR
+ * "remove path1 |add path1 |remove path2 |add path2 |remove path3"
+ * to merge as "add path1, path2" and "remove path1, path2, path3"
+ * SO
+ * when we meet a non-change uevent from the same LUN
+ * with the same merge_id and different action
+ * it would be better to stop merging.
+ */
+ if (!strcmp(earlier->merge_id, later->merge_id) &&
+ strcmp(earlier->action, later->action) &&
+ strcmp(earlier->action, "change") &&
+ strcmp(later->action, "change"))
+ return true;
+
+ return false;
+}
+
+bool
+uevent_can_merge(struct uevent *earlier, struct uevent *later)
+{
+ /* merge paths uevents
+ * whose merge_ids exsit and are same
+ * and actions are same,
+ * and actions are addition or deletion
+ */
+ if (earlier->merge_id && later->merge_id &&
+ !strcmp(earlier->merge_id, later->merge_id) &&
+ !strcmp(earlier->action, later->action) &&
+ strncmp(earlier->action, "change", 6) &&
+ strncmp(earlier->kernel, "dm-", 3)) {
+ return true;
+ }
+
+ return false;
+}
+
void
uevent_discard(struct list_head *tmpq)
{
@@ -145,6 +215,38 @@ uevent_discard(struct list_head *tmpq)
}
void
+uevent_merge(struct uevent *later, struct list_head *tmpq)
+{
+ struct uevent *earlier, *tmp;
+
+ list_for_some_entry_reverse_safe(earlier, tmp, &later->node, tmpq, node) {
+ if (merge_need_stop(earlier, later))
+ break;
+ /*
+ * merge earlier uevents to the later uevent
+ */
+ if (uevent_can_merge(earlier, later)) {
+ condlog(3, "merged uevent: %s-%s-%s with uevent: %s-%s-%s",
+ earlier->action, earlier->kernel, earlier->merge_id,
+ later->action, later->kernel, later->merge_id);
+
+ list_move(&earlier->node, &later->merge_node);
+ }
+ }
+}
+
+void
+merge_uevq(struct list_head *tmpq)
+{
+ struct uevent *later;
+
+ uevent_discard(tmpq);
+ list_for_each_entry_reverse(later, tmpq, node) {
+ uevent_merge(later, tmpq);
+ }
+}
+
+void
service_uevq(struct list_head *tmpq)
{
struct uevent *uev, *tmp;
@@ -155,6 +257,8 @@ service_uevq(struct list_head *tmpq)
if (my_uev_trigger && my_uev_trigger(uev, my_trigger_data))
condlog(0, "uevent trigger error");
+ uevq_cleanup(&uev->merge_node);
+
if (uev->udev)
udev_device_unref(uev->udev);
FREE(uev);
@@ -169,17 +273,6 @@ static void uevent_cleanup(void *arg)
udev_unref(udev);
}
-void
-uevq_cleanup(struct list_head *tmpq)
-{
- struct uevent *uev, *tmp;
-
- list_for_each_entry_safe(uev, tmp, tmpq, node) {
- list_del_init(&uev->node);
- FREE(uev);
- }
-}
-
/*
* Service the uevent queue.
*/
@@ -208,7 +301,7 @@ int uevent_dispatch(int (*uev_trigger)(struct uevent *, void * trigger_data),
pthread_mutex_unlock(uevq_lockp);
if (!my_uev_trigger)
break;
- uevent_discard(&uevq_tmp);
+ merge_uevq(&uevq_tmp);
service_uevq(&uevq_tmp);
}
condlog(3, "Terminating uev service queue");