2008-02-07 11:13:49 +03:00
/*
* resource cgroups
*
* Copyright 2007 OpenVZ SWsoft Inc
*
* Author : Pavel Emelianov < xemul @ openvz . org >
*
*/
# include <linux/types.h>
# include <linux/parser.h>
# include <linux/fs.h>
2008-04-29 11:59:25 +04:00
# include <linux/slab.h>
2008-02-07 11:13:49 +03:00
# include <linux/res_counter.h>
# include <linux/uaccess.h>
2008-07-25 12:47:04 +04:00
# include <linux/mm.h>
2008-02-07 11:13:49 +03:00
2009-01-08 05:08:05 +03:00
void res_counter_init ( struct res_counter * counter , struct res_counter * parent )
2008-02-07 11:13:49 +03:00
{
spin_lock_init ( & counter - > lock ) ;
2009-06-18 03:27:20 +04:00
counter - > limit = RESOURCE_MAX ;
2009-09-24 02:56:36 +04:00
counter - > soft_limit = RESOURCE_MAX ;
2009-01-08 05:08:05 +03:00
counter - > parent = parent ;
2008-02-07 11:13:49 +03:00
}
int res_counter_charge_locked ( struct res_counter * counter , unsigned long val )
{
if ( counter - > usage + val > counter - > limit ) {
counter - > failcnt + + ;
return - ENOMEM ;
}
counter - > usage + = val ;
2008-04-29 12:00:17 +04:00
if ( counter - > usage > counter - > max_usage )
counter - > max_usage = counter - > usage ;
2008-02-07 11:13:49 +03:00
return 0 ;
}
2009-01-08 05:08:05 +03:00
int res_counter_charge ( struct res_counter * counter , unsigned long val ,
2009-09-24 02:56:37 +04:00
struct res_counter * * limit_fail_at ,
struct res_counter * * soft_limit_fail_at )
2008-02-07 11:13:49 +03:00
{
int ret ;
unsigned long flags ;
2009-01-08 05:08:05 +03:00
struct res_counter * c , * u ;
2008-02-07 11:13:49 +03:00
2009-01-08 05:08:05 +03:00
* limit_fail_at = NULL ;
2009-09-24 02:56:37 +04:00
if ( soft_limit_fail_at )
* soft_limit_fail_at = NULL ;
2009-01-08 05:08:05 +03:00
local_irq_save ( flags ) ;
for ( c = counter ; c ! = NULL ; c = c - > parent ) {
spin_lock ( & c - > lock ) ;
ret = res_counter_charge_locked ( c , val ) ;
2009-09-24 02:56:37 +04:00
/*
* With soft limits , we return the highest ancestor
* that exceeds its soft limit
*/
if ( soft_limit_fail_at & &
! res_counter_soft_limit_check_locked ( c ) )
* soft_limit_fail_at = c ;
2009-01-08 05:08:05 +03:00
spin_unlock ( & c - > lock ) ;
if ( ret < 0 ) {
* limit_fail_at = c ;
goto undo ;
}
}
ret = 0 ;
goto done ;
undo :
for ( u = counter ; u ! = c ; u = u - > parent ) {
spin_lock ( & u - > lock ) ;
res_counter_uncharge_locked ( u , val ) ;
spin_unlock ( & u - > lock ) ;
}
done :
local_irq_restore ( flags ) ;
2008-02-07 11:13:49 +03:00
return ret ;
}
void res_counter_uncharge_locked ( struct res_counter * counter , unsigned long val )
{
if ( WARN_ON ( counter - > usage < val ) )
val = counter - > usage ;
counter - > usage - = val ;
}
2009-09-24 02:56:37 +04:00
void res_counter_uncharge ( struct res_counter * counter , unsigned long val ,
bool * was_soft_limit_excess )
2008-02-07 11:13:49 +03:00
{
unsigned long flags ;
2009-01-08 05:08:05 +03:00
struct res_counter * c ;
2008-02-07 11:13:49 +03:00
2009-01-08 05:08:05 +03:00
local_irq_save ( flags ) ;
for ( c = counter ; c ! = NULL ; c = c - > parent ) {
spin_lock ( & c - > lock ) ;
2009-09-24 02:56:37 +04:00
if ( was_soft_limit_excess )
* was_soft_limit_excess =
! res_counter_soft_limit_check_locked ( c ) ;
2009-01-08 05:08:05 +03:00
res_counter_uncharge_locked ( c , val ) ;
spin_unlock ( & c - > lock ) ;
}
local_irq_restore ( flags ) ;
2008-02-07 11:13:49 +03:00
}
2008-02-07 11:13:57 +03:00
static inline unsigned long long *
res_counter_member ( struct res_counter * counter , int member )
2008-02-07 11:13:49 +03:00
{
switch ( member ) {
case RES_USAGE :
return & counter - > usage ;
2008-04-29 12:00:17 +04:00
case RES_MAX_USAGE :
return & counter - > max_usage ;
2008-02-07 11:13:49 +03:00
case RES_LIMIT :
return & counter - > limit ;
case RES_FAILCNT :
return & counter - > failcnt ;
2009-09-24 02:56:36 +04:00
case RES_SOFT_LIMIT :
return & counter - > soft_limit ;
2008-02-07 11:13:49 +03:00
} ;
BUG ( ) ;
return NULL ;
}
ssize_t res_counter_read ( struct res_counter * counter , int member ,
2008-02-07 11:13:57 +03:00
const char __user * userbuf , size_t nbytes , loff_t * pos ,
int ( * read_strategy ) ( unsigned long long val , char * st_buf ) )
2008-02-07 11:13:49 +03:00
{
2008-02-07 11:13:57 +03:00
unsigned long long * val ;
2008-02-07 11:13:49 +03:00
char buf [ 64 ] , * s ;
s = buf ;
val = res_counter_member ( counter , member ) ;
2008-02-07 11:13:57 +03:00
if ( read_strategy )
s + = read_strategy ( * val , s ) ;
else
s + = sprintf ( s , " %llu \n " , * val ) ;
2008-02-07 11:13:49 +03:00
return simple_read_from_buffer ( ( void __user * ) userbuf , nbytes ,
pos , buf , s - buf ) ;
}
2008-04-29 11:59:58 +04:00
u64 res_counter_read_u64 ( struct res_counter * counter , int member )
{
return * res_counter_member ( counter , member ) ;
}
2008-07-25 12:47:04 +04:00
int res_counter_memparse_write_strategy ( const char * buf ,
unsigned long long * res )
2008-02-07 11:13:49 +03:00
{
2008-07-25 12:47:04 +04:00
char * end ;
2009-06-18 03:27:20 +04:00
/* return RESOURCE_MAX(unlimited) if "-1" is specified */
if ( * buf = = ' - ' ) {
* res = simple_strtoull ( buf + 1 , & end , 10 ) ;
if ( * res ! = 1 | | * end ! = ' \0 ' )
return - EINVAL ;
* res = RESOURCE_MAX ;
return 0 ;
}
2008-07-25 12:47:04 +04:00
/* FIXME - make memparse() take const char* args */
* res = memparse ( ( char * ) buf , & end ) ;
if ( * end ! = ' \0 ' )
return - EINVAL ;
2008-02-07 11:13:49 +03:00
2008-07-25 12:47:04 +04:00
* res = PAGE_ALIGN ( * res ) ;
return 0 ;
}
2008-02-07 11:13:49 +03:00
2008-07-25 12:47:04 +04:00
int res_counter_write ( struct res_counter * counter , int member ,
const char * buf , write_strategy_fn write_strategy )
{
char * end ;
unsigned long flags ;
unsigned long long tmp , * val ;
2008-02-07 11:13:49 +03:00
2008-02-07 11:13:57 +03:00
if ( write_strategy ) {
2008-07-25 12:47:04 +04:00
if ( write_strategy ( buf , & tmp ) )
return - EINVAL ;
2008-02-07 11:13:57 +03:00
} else {
tmp = simple_strtoull ( buf , & end , 10 ) ;
if ( * end ! = ' \0 ' )
2008-07-25 12:47:04 +04:00
return - EINVAL ;
2008-02-07 11:13:57 +03:00
}
spin_lock_irqsave ( & counter - > lock , flags ) ;
2008-02-07 11:13:49 +03:00
val = res_counter_member ( counter , member ) ;
* val = tmp ;
2008-02-07 11:13:57 +03:00
spin_unlock_irqrestore ( & counter - > lock , flags ) ;
2008-07-25 12:47:04 +04:00
return 0 ;
2008-02-07 11:13:49 +03:00
}