list_lru.c 4.3 KB
Newer Older
D
Dave Chinner 已提交
1 2 3 4 5 6 7 8
/*
 * Copyright (c) 2013 Red Hat, Inc. and Parallels Inc. All rights reserved.
 * Authors: David Chinner and Glauber Costa
 *
 * Generic LRU infrastructure
 */
#include <linux/kernel.h>
#include <linux/module.h>
9
#include <linux/mm.h>
D
Dave Chinner 已提交
10 11 12 13
#include <linux/list_lru.h>

bool list_lru_add(struct list_lru *lru, struct list_head *item)
{
14 15 16 17 18
	int nid = page_to_nid(virt_to_page(item));
	struct list_lru_node *nlru = &lru->node[nid];

	spin_lock(&nlru->lock);
	WARN_ON_ONCE(nlru->nr_items < 0);
D
Dave Chinner 已提交
19
	if (list_empty(item)) {
20 21 22 23
		list_add_tail(item, &nlru->list);
		if (nlru->nr_items++ == 0)
			node_set(nid, lru->active_nodes);
		spin_unlock(&nlru->lock);
D
Dave Chinner 已提交
24 25
		return true;
	}
26
	spin_unlock(&nlru->lock);
D
Dave Chinner 已提交
27 28 29 30 31 32
	return false;
}
EXPORT_SYMBOL_GPL(list_lru_add);

bool list_lru_del(struct list_lru *lru, struct list_head *item)
{
33 34 35 36
	int nid = page_to_nid(virt_to_page(item));
	struct list_lru_node *nlru = &lru->node[nid];

	spin_lock(&nlru->lock);
D
Dave Chinner 已提交
37 38
	if (!list_empty(item)) {
		list_del_init(item);
39 40 41 42
		if (--nlru->nr_items == 0)
			node_clear(nid, lru->active_nodes);
		WARN_ON_ONCE(nlru->nr_items < 0);
		spin_unlock(&nlru->lock);
D
Dave Chinner 已提交
43 44
		return true;
	}
45
	spin_unlock(&nlru->lock);
D
Dave Chinner 已提交
46 47 48 49
	return false;
}
EXPORT_SYMBOL_GPL(list_lru_del);

50
unsigned long list_lru_count(struct list_lru *lru)
D
Dave Chinner 已提交
51
{
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
	unsigned long count = 0;
	int nid;

	for_each_node_mask(nid, lru->active_nodes) {
		struct list_lru_node *nlru = &lru->node[nid];

		spin_lock(&nlru->lock);
		WARN_ON_ONCE(nlru->nr_items < 0);
		count += nlru->nr_items;
		spin_unlock(&nlru->lock);
	}

	return count;
}
EXPORT_SYMBOL_GPL(list_lru_count);

static unsigned long
list_lru_walk_node(struct list_lru *lru, int nid, list_lru_walk_cb isolate,
		   void *cb_arg, unsigned long *nr_to_walk)
{

	struct list_lru_node	*nlru = &lru->node[nid];
D
Dave Chinner 已提交
74
	struct list_head *item, *n;
75
	unsigned long isolated = 0;
D
Dave Chinner 已提交
76 77 78 79 80 81 82 83 84
	/*
	 * If we don't keep state of at which pass we are, we can loop at
	 * LRU_RETRY, since we have no guarantees that the caller will be able
	 * to do something other than retry on the next pass. We handle this by
	 * allowing at most one retry per object. This should not be altered
	 * by any condition other than LRU_RETRY.
	 */
	bool first_pass = true;

85
	spin_lock(&nlru->lock);
D
Dave Chinner 已提交
86
restart:
87
	list_for_each_safe(item, n, &nlru->list) {
D
Dave Chinner 已提交
88
		enum lru_status ret;
89
		ret = isolate(item, &nlru->lock, cb_arg);
D
Dave Chinner 已提交
90 91
		switch (ret) {
		case LRU_REMOVED:
92 93 94 95
			if (--nlru->nr_items == 0)
				node_clear(nid, lru->active_nodes);
			WARN_ON_ONCE(nlru->nr_items < 0);
			isolated++;
D
Dave Chinner 已提交
96 97
			break;
		case LRU_ROTATE:
98
			list_move_tail(item, &nlru->list);
D
Dave Chinner 已提交
99 100 101 102 103 104 105 106 107 108 109 110 111 112
			break;
		case LRU_SKIP:
			break;
		case LRU_RETRY:
			if (!first_pass) {
				first_pass = true;
				break;
			}
			first_pass = false;
			goto restart;
		default:
			BUG();
		}

113
		if ((*nr_to_walk)-- == 0)
D
Dave Chinner 已提交
114 115 116
			break;

	}
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135

	spin_unlock(&nlru->lock);
	return isolated;
}
EXPORT_SYMBOL_GPL(list_lru_walk_node);

unsigned long list_lru_walk(struct list_lru *lru, list_lru_walk_cb isolate,
			    void *cb_arg, unsigned long nr_to_walk)
{
	unsigned long isolated = 0;
	int nid;

	for_each_node_mask(nid, lru->active_nodes) {
		isolated += list_lru_walk_node(lru, nid, isolate,
					       cb_arg, &nr_to_walk);
		if (nr_to_walk <= 0)
			break;
	}
	return isolated;
D
Dave Chinner 已提交
136 137 138
}
EXPORT_SYMBOL_GPL(list_lru_walk);

139 140
static unsigned long list_lru_dispose_all_node(struct list_lru *lru, int nid,
					       list_lru_dispose_cb dispose)
D
Dave Chinner 已提交
141
{
142
	struct list_lru_node	*nlru = &lru->node[nid];
D
Dave Chinner 已提交
143
	LIST_HEAD(dispose_list);
144
	unsigned long disposed = 0;
D
Dave Chinner 已提交
145

146 147 148 149 150 151 152
	spin_lock(&nlru->lock);
	while (!list_empty(&nlru->list)) {
		list_splice_init(&nlru->list, &dispose_list);
		disposed += nlru->nr_items;
		nlru->nr_items = 0;
		node_clear(nid, lru->active_nodes);
		spin_unlock(&nlru->lock);
D
Dave Chinner 已提交
153 154 155

		dispose(&dispose_list);

156
		spin_lock(&nlru->lock);
D
Dave Chinner 已提交
157
	}
158
	spin_unlock(&nlru->lock);
D
Dave Chinner 已提交
159 160 161
	return disposed;
}

162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
unsigned long list_lru_dispose_all(struct list_lru *lru,
				   list_lru_dispose_cb dispose)
{
	unsigned long disposed;
	unsigned long total = 0;
	int nid;

	do {
		disposed = 0;
		for_each_node_mask(nid, lru->active_nodes) {
			disposed += list_lru_dispose_all_node(lru, nid,
							      dispose);
		}
		total += disposed;
	} while (disposed != 0);

	return total;
}

D
Dave Chinner 已提交
181 182
int list_lru_init(struct list_lru *lru)
{
183
	int i;
D
Dave Chinner 已提交
184

185 186 187 188 189 190
	nodes_clear(lru->active_nodes);
	for (i = 0; i < MAX_NUMNODES; i++) {
		spin_lock_init(&lru->node[i].lock);
		INIT_LIST_HEAD(&lru->node[i].list);
		lru->node[i].nr_items = 0;
	}
D
Dave Chinner 已提交
191 192 193
	return 0;
}
EXPORT_SYMBOL_GPL(list_lru_init);