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 ) ;