| 
									
										
										
										
											2013-08-28 10:17:58 +10:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * 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>
 | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:00 +10:00
										 |  |  | #include <linux/mm.h>
 | 
					
						
							| 
									
										
										
										
											2013-08-28 10:17:58 +10:00
										 |  |  | #include <linux/list_lru.h>
 | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:18 +10:00
										 |  |  | #include <linux/slab.h>
 | 
					
						
							| 
									
										
										
										
											2013-08-28 10:17:58 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | bool list_lru_add(struct list_lru *lru, struct list_head *item) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:00 +10:00
										 |  |  | 	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); | 
					
						
							| 
									
										
										
										
											2013-08-28 10:17:58 +10:00
										 |  |  | 	if (list_empty(item)) { | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:00 +10:00
										 |  |  | 		list_add_tail(item, &nlru->list); | 
					
						
							|  |  |  | 		if (nlru->nr_items++ == 0) | 
					
						
							|  |  |  | 			node_set(nid, lru->active_nodes); | 
					
						
							|  |  |  | 		spin_unlock(&nlru->lock); | 
					
						
							| 
									
										
										
										
											2013-08-28 10:17:58 +10:00
										 |  |  | 		return true; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:00 +10:00
										 |  |  | 	spin_unlock(&nlru->lock); | 
					
						
							| 
									
										
										
										
											2013-08-28 10:17:58 +10:00
										 |  |  | 	return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(list_lru_add); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool list_lru_del(struct list_lru *lru, struct list_head *item) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:00 +10:00
										 |  |  | 	int nid = page_to_nid(virt_to_page(item)); | 
					
						
							|  |  |  | 	struct list_lru_node *nlru = &lru->node[nid]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock(&nlru->lock); | 
					
						
							| 
									
										
										
										
											2013-08-28 10:17:58 +10:00
										 |  |  | 	if (!list_empty(item)) { | 
					
						
							|  |  |  | 		list_del_init(item); | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:00 +10:00
										 |  |  | 		if (--nlru->nr_items == 0) | 
					
						
							|  |  |  | 			node_clear(nid, lru->active_nodes); | 
					
						
							|  |  |  | 		WARN_ON_ONCE(nlru->nr_items < 0); | 
					
						
							|  |  |  | 		spin_unlock(&nlru->lock); | 
					
						
							| 
									
										
										
										
											2013-08-28 10:17:58 +10:00
										 |  |  | 		return true; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:00 +10:00
										 |  |  | 	spin_unlock(&nlru->lock); | 
					
						
							| 
									
										
										
										
											2013-08-28 10:17:58 +10:00
										 |  |  | 	return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(list_lru_del); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:02 +10:00
										 |  |  | unsigned long | 
					
						
							|  |  |  | list_lru_count_node(struct list_lru *lru, int nid) | 
					
						
							| 
									
										
										
										
											2013-08-28 10:17:58 +10:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:00 +10:00
										 |  |  | 	unsigned long count = 0; | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:02 +10:00
										 |  |  | 	struct list_lru_node *nlru = &lru->node[nid]; | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:00 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:02 +10:00
										 |  |  | 	spin_lock(&nlru->lock); | 
					
						
							|  |  |  | 	WARN_ON_ONCE(nlru->nr_items < 0); | 
					
						
							|  |  |  | 	count += nlru->nr_items; | 
					
						
							|  |  |  | 	spin_unlock(&nlru->lock); | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:00 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return count; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:02 +10:00
										 |  |  | EXPORT_SYMBOL_GPL(list_lru_count_node); | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:00 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:02 +10:00
										 |  |  | unsigned long | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:00 +10:00
										 |  |  | 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]; | 
					
						
							| 
									
										
										
										
											2013-08-28 10:17:58 +10:00
										 |  |  | 	struct list_head *item, *n; | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:00 +10:00
										 |  |  | 	unsigned long isolated = 0; | 
					
						
							| 
									
										
										
										
											2013-08-28 10:17:58 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:00 +10:00
										 |  |  | 	spin_lock(&nlru->lock); | 
					
						
							| 
									
										
										
										
											2013-08-28 10:17:58 +10:00
										 |  |  | restart: | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:00 +10:00
										 |  |  | 	list_for_each_safe(item, n, &nlru->list) { | 
					
						
							| 
									
										
										
										
											2013-08-28 10:17:58 +10:00
										 |  |  | 		enum lru_status ret; | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:01 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * decrement nr_to_walk first so that we don't livelock if we | 
					
						
							|  |  |  | 		 * get stuck on large numbesr of LRU_RETRY items | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		if (--(*nr_to_walk) == 0) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:00 +10:00
										 |  |  | 		ret = isolate(item, &nlru->lock, cb_arg); | 
					
						
							| 
									
										
										
										
											2013-08-28 10:17:58 +10:00
										 |  |  | 		switch (ret) { | 
					
						
							|  |  |  | 		case LRU_REMOVED: | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:00 +10:00
										 |  |  | 			if (--nlru->nr_items == 0) | 
					
						
							|  |  |  | 				node_clear(nid, lru->active_nodes); | 
					
						
							|  |  |  | 			WARN_ON_ONCE(nlru->nr_items < 0); | 
					
						
							|  |  |  | 			isolated++; | 
					
						
							| 
									
										
										
										
											2013-08-28 10:17:58 +10:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		case LRU_ROTATE: | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:00 +10:00
										 |  |  | 			list_move_tail(item, &nlru->list); | 
					
						
							| 
									
										
										
										
											2013-08-28 10:17:58 +10:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		case LRU_SKIP: | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case LRU_RETRY: | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:01 +10:00
										 |  |  | 			/*
 | 
					
						
							|  |  |  | 			 * The lru lock has been dropped, our list traversal is | 
					
						
							|  |  |  | 			 * now invalid and so we have to restart from scratch. | 
					
						
							|  |  |  | 			 */ | 
					
						
							| 
									
										
										
										
											2013-08-28 10:17:58 +10:00
										 |  |  | 			goto restart; | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			BUG(); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:00 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	spin_unlock(&nlru->lock); | 
					
						
							|  |  |  | 	return isolated; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(list_lru_walk_node); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-28 10:17:58 +10:00
										 |  |  | int list_lru_init(struct list_lru *lru) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:00 +10:00
										 |  |  | 	int i; | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:18 +10:00
										 |  |  | 	size_t size = sizeof(*lru->node) * nr_node_ids; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	lru->node = kzalloc(size, GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!lru->node) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							| 
									
										
										
										
											2013-08-28 10:17:58 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:00 +10:00
										 |  |  | 	nodes_clear(lru->active_nodes); | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:18 +10:00
										 |  |  | 	for (i = 0; i < nr_node_ids; i++) { | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:00 +10:00
										 |  |  | 		spin_lock_init(&lru->node[i].lock); | 
					
						
							|  |  |  | 		INIT_LIST_HEAD(&lru->node[i].list); | 
					
						
							|  |  |  | 		lru->node[i].nr_items = 0; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-08-28 10:17:58 +10:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(list_lru_init); | 
					
						
							| 
									
										
										
										
											2013-08-28 10:18:18 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | void list_lru_destroy(struct list_lru *lru) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	kfree(lru->node); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(list_lru_destroy); |