list_lru.c revision a38e40824844a5ec85f3ea95632be953477d2afa
1/*
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>
9#include <linux/list_lru.h>
10
11bool list_lru_add(struct list_lru *lru, struct list_head *item)
12{
13	spin_lock(&lru->lock);
14	if (list_empty(item)) {
15		list_add_tail(item, &lru->list);
16		lru->nr_items++;
17		spin_unlock(&lru->lock);
18		return true;
19	}
20	spin_unlock(&lru->lock);
21	return false;
22}
23EXPORT_SYMBOL_GPL(list_lru_add);
24
25bool list_lru_del(struct list_lru *lru, struct list_head *item)
26{
27	spin_lock(&lru->lock);
28	if (!list_empty(item)) {
29		list_del_init(item);
30		lru->nr_items--;
31		spin_unlock(&lru->lock);
32		return true;
33	}
34	spin_unlock(&lru->lock);
35	return false;
36}
37EXPORT_SYMBOL_GPL(list_lru_del);
38
39unsigned long list_lru_walk(struct list_lru *lru, list_lru_walk_cb isolate,
40			    void *cb_arg, unsigned long nr_to_walk)
41{
42	struct list_head *item, *n;
43	unsigned long removed = 0;
44	/*
45	 * If we don't keep state of at which pass we are, we can loop at
46	 * LRU_RETRY, since we have no guarantees that the caller will be able
47	 * to do something other than retry on the next pass. We handle this by
48	 * allowing at most one retry per object. This should not be altered
49	 * by any condition other than LRU_RETRY.
50	 */
51	bool first_pass = true;
52
53	spin_lock(&lru->lock);
54restart:
55	list_for_each_safe(item, n, &lru->list) {
56		enum lru_status ret;
57		ret = isolate(item, &lru->lock, cb_arg);
58		switch (ret) {
59		case LRU_REMOVED:
60			lru->nr_items--;
61			removed++;
62			break;
63		case LRU_ROTATE:
64			list_move_tail(item, &lru->list);
65			break;
66		case LRU_SKIP:
67			break;
68		case LRU_RETRY:
69			if (!first_pass) {
70				first_pass = true;
71				break;
72			}
73			first_pass = false;
74			goto restart;
75		default:
76			BUG();
77		}
78
79		if (nr_to_walk-- == 0)
80			break;
81
82	}
83	spin_unlock(&lru->lock);
84	return removed;
85}
86EXPORT_SYMBOL_GPL(list_lru_walk);
87
88unsigned long list_lru_dispose_all(struct list_lru *lru,
89				   list_lru_dispose_cb dispose)
90{
91	unsigned long disposed = 0;
92	LIST_HEAD(dispose_list);
93
94	spin_lock(&lru->lock);
95	while (!list_empty(&lru->list)) {
96		list_splice_init(&lru->list, &dispose_list);
97		disposed += lru->nr_items;
98		lru->nr_items = 0;
99		spin_unlock(&lru->lock);
100
101		dispose(&dispose_list);
102
103		spin_lock(&lru->lock);
104	}
105	spin_unlock(&lru->lock);
106	return disposed;
107}
108
109int list_lru_init(struct list_lru *lru)
110{
111	spin_lock_init(&lru->lock);
112	INIT_LIST_HEAD(&lru->list);
113	lru->nr_items = 0;
114
115	return 0;
116}
117EXPORT_SYMBOL_GPL(list_lru_init);
118