/* * Copyright (c) 2013 Red Hat, Inc. and Parallels Inc. All rights reserved. * Authors: David Chinner and Glauber Costa * * Generic LRU infrastructure */ #include #include #include bool list_lru_add(struct list_lru *lru, struct list_head *item) { spin_lock(&lru->lock); if (list_empty(item)) { list_add_tail(item, &lru->list); lru->nr_items++; spin_unlock(&lru->lock); return true; } spin_unlock(&lru->lock); return false; } EXPORT_SYMBOL_GPL(list_lru_add); bool list_lru_del(struct list_lru *lru, struct list_head *item) { spin_lock(&lru->lock); if (!list_empty(item)) { list_del_init(item); lru->nr_items--; spin_unlock(&lru->lock); return true; } spin_unlock(&lru->lock); return false; } EXPORT_SYMBOL_GPL(list_lru_del); unsigned long list_lru_walk(struct list_lru *lru, list_lru_walk_cb isolate, void *cb_arg, unsigned long nr_to_walk) { struct list_head *item, *n; unsigned long removed = 0; /* * 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; spin_lock(&lru->lock); restart: list_for_each_safe(item, n, &lru->list) { enum lru_status ret; ret = isolate(item, &lru->lock, cb_arg); switch (ret) { case LRU_REMOVED: lru->nr_items--; removed++; break; case LRU_ROTATE: list_move_tail(item, &lru->list); break; case LRU_SKIP: break; case LRU_RETRY: if (!first_pass) { first_pass = true; break; } first_pass = false; goto restart; default: BUG(); } if (nr_to_walk-- == 0) break; } spin_unlock(&lru->lock); return removed; } EXPORT_SYMBOL_GPL(list_lru_walk); unsigned long list_lru_dispose_all(struct list_lru *lru, list_lru_dispose_cb dispose) { unsigned long disposed = 0; LIST_HEAD(dispose_list); spin_lock(&lru->lock); while (!list_empty(&lru->list)) { list_splice_init(&lru->list, &dispose_list); disposed += lru->nr_items; lru->nr_items = 0; spin_unlock(&lru->lock); dispose(&dispose_list); spin_lock(&lru->lock); } spin_unlock(&lru->lock); return disposed; } int list_lru_init(struct list_lru *lru) { spin_lock_init(&lru->lock); INIT_LIST_HEAD(&lru->list); lru->nr_items = 0; return 0; } EXPORT_SYMBOL_GPL(list_lru_init);