aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Streetman <ddstreet@ieee.org>2014-06-04 19:09:57 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-06-04 19:54:07 -0400
commita75f232ce0fe38bd01301899ecd97ffd0254316a (patch)
treef2cdf5268b5c52ea8584097a3eb571560f40fe47
parentfd16618e12a05df79a3439d72d5ffdac5d34f3da (diff)
lib/plist: add plist_requeue
Add plist_requeue(), which moves the specified plist_node after all other same-priority plist_nodes in the list. This is essentially an optimized plist_del() followed by plist_add(). This is needed by swap, which (with the next patch in this set) uses a plist of available swap devices. When a swap device (either a swap partition or swap file) are added to the system with swapon(), the device is added to a plist, ordered by the swap device's priority. When swap needs to allocate a page from one of the swap devices, it takes the page from the first swap device on the plist, which is the highest priority swap device. The swap device is left in the plist until all its pages are used, and then removed from the plist when it becomes full. However, as described in man 2 swapon, swap must allocate pages from swap devices with the same priority in round-robin order; to do this, on each swap page allocation, swap uses a page from the first swap device in the plist, and then calls plist_requeue() to move that swap device entry to after any other same-priority swap devices. The next swap page allocation will again use a page from the first swap device in the plist and requeue it, and so on, resulting in round-robin usage of equal-priority swap devices. Also add plist_test_requeue() test function, for use by plist_test() to test plist_requeue() function. Signed-off-by: Dan Streetman <ddstreet@ieee.org> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: Peter Zijlstra <peterz@infradead.org> Acked-by: Mel Gorman <mgorman@suse.de> Cc: Paul Gortmaker <paul.gortmaker@windriver.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Shaohua Li <shli@fusionio.com> Cc: Hugh Dickins <hughd@google.com> Cc: Dan Streetman <ddstreet@ieee.org> Cc: Michal Hocko <mhocko@suse.cz> Cc: Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com> Cc: Weijie Yang <weijieut@gmail.com> Cc: Rik van Riel <riel@redhat.com> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Bob Liu <bob.liu@oracle.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--include/linux/plist.h2
-rw-r--r--lib/plist.c52
2 files changed, 54 insertions, 0 deletions
diff --git a/include/linux/plist.h b/include/linux/plist.h
index c81549119bd4..8b6c970cff6c 100644
--- a/include/linux/plist.h
+++ b/include/linux/plist.h
@@ -141,6 +141,8 @@ static inline void plist_node_init(struct plist_node *node, int prio)
141extern void plist_add(struct plist_node *node, struct plist_head *head); 141extern void plist_add(struct plist_node *node, struct plist_head *head);
142extern void plist_del(struct plist_node *node, struct plist_head *head); 142extern void plist_del(struct plist_node *node, struct plist_head *head);
143 143
144extern void plist_requeue(struct plist_node *node, struct plist_head *head);
145
144/** 146/**
145 * plist_for_each - iterate over the plist 147 * plist_for_each - iterate over the plist
146 * @pos: the type * to use as a loop counter 148 * @pos: the type * to use as a loop counter
diff --git a/lib/plist.c b/lib/plist.c
index 1ebc95f7a46f..0f2084d30798 100644
--- a/lib/plist.c
+++ b/lib/plist.c
@@ -134,6 +134,46 @@ void plist_del(struct plist_node *node, struct plist_head *head)
134 plist_check_head(head); 134 plist_check_head(head);
135} 135}
136 136
137/**
138 * plist_requeue - Requeue @node at end of same-prio entries.
139 *
140 * This is essentially an optimized plist_del() followed by
141 * plist_add(). It moves an entry already in the plist to
142 * after any other same-priority entries.
143 *
144 * @node: &struct plist_node pointer - entry to be moved
145 * @head: &struct plist_head pointer - list head
146 */
147void plist_requeue(struct plist_node *node, struct plist_head *head)
148{
149 struct plist_node *iter;
150 struct list_head *node_next = &head->node_list;
151
152 plist_check_head(head);
153 BUG_ON(plist_head_empty(head));
154 BUG_ON(plist_node_empty(node));
155
156 if (node == plist_last(head))
157 return;
158
159 iter = plist_next(node);
160
161 if (node->prio != iter->prio)
162 return;
163
164 plist_del(node, head);
165
166 plist_for_each_continue(iter, head) {
167 if (node->prio != iter->prio) {
168 node_next = &iter->node_list;
169 break;
170 }
171 }
172 list_add_tail(&node->node_list, node_next);
173
174 plist_check_head(head);
175}
176
137#ifdef CONFIG_DEBUG_PI_LIST 177#ifdef CONFIG_DEBUG_PI_LIST
138#include <linux/sched.h> 178#include <linux/sched.h>
139#include <linux/module.h> 179#include <linux/module.h>
@@ -170,6 +210,14 @@ static void __init plist_test_check(int nr_expect)
170 BUG_ON(prio_pos->prio_list.next != &first->prio_list); 210 BUG_ON(prio_pos->prio_list.next != &first->prio_list);
171} 211}
172 212
213static void __init plist_test_requeue(struct plist_node *node)
214{
215 plist_requeue(node, &test_head);
216
217 if (node != plist_last(&test_head))
218 BUG_ON(node->prio == plist_next(node)->prio);
219}
220
173static int __init plist_test(void) 221static int __init plist_test(void)
174{ 222{
175 int nr_expect = 0, i, loop; 223 int nr_expect = 0, i, loop;
@@ -193,6 +241,10 @@ static int __init plist_test(void)
193 nr_expect--; 241 nr_expect--;
194 } 242 }
195 plist_test_check(nr_expect); 243 plist_test_check(nr_expect);
244 if (!plist_node_empty(test_node + i)) {
245 plist_test_requeue(test_node + i);
246 plist_test_check(nr_expect);
247 }
196 } 248 }
197 249
198 for (i = 0; i < ARRAY_SIZE(test_node); i++) { 250 for (i = 0; i < ARRAY_SIZE(test_node); i++) {