blob: 86cb55464f71a892c7d765907ea4de5182102001 [file] [log] [blame]
Dave Chinnera38e4082013-08-28 10:17:58 +10001/*
2 * Copyright (c) 2013 Red Hat, Inc. and Parallels Inc. All rights reserved.
3 * Authors: David Chinner and Glauber Costa
4 *
5 * Generic LRU infrastructure
6 */
7#include <linux/kernel.h>
8#include <linux/module.h>
Dave Chinner3b1d58a2013-08-28 10:18:00 +10009#include <linux/mm.h>
Dave Chinnera38e4082013-08-28 10:17:58 +100010#include <linux/list_lru.h>
11
12bool list_lru_add(struct list_lru *lru, struct list_head *item)
13{
Dave Chinner3b1d58a2013-08-28 10:18:00 +100014 int nid = page_to_nid(virt_to_page(item));
15 struct list_lru_node *nlru = &lru->node[nid];
16
17 spin_lock(&nlru->lock);
18 WARN_ON_ONCE(nlru->nr_items < 0);
Dave Chinnera38e4082013-08-28 10:17:58 +100019 if (list_empty(item)) {
Dave Chinner3b1d58a2013-08-28 10:18:00 +100020 list_add_tail(item, &nlru->list);
21 if (nlru->nr_items++ == 0)
22 node_set(nid, lru->active_nodes);
23 spin_unlock(&nlru->lock);
Dave Chinnera38e4082013-08-28 10:17:58 +100024 return true;
25 }
Dave Chinner3b1d58a2013-08-28 10:18:00 +100026 spin_unlock(&nlru->lock);
Dave Chinnera38e4082013-08-28 10:17:58 +100027 return false;
28}
29EXPORT_SYMBOL_GPL(list_lru_add);
30
31bool list_lru_del(struct list_lru *lru, struct list_head *item)
32{
Dave Chinner3b1d58a2013-08-28 10:18:00 +100033 int nid = page_to_nid(virt_to_page(item));
34 struct list_lru_node *nlru = &lru->node[nid];
35
36 spin_lock(&nlru->lock);
Dave Chinnera38e4082013-08-28 10:17:58 +100037 if (!list_empty(item)) {
38 list_del_init(item);
Dave Chinner3b1d58a2013-08-28 10:18:00 +100039 if (--nlru->nr_items == 0)
40 node_clear(nid, lru->active_nodes);
41 WARN_ON_ONCE(nlru->nr_items < 0);
42 spin_unlock(&nlru->lock);
Dave Chinnera38e4082013-08-28 10:17:58 +100043 return true;
44 }
Dave Chinner3b1d58a2013-08-28 10:18:00 +100045 spin_unlock(&nlru->lock);
Dave Chinnera38e4082013-08-28 10:17:58 +100046 return false;
47}
48EXPORT_SYMBOL_GPL(list_lru_del);
49
Glauber Costa6a4f4962013-08-28 10:18:02 +100050unsigned long
51list_lru_count_node(struct list_lru *lru, int nid)
Dave Chinnera38e4082013-08-28 10:17:58 +100052{
Dave Chinner3b1d58a2013-08-28 10:18:00 +100053 unsigned long count = 0;
Glauber Costa6a4f4962013-08-28 10:18:02 +100054 struct list_lru_node *nlru = &lru->node[nid];
Dave Chinner3b1d58a2013-08-28 10:18:00 +100055
Glauber Costa6a4f4962013-08-28 10:18:02 +100056 spin_lock(&nlru->lock);
57 WARN_ON_ONCE(nlru->nr_items < 0);
58 count += nlru->nr_items;
59 spin_unlock(&nlru->lock);
Dave Chinner3b1d58a2013-08-28 10:18:00 +100060
61 return count;
62}
Glauber Costa6a4f4962013-08-28 10:18:02 +100063EXPORT_SYMBOL_GPL(list_lru_count_node);
Dave Chinner3b1d58a2013-08-28 10:18:00 +100064
Glauber Costa6a4f4962013-08-28 10:18:02 +100065unsigned long
Dave Chinner3b1d58a2013-08-28 10:18:00 +100066list_lru_walk_node(struct list_lru *lru, int nid, list_lru_walk_cb isolate,
67 void *cb_arg, unsigned long *nr_to_walk)
68{
69
70 struct list_lru_node *nlru = &lru->node[nid];
Dave Chinnera38e4082013-08-28 10:17:58 +100071 struct list_head *item, *n;
Dave Chinner3b1d58a2013-08-28 10:18:00 +100072 unsigned long isolated = 0;
Dave Chinnera38e4082013-08-28 10:17:58 +100073
Dave Chinner3b1d58a2013-08-28 10:18:00 +100074 spin_lock(&nlru->lock);
Dave Chinnera38e4082013-08-28 10:17:58 +100075restart:
Dave Chinner3b1d58a2013-08-28 10:18:00 +100076 list_for_each_safe(item, n, &nlru->list) {
Dave Chinnera38e4082013-08-28 10:17:58 +100077 enum lru_status ret;
Dave Chinner5cedf7212013-08-28 10:18:01 +100078
79 /*
80 * decrement nr_to_walk first so that we don't livelock if we
81 * get stuck on large numbesr of LRU_RETRY items
82 */
83 if (--(*nr_to_walk) == 0)
84 break;
85
Dave Chinner3b1d58a2013-08-28 10:18:00 +100086 ret = isolate(item, &nlru->lock, cb_arg);
Dave Chinnera38e4082013-08-28 10:17:58 +100087 switch (ret) {
88 case LRU_REMOVED:
Dave Chinner3b1d58a2013-08-28 10:18:00 +100089 if (--nlru->nr_items == 0)
90 node_clear(nid, lru->active_nodes);
91 WARN_ON_ONCE(nlru->nr_items < 0);
92 isolated++;
Dave Chinnera38e4082013-08-28 10:17:58 +100093 break;
94 case LRU_ROTATE:
Dave Chinner3b1d58a2013-08-28 10:18:00 +100095 list_move_tail(item, &nlru->list);
Dave Chinnera38e4082013-08-28 10:17:58 +100096 break;
97 case LRU_SKIP:
98 break;
99 case LRU_RETRY:
Dave Chinner5cedf7212013-08-28 10:18:01 +1000100 /*
101 * The lru lock has been dropped, our list traversal is
102 * now invalid and so we have to restart from scratch.
103 */
Dave Chinnera38e4082013-08-28 10:17:58 +1000104 goto restart;
105 default:
106 BUG();
107 }
Dave Chinnera38e4082013-08-28 10:17:58 +1000108 }
Dave Chinner3b1d58a2013-08-28 10:18:00 +1000109
110 spin_unlock(&nlru->lock);
111 return isolated;
112}
113EXPORT_SYMBOL_GPL(list_lru_walk_node);
114
Dave Chinner3b1d58a2013-08-28 10:18:00 +1000115static unsigned long list_lru_dispose_all_node(struct list_lru *lru, int nid,
116 list_lru_dispose_cb dispose)
117{
118 struct list_lru_node *nlru = &lru->node[nid];
119 LIST_HEAD(dispose_list);
120 unsigned long disposed = 0;
121
122 spin_lock(&nlru->lock);
123 while (!list_empty(&nlru->list)) {
124 list_splice_init(&nlru->list, &dispose_list);
125 disposed += nlru->nr_items;
126 nlru->nr_items = 0;
127 node_clear(nid, lru->active_nodes);
128 spin_unlock(&nlru->lock);
129
130 dispose(&dispose_list);
131
132 spin_lock(&nlru->lock);
133 }
134 spin_unlock(&nlru->lock);
135 return disposed;
136}
137
Dave Chinnera38e4082013-08-28 10:17:58 +1000138unsigned long list_lru_dispose_all(struct list_lru *lru,
139 list_lru_dispose_cb dispose)
140{
Dave Chinner3b1d58a2013-08-28 10:18:00 +1000141 unsigned long disposed;
142 unsigned long total = 0;
143 int nid;
Dave Chinnera38e4082013-08-28 10:17:58 +1000144
Dave Chinner3b1d58a2013-08-28 10:18:00 +1000145 do {
146 disposed = 0;
147 for_each_node_mask(nid, lru->active_nodes) {
148 disposed += list_lru_dispose_all_node(lru, nid,
149 dispose);
150 }
151 total += disposed;
152 } while (disposed != 0);
Dave Chinnera38e4082013-08-28 10:17:58 +1000153
Dave Chinner3b1d58a2013-08-28 10:18:00 +1000154 return total;
Dave Chinnera38e4082013-08-28 10:17:58 +1000155}
156
157int list_lru_init(struct list_lru *lru)
158{
Dave Chinner3b1d58a2013-08-28 10:18:00 +1000159 int i;
Dave Chinnera38e4082013-08-28 10:17:58 +1000160
Dave Chinner3b1d58a2013-08-28 10:18:00 +1000161 nodes_clear(lru->active_nodes);
162 for (i = 0; i < MAX_NUMNODES; i++) {
163 spin_lock_init(&lru->node[i].lock);
164 INIT_LIST_HEAD(&lru->node[i].list);
165 lru->node[i].nr_items = 0;
166 }
Dave Chinnera38e4082013-08-28 10:17:58 +1000167 return 0;
168}
169EXPORT_SYMBOL_GPL(list_lru_init);