]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
libxfs: add more list operations
authorDarrick J. Wong <darrick.wong@oracle.com>
Wed, 10 Aug 2016 01:29:35 +0000 (11:29 +1000)
committerDave Chinner <david@fromorbit.com>
Wed, 10 Aug 2016 01:29:35 +0000 (11:29 +1000)
Add some list operations that the deferred rmap code requires.

Code comes from the following kernel files:
lib/list_sort.c for all the list_sort stuff,
include/linux/list.h for the rest of the list_* stuff,
include/linux/kernel.h for container_of.

[ dchinner: move list_sort code to libxfs/list_sort.c ]

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
include/list.h
libxfs/Makefile
libxfs/list_sort.c [new file with mode: 0644]
libxfs/rdwr.c
libxfs/util.c

index f92faed893dcea25bc1a4e5c7f12a3617dabfe45..372cf06230f2df1774a25f486a9231ef0b4c81d1 100644 (file)
@@ -111,30 +111,30 @@ static inline int list_empty(const struct list_head *head)
 }
 
 static inline void __list_splice(struct list_head *list,
-                                struct list_head *head)
+                                struct list_head *prev,
+                                struct list_head *next)
 {
        struct list_head *first = list->next;
        struct list_head *last = list->prev;
-       struct list_head *at = head->next;
 
-       first->prev = head;
-       head->next = first;
+       first->prev = prev;
+       prev->next = first;
 
-       last->next = at;
-       at->prev = last;
+       last->next = next;
+       next->prev = last;
 }
 
 static inline void list_splice(struct list_head *list, struct list_head *head)
 {
        if (!list_empty(list))
-               __list_splice(list, head);
+               __list_splice(list, head, head->next);
 }
 
 static inline void list_splice_init(struct list_head *list,
                                    struct list_head *head)
 {
        if (!list_empty(list)) {
-               __list_splice(list, head);
+               __list_splice(list, head, head->next);
                list_head_init(list);
        }
 }
@@ -161,4 +161,64 @@ static inline void list_splice_init(struct list_head *list,
             &pos->member != (head);                                    \
             pos = n, n = list_entry(n->member.next, typeof(*n), member))
 
+#define list_first_entry(ptr, type, member) \
+       list_entry((ptr)->next, type, member)
+
+#define container_of(ptr, type, member) ({                     \
+       const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
+       (type *)( (char *)__mptr - offsetof(type,member) );})
+
+void list_sort(void *priv, struct list_head *head,
+              int (*cmp)(void *priv, struct list_head *a,
+                         struct list_head *b));
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+/**
+ * list_splice_tail_init - join two lists and reinitialise the emptied list
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * Each of the lists is a queue.
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_tail_init(struct list_head *list,
+                                        struct list_head *head)
+{
+       if (!list_empty(list)) {
+               __list_splice(list, head->prev, head);
+               INIT_LIST_HEAD(list);
+       }
+}
+
+/**
+ * list_last_entry - get the last element from a list
+ * @ptr:       the list head to take the element from.
+ * @type:      the type of the struct this is embedded in.
+ * @member:    the name of the list_head within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_last_entry(ptr, type, member) \
+       list_entry((ptr)->prev, type, member)
+
+/**
+ * list_prev_entry - get the prev element in list
+ * @pos:       the type * to cursor
+ * @member:    the name of the list_head within the struct.
+ */
+#define list_prev_entry(pos, member) \
+       list_entry((pos)->member.prev, typeof(*(pos)), member)
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos:       the type * to use as a loop cursor.
+ * @head:      the head for your list.
+ * @member:    the name of the list_head within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member)                 \
+       for (pos = list_last_entry(head, typeof(*pos), member);         \
+            &pos->member != (head);                                    \
+            pos = list_prev_entry(pos, member))
+
 #endif /* __LIST_H__ */
index ca1a5ee181bbdde76959e557d255f77cb1abc8ec..a2178c44d715329fadf63b5c79f80f7ae85090a1 100644 (file)
@@ -51,6 +51,7 @@ CFILES = cache.c \
        crc32.c \
        init.c \
        kmem.c \
+       list_sort.c \
        logitem.c \
        radix-tree.c \
        rdwr.c \
diff --git a/libxfs/list_sort.c b/libxfs/list_sort.c
new file mode 100644 (file)
index 0000000..16258d9
--- /dev/null
@@ -0,0 +1,141 @@
+/* List sorting code from Linux::lib/list_sort.c. */
+
+#include "libxfs_priv.h"
+#include "libxfs_io.h"
+#include "init.h"
+#include "list.h"
+
+#define MAX_LIST_LENGTH_BITS 20
+
+/*
+ * Returns a list organized in an intermediate format suited
+ * to chaining of merge() calls: null-terminated, no reserved or
+ * sentinel head node, "prev" links not maintained.
+ */
+static struct list_head *merge(void *priv,
+                               int (*cmp)(void *priv, struct list_head *a,
+                                       struct list_head *b),
+                               struct list_head *a, struct list_head *b)
+{
+       struct list_head head, *tail = &head;
+
+       while (a && b) {
+               /* if equal, take 'a' -- important for sort stability */
+               if ((*cmp)(priv, a, b) <= 0) {
+                       tail->next = a;
+                       a = a->next;
+               } else {
+                       tail->next = b;
+                       b = b->next;
+               }
+               tail = tail->next;
+       }
+       tail->next = a?:b;
+       return head.next;
+}
+
+/*
+ * Combine final list merge with restoration of standard doubly-linked
+ * list structure.  This approach duplicates code from merge(), but
+ * runs faster than the tidier alternatives of either a separate final
+ * prev-link restoration pass, or maintaining the prev links
+ * throughout.
+ */
+static void merge_and_restore_back_links(void *priv,
+                               int (*cmp)(void *priv, struct list_head *a,
+                                       struct list_head *b),
+                               struct list_head *head,
+                               struct list_head *a, struct list_head *b)
+{
+       struct list_head *tail = head;
+       unsigned count = 0;
+
+       while (a && b) {
+               /* if equal, take 'a' -- important for sort stability */
+               if ((*cmp)(priv, a, b) <= 0) {
+                       tail->next = a;
+                       a->prev = tail;
+                       a = a->next;
+               } else {
+                       tail->next = b;
+                       b->prev = tail;
+                       b = b->next;
+               }
+               tail = tail->next;
+       }
+       tail->next = a ? : b;
+
+       do {
+               /*
+                * In worst cases this loop may run many iterations.
+                * Continue callbacks to the client even though no
+                * element comparison is needed, so the client's cmp()
+                * routine can invoke cond_resched() periodically.
+                */
+               if (unlikely(!(++count)))
+                       (*cmp)(priv, tail->next, tail->next);
+
+               tail->next->prev = tail;
+               tail = tail->next;
+       } while (tail->next);
+
+       tail->next = head;
+       head->prev = tail;
+}
+
+/**
+ * list_sort - sort a list
+ * @priv: private data, opaque to list_sort(), passed to @cmp
+ * @head: the list to sort
+ * @cmp: the elements comparison function
+ *
+ * This function implements "merge sort", which has O(nlog(n))
+ * complexity.
+ *
+ * The comparison function @cmp must return a negative value if @a
+ * should sort before @b, and a positive value if @a should sort after
+ * @b. If @a and @b are equivalent, and their original relative
+ * ordering is to be preserved, @cmp must return 0.
+ */
+void list_sort(void *priv, struct list_head *head,
+               int (*cmp)(void *priv, struct list_head *a,
+                       struct list_head *b))
+{
+       struct list_head *part[MAX_LIST_LENGTH_BITS+1]; /* sorted partial lists
+                                               -- last slot is a sentinel */
+       int lev;  /* index into part[] */
+       int max_lev = 0;
+       struct list_head *list;
+
+       if (list_empty(head))
+               return;
+
+       memset(part, 0, sizeof(part));
+
+       head->prev->next = NULL;
+       list = head->next;
+
+       while (list) {
+               struct list_head *cur = list;
+               list = list->next;
+               cur->next = NULL;
+
+               for (lev = 0; part[lev]; lev++) {
+                       cur = merge(priv, cmp, part[lev], cur);
+                       part[lev] = NULL;
+               }
+               if (lev > max_lev) {
+                       if (unlikely(lev >= ARRAY_SIZE(part)-1)) {
+                               lev--;
+                       }
+                       max_lev = lev;
+               }
+               part[lev] = cur;
+       }
+
+       for (lev = 0; lev < max_lev; lev++)
+               if (part[lev])
+                       list = merge(priv, cmp, part[lev], list);
+
+       merge_and_restore_back_links(priv, cmp, head, part[max_lev], list);
+}
index 0ec38c58f06070261ab25baa7669a7af64e08c4d..aa30522e9641a09d4c628a0c11e432b1b27c77a1 100644 (file)
@@ -1260,7 +1260,7 @@ libxfs_bulkrelse(
        }
 
        pthread_mutex_lock(&xfs_buf_freelist.cm_mutex);
-       __list_splice(list, &xfs_buf_freelist.cm_list);
+       list_splice(list, &xfs_buf_freelist.cm_list);
        pthread_mutex_unlock(&xfs_buf_freelist.cm_mutex);
 
        return count;
index 90eaf41c8ea388a61bef7cd56509a4fe44d58aef..2a38c89fb0e26228b54c41ed100e1df0bf99c779 100644 (file)
@@ -777,3 +777,4 @@ libxfs_zero_extent(
 
        return libxfs_device_zero(xfs_find_bdev_for_inode(ip), sector, size);
 }
+