list_lru.c revision 5cedf721a7cdb54e9222133516c916210d836470
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/mm.h> 10#include <linux/list_lru.h> 11 12bool list_lru_add(struct list_lru *lru, struct list_head *item) 13{ 14 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); 19 if (list_empty(item)) { 20 list_add_tail(item, &nlru->list); 21 if (nlru->nr_items++ == 0) 22 node_set(nid, lru->active_nodes); 23 spin_unlock(&nlru->lock); 24 return true; 25 } 26 spin_unlock(&nlru->lock); 27 return false; 28} 29EXPORT_SYMBOL_GPL(list_lru_add); 30 31bool list_lru_del(struct list_lru *lru, struct list_head *item) 32{ 33 int nid = page_to_nid(virt_to_page(item)); 34 struct list_lru_node *nlru = &lru->node[nid]; 35 36 spin_lock(&nlru->lock); 37 if (!list_empty(item)) { 38 list_del_init(item); 39 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); 43 return true; 44 } 45 spin_unlock(&nlru->lock); 46 return false; 47} 48EXPORT_SYMBOL_GPL(list_lru_del); 49 50unsigned long list_lru_count(struct list_lru *lru) 51{ 52 unsigned long count = 0; 53 int nid; 54 55 for_each_node_mask(nid, lru->active_nodes) { 56 struct list_lru_node *nlru = &lru->node[nid]; 57 58 spin_lock(&nlru->lock); 59 WARN_ON_ONCE(nlru->nr_items < 0); 60 count += nlru->nr_items; 61 spin_unlock(&nlru->lock); 62 } 63 64 return count; 65} 66EXPORT_SYMBOL_GPL(list_lru_count); 67 68static unsigned long 69list_lru_walk_node(struct list_lru *lru, int nid, list_lru_walk_cb isolate, 70 void *cb_arg, unsigned long *nr_to_walk) 71{ 72 73 struct list_lru_node *nlru = &lru->node[nid]; 74 struct list_head *item, *n; 75 unsigned long isolated = 0; 76 77 spin_lock(&nlru->lock); 78restart: 79 list_for_each_safe(item, n, &nlru->list) { 80 enum lru_status ret; 81 82 /* 83 * decrement nr_to_walk first so that we don't livelock if we 84 * get stuck on large numbesr of LRU_RETRY items 85 */ 86 if (--(*nr_to_walk) == 0) 87 break; 88 89 ret = isolate(item, &nlru->lock, cb_arg); 90 switch (ret) { 91 case LRU_REMOVED: 92 if (--nlru->nr_items == 0) 93 node_clear(nid, lru->active_nodes); 94 WARN_ON_ONCE(nlru->nr_items < 0); 95 isolated++; 96 break; 97 case LRU_ROTATE: 98 list_move_tail(item, &nlru->list); 99 break; 100 case LRU_SKIP: 101 break; 102 case LRU_RETRY: 103 /* 104 * The lru lock has been dropped, our list traversal is 105 * now invalid and so we have to restart from scratch. 106 */ 107 goto restart; 108 default: 109 BUG(); 110 } 111 } 112 113 spin_unlock(&nlru->lock); 114 return isolated; 115} 116EXPORT_SYMBOL_GPL(list_lru_walk_node); 117 118unsigned long list_lru_walk(struct list_lru *lru, list_lru_walk_cb isolate, 119 void *cb_arg, unsigned long nr_to_walk) 120{ 121 unsigned long isolated = 0; 122 int nid; 123 124 for_each_node_mask(nid, lru->active_nodes) { 125 isolated += list_lru_walk_node(lru, nid, isolate, 126 cb_arg, &nr_to_walk); 127 if (nr_to_walk <= 0) 128 break; 129 } 130 return isolated; 131} 132EXPORT_SYMBOL_GPL(list_lru_walk); 133 134static unsigned long list_lru_dispose_all_node(struct list_lru *lru, int nid, 135 list_lru_dispose_cb dispose) 136{ 137 struct list_lru_node *nlru = &lru->node[nid]; 138 LIST_HEAD(dispose_list); 139 unsigned long disposed = 0; 140 141 spin_lock(&nlru->lock); 142 while (!list_empty(&nlru->list)) { 143 list_splice_init(&nlru->list, &dispose_list); 144 disposed += nlru->nr_items; 145 nlru->nr_items = 0; 146 node_clear(nid, lru->active_nodes); 147 spin_unlock(&nlru->lock); 148 149 dispose(&dispose_list); 150 151 spin_lock(&nlru->lock); 152 } 153 spin_unlock(&nlru->lock); 154 return disposed; 155} 156 157unsigned long list_lru_dispose_all(struct list_lru *lru, 158 list_lru_dispose_cb dispose) 159{ 160 unsigned long disposed; 161 unsigned long total = 0; 162 int nid; 163 164 do { 165 disposed = 0; 166 for_each_node_mask(nid, lru->active_nodes) { 167 disposed += list_lru_dispose_all_node(lru, nid, 168 dispose); 169 } 170 total += disposed; 171 } while (disposed != 0); 172 173 return total; 174} 175 176int list_lru_init(struct list_lru *lru) 177{ 178 int i; 179 180 nodes_clear(lru->active_nodes); 181 for (i = 0; i < MAX_NUMNODES; i++) { 182 spin_lock_init(&lru->node[i].lock); 183 INIT_LIST_HEAD(&lru->node[i].list); 184 lru->node[i].nr_items = 0; 185 } 186 return 0; 187} 188EXPORT_SYMBOL_GPL(list_lru_init); 189