2005-04-16 15:20:36 -07:00
/*
* mm / thrash . c
*
* Copyright ( C ) 2004 , Red Hat , Inc .
* Copyright ( C ) 2004 , Rik van Riel < riel @ redhat . com >
* Released under the GPL , see the file COPYING for details .
*
* Simple token based thrashing protection , using the algorithm
* described in : http : //www.cs.wm.edu/~sjiang/token.pdf
2006-12-06 20:31:57 -08:00
*
* Sep 2006 , Ashwin Chaugule < ashwin . chaugule @ celunite . com >
* Improved algorithm to pass token :
* Each task has a priority which is incremented if it contended
* for the token in an interval less than its previous attempt .
* If the token is acquired , that task ' s priority is boosted to prevent
* the token from bouncing around too often and to let the task make
* some progress in its execution .
2005-04-16 15:20:36 -07:00
*/
2006-12-06 20:31:57 -08:00
2005-04-16 15:20:36 -07:00
# include <linux/jiffies.h>
# include <linux/mm.h>
# include <linux/sched.h>
# include <linux/swap.h>
2011-06-15 15:08:13 -07:00
# include <linux/memcontrol.h>
2005-04-16 15:20:36 -07:00
2011-06-15 15:08:14 -07:00
# include <trace/events/vmscan.h>
2005-04-16 15:20:36 -07:00
static DEFINE_SPINLOCK ( swap_token_lock ) ;
2006-12-06 20:31:57 -08:00
struct mm_struct * swap_token_mm ;
2011-06-15 15:08:13 -07:00
struct mem_cgroup * swap_token_memcg ;
2006-12-06 20:32:43 -08:00
static unsigned int global_faults ;
2005-04-16 15:20:36 -07:00
2011-06-15 15:08:13 -07:00
# ifdef CONFIG_CGROUP_MEM_RES_CTLR
static struct mem_cgroup * swap_token_memcg_from_mm ( struct mm_struct * mm )
{
struct mem_cgroup * memcg ;
memcg = try_get_mem_cgroup_from_mm ( mm ) ;
if ( memcg )
css_put ( mem_cgroup_css ( memcg ) ) ;
return memcg ;
}
# else
static struct mem_cgroup * swap_token_memcg_from_mm ( struct mm_struct * mm )
{
return NULL ;
}
# endif
2009-06-23 12:36:58 -07:00
void grab_swap_token ( struct mm_struct * mm )
2005-04-16 15:20:36 -07:00
{
2006-12-06 20:31:57 -08:00
int current_interval ;
2011-06-15 15:08:14 -07:00
unsigned int old_prio = mm - > token_priority ;
2005-04-16 15:20:36 -07:00
2006-12-06 20:31:57 -08:00
global_faults + + ;
2005-04-16 15:20:36 -07:00
2009-06-23 12:36:58 -07:00
current_interval = global_faults - mm - > faultstamp ;
2005-04-16 15:20:36 -07:00
2006-12-06 20:31:57 -08:00
if ( ! spin_trylock ( & swap_token_lock ) )
return ;
2005-04-16 15:20:36 -07:00
2006-12-06 20:31:57 -08:00
/* First come first served */
2011-06-15 15:08:13 -07:00
if ( ! swap_token_mm )
goto replace_token ;
if ( mm = = swap_token_mm ) {
mm - > token_priority + = 2 ;
2011-06-15 15:08:14 -07:00
goto update_priority ;
2006-12-06 20:31:57 -08:00
}
2005-04-16 15:20:36 -07:00
2011-06-15 15:08:13 -07:00
if ( current_interval < mm - > last_interval )
mm - > token_priority + + ;
else {
if ( likely ( mm - > token_priority > 0 ) )
mm - > token_priority - - ;
2005-04-16 15:20:36 -07:00
}
2006-12-06 20:31:57 -08:00
2011-06-15 15:08:13 -07:00
/* Check if we deserve the token */
if ( mm - > token_priority > swap_token_mm - > token_priority )
goto replace_token ;
2011-06-15 15:08:14 -07:00
update_priority :
trace_update_swap_token_priority ( mm , old_prio ) ;
2006-12-06 20:31:57 -08:00
out :
2009-06-23 12:36:58 -07:00
mm - > faultstamp = global_faults ;
mm - > last_interval = current_interval ;
2006-12-06 20:31:57 -08:00
spin_unlock ( & swap_token_lock ) ;
2011-06-15 15:08:13 -07:00
return ;
replace_token :
mm - > token_priority + = 2 ;
2011-06-15 15:08:14 -07:00
trace_replace_swap_token ( swap_token_mm , mm ) ;
2011-06-15 15:08:13 -07:00
swap_token_mm = mm ;
swap_token_memcg = swap_token_memcg_from_mm ( mm ) ;
goto out ;
2005-04-16 15:20:36 -07:00
}
/* Called on process exit. */
void __put_swap_token ( struct mm_struct * mm )
{
spin_lock ( & swap_token_lock ) ;
2011-06-15 15:08:13 -07:00
if ( likely ( mm = = swap_token_mm ) ) {
2011-06-15 15:08:14 -07:00
trace_put_swap_token ( swap_token_mm ) ;
2006-12-06 20:31:57 -08:00
swap_token_mm = NULL ;
2011-06-15 15:08:13 -07:00
swap_token_memcg = NULL ;
}
2005-04-16 15:20:36 -07:00
spin_unlock ( & swap_token_lock ) ;
}
2011-06-15 15:08:13 -07:00
static bool match_memcg ( struct mem_cgroup * a , struct mem_cgroup * b )
{
if ( ! a )
return true ;
if ( ! b )
return true ;
if ( a = = b )
return true ;
return false ;
}
void disable_swap_token ( struct mem_cgroup * memcg )
{
/* memcg reclaim don't disable unrelated mm token. */
if ( match_memcg ( memcg , swap_token_memcg ) ) {
spin_lock ( & swap_token_lock ) ;
if ( match_memcg ( memcg , swap_token_memcg ) ) {
2011-06-15 15:08:14 -07:00
trace_disable_swap_token ( swap_token_mm ) ;
2011-06-15 15:08:13 -07:00
swap_token_mm = NULL ;
swap_token_memcg = NULL ;
}
spin_unlock ( & swap_token_lock ) ;
}
}