2022-10-27 01:59:41 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* DAMON sysfs Interface
*
* Copyright ( c ) 2022 SeongJae Park < sj @ kernel . org >
*/
# include <linux/slab.h>
# include "sysfs-common.h"
2022-11-02 01:03:23 +03:00
/*
* scheme region directory
*/
struct damon_sysfs_scheme_region {
struct kobject kobj ;
struct damon_addr_range ar ;
unsigned int nr_accesses ;
unsigned int age ;
struct list_head list ;
} ;
static struct damon_sysfs_scheme_region * damon_sysfs_scheme_region_alloc (
struct damon_region * region )
{
struct damon_sysfs_scheme_region * sysfs_region = kmalloc (
sizeof ( * sysfs_region ) , GFP_KERNEL ) ;
if ( ! sysfs_region )
return NULL ;
sysfs_region - > kobj = ( struct kobject ) { } ;
sysfs_region - > ar = region - > ar ;
sysfs_region - > nr_accesses = region - > nr_accesses ;
sysfs_region - > age = region - > age ;
INIT_LIST_HEAD ( & sysfs_region - > list ) ;
return sysfs_region ;
}
static ssize_t start_show ( struct kobject * kobj , struct kobj_attribute * attr ,
char * buf )
{
struct damon_sysfs_scheme_region * region = container_of ( kobj ,
struct damon_sysfs_scheme_region , kobj ) ;
return sysfs_emit ( buf , " %lu \n " , region - > ar . start ) ;
}
static ssize_t end_show ( struct kobject * kobj , struct kobj_attribute * attr ,
char * buf )
{
struct damon_sysfs_scheme_region * region = container_of ( kobj ,
struct damon_sysfs_scheme_region , kobj ) ;
return sysfs_emit ( buf , " %lu \n " , region - > ar . end ) ;
}
static ssize_t nr_accesses_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * buf )
{
struct damon_sysfs_scheme_region * region = container_of ( kobj ,
struct damon_sysfs_scheme_region , kobj ) ;
return sysfs_emit ( buf , " %u \n " , region - > nr_accesses ) ;
}
static ssize_t age_show ( struct kobject * kobj , struct kobj_attribute * attr ,
char * buf )
{
struct damon_sysfs_scheme_region * region = container_of ( kobj ,
struct damon_sysfs_scheme_region , kobj ) ;
return sysfs_emit ( buf , " %u \n " , region - > age ) ;
}
static void damon_sysfs_scheme_region_release ( struct kobject * kobj )
{
struct damon_sysfs_scheme_region * region = container_of ( kobj ,
struct damon_sysfs_scheme_region , kobj ) ;
list_del ( & region - > list ) ;
kfree ( region ) ;
}
static struct kobj_attribute damon_sysfs_scheme_region_start_attr =
__ATTR_RO_MODE ( start , 0400 ) ;
static struct kobj_attribute damon_sysfs_scheme_region_end_attr =
__ATTR_RO_MODE ( end , 0400 ) ;
static struct kobj_attribute damon_sysfs_scheme_region_nr_accesses_attr =
__ATTR_RO_MODE ( nr_accesses , 0400 ) ;
static struct kobj_attribute damon_sysfs_scheme_region_age_attr =
__ATTR_RO_MODE ( age , 0400 ) ;
static struct attribute * damon_sysfs_scheme_region_attrs [ ] = {
& damon_sysfs_scheme_region_start_attr . attr ,
& damon_sysfs_scheme_region_end_attr . attr ,
& damon_sysfs_scheme_region_nr_accesses_attr . attr ,
& damon_sysfs_scheme_region_age_attr . attr ,
NULL ,
} ;
ATTRIBUTE_GROUPS ( damon_sysfs_scheme_region ) ;
static struct kobj_type damon_sysfs_scheme_region_ktype = {
. release = damon_sysfs_scheme_region_release ,
. sysfs_ops = & kobj_sysfs_ops ,
. default_groups = damon_sysfs_scheme_region_groups ,
} ;
2022-11-02 01:03:22 +03:00
/*
* scheme regions directory
*/
struct damon_sysfs_scheme_regions {
struct kobject kobj ;
2022-11-02 01:03:23 +03:00
struct list_head regions_list ;
int nr_regions ;
2022-11-02 01:03:22 +03:00
} ;
static struct damon_sysfs_scheme_regions *
damon_sysfs_scheme_regions_alloc ( void )
{
2022-11-02 01:03:23 +03:00
struct damon_sysfs_scheme_regions * regions = kmalloc ( sizeof ( * regions ) ,
GFP_KERNEL ) ;
regions - > kobj = ( struct kobject ) { } ;
INIT_LIST_HEAD ( & regions - > regions_list ) ;
regions - > nr_regions = 0 ;
return regions ;
}
static void damon_sysfs_scheme_regions_rm_dirs (
struct damon_sysfs_scheme_regions * regions )
{
struct damon_sysfs_scheme_region * r , * next ;
list_for_each_entry_safe ( r , next , & regions - > regions_list , list ) {
/* release function deletes it from the list */
kobject_put ( & r - > kobj ) ;
regions - > nr_regions - - ;
}
2022-11-02 01:03:22 +03:00
}
static void damon_sysfs_scheme_regions_release ( struct kobject * kobj )
{
kfree ( container_of ( kobj , struct damon_sysfs_scheme_regions , kobj ) ) ;
}
static struct attribute * damon_sysfs_scheme_regions_attrs [ ] = {
NULL ,
} ;
ATTRIBUTE_GROUPS ( damon_sysfs_scheme_regions ) ;
static struct kobj_type damon_sysfs_scheme_regions_ktype = {
. release = damon_sysfs_scheme_regions_release ,
. sysfs_ops = & kobj_sysfs_ops ,
. default_groups = damon_sysfs_scheme_regions_groups ,
} ;
2022-10-27 01:59:41 +03:00
/*
* schemes / stats directory
*/
struct damon_sysfs_stats {
struct kobject kobj ;
unsigned long nr_tried ;
unsigned long sz_tried ;
unsigned long nr_applied ;
unsigned long sz_applied ;
unsigned long qt_exceeds ;
} ;
static struct damon_sysfs_stats * damon_sysfs_stats_alloc ( void )
{
return kzalloc ( sizeof ( struct damon_sysfs_stats ) , GFP_KERNEL ) ;
}
static ssize_t nr_tried_show ( struct kobject * kobj , struct kobj_attribute * attr ,
char * buf )
{
struct damon_sysfs_stats * stats = container_of ( kobj ,
struct damon_sysfs_stats , kobj ) ;
return sysfs_emit ( buf , " %lu \n " , stats - > nr_tried ) ;
}
static ssize_t sz_tried_show ( struct kobject * kobj , struct kobj_attribute * attr ,
char * buf )
{
struct damon_sysfs_stats * stats = container_of ( kobj ,
struct damon_sysfs_stats , kobj ) ;
return sysfs_emit ( buf , " %lu \n " , stats - > sz_tried ) ;
}
static ssize_t nr_applied_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * buf )
{
struct damon_sysfs_stats * stats = container_of ( kobj ,
struct damon_sysfs_stats , kobj ) ;
return sysfs_emit ( buf , " %lu \n " , stats - > nr_applied ) ;
}
static ssize_t sz_applied_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * buf )
{
struct damon_sysfs_stats * stats = container_of ( kobj ,
struct damon_sysfs_stats , kobj ) ;
return sysfs_emit ( buf , " %lu \n " , stats - > sz_applied ) ;
}
static ssize_t qt_exceeds_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * buf )
{
struct damon_sysfs_stats * stats = container_of ( kobj ,
struct damon_sysfs_stats , kobj ) ;
return sysfs_emit ( buf , " %lu \n " , stats - > qt_exceeds ) ;
}
static void damon_sysfs_stats_release ( struct kobject * kobj )
{
kfree ( container_of ( kobj , struct damon_sysfs_stats , kobj ) ) ;
}
static struct kobj_attribute damon_sysfs_stats_nr_tried_attr =
__ATTR_RO_MODE ( nr_tried , 0400 ) ;
static struct kobj_attribute damon_sysfs_stats_sz_tried_attr =
__ATTR_RO_MODE ( sz_tried , 0400 ) ;
static struct kobj_attribute damon_sysfs_stats_nr_applied_attr =
__ATTR_RO_MODE ( nr_applied , 0400 ) ;
static struct kobj_attribute damon_sysfs_stats_sz_applied_attr =
__ATTR_RO_MODE ( sz_applied , 0400 ) ;
static struct kobj_attribute damon_sysfs_stats_qt_exceeds_attr =
__ATTR_RO_MODE ( qt_exceeds , 0400 ) ;
static struct attribute * damon_sysfs_stats_attrs [ ] = {
& damon_sysfs_stats_nr_tried_attr . attr ,
& damon_sysfs_stats_sz_tried_attr . attr ,
& damon_sysfs_stats_nr_applied_attr . attr ,
& damon_sysfs_stats_sz_applied_attr . attr ,
& damon_sysfs_stats_qt_exceeds_attr . attr ,
NULL ,
} ;
ATTRIBUTE_GROUPS ( damon_sysfs_stats ) ;
static struct kobj_type damon_sysfs_stats_ktype = {
. release = damon_sysfs_stats_release ,
. sysfs_ops = & kobj_sysfs_ops ,
. default_groups = damon_sysfs_stats_groups ,
} ;
2022-12-06 02:08:25 +03:00
/*
* filter directory
*/
struct damon_sysfs_scheme_filter {
struct kobject kobj ;
enum damos_filter_type type ;
bool matching ;
char * memcg_path ;
} ;
2022-12-06 02:08:26 +03:00
static struct damon_sysfs_scheme_filter * damon_sysfs_scheme_filter_alloc ( void )
{
return kzalloc ( sizeof ( struct damon_sysfs_scheme_filter ) , GFP_KERNEL ) ;
}
2022-12-06 02:08:25 +03:00
/* Should match with enum damos_filter_type */
static const char * const damon_sysfs_scheme_filter_type_strs [ ] = {
" anon " ,
" memcg " ,
} ;
static ssize_t type_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * buf )
{
struct damon_sysfs_scheme_filter * filter = container_of ( kobj ,
struct damon_sysfs_scheme_filter , kobj ) ;
return sysfs_emit ( buf , " %s \n " ,
damon_sysfs_scheme_filter_type_strs [ filter - > type ] ) ;
}
static ssize_t type_store ( struct kobject * kobj ,
struct kobj_attribute * attr , const char * buf , size_t count )
{
struct damon_sysfs_scheme_filter * filter = container_of ( kobj ,
struct damon_sysfs_scheme_filter , kobj ) ;
enum damos_filter_type type ;
ssize_t ret = - EINVAL ;
for ( type = 0 ; type < NR_DAMOS_FILTER_TYPES ; type + + ) {
if ( sysfs_streq ( buf , damon_sysfs_scheme_filter_type_strs [
type ] ) ) {
filter - > type = type ;
ret = count ;
break ;
}
}
return ret ;
}
static ssize_t matching_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * buf )
{
struct damon_sysfs_scheme_filter * filter = container_of ( kobj ,
struct damon_sysfs_scheme_filter , kobj ) ;
return sysfs_emit ( buf , " %c \n " , filter - > matching ? ' Y ' : ' N ' ) ;
}
static ssize_t matching_store ( struct kobject * kobj ,
struct kobj_attribute * attr , const char * buf , size_t count )
{
struct damon_sysfs_scheme_filter * filter = container_of ( kobj ,
struct damon_sysfs_scheme_filter , kobj ) ;
bool matching ;
int err = kstrtobool ( buf , & matching ) ;
if ( err )
return err ;
filter - > matching = matching ;
return count ;
}
static ssize_t memcg_path_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * buf )
{
struct damon_sysfs_scheme_filter * filter = container_of ( kobj ,
struct damon_sysfs_scheme_filter , kobj ) ;
return sysfs_emit ( buf , " %s \n " ,
filter - > memcg_path ? filter - > memcg_path : " " ) ;
}
static ssize_t memcg_path_store ( struct kobject * kobj ,
struct kobj_attribute * attr , const char * buf , size_t count )
{
struct damon_sysfs_scheme_filter * filter = container_of ( kobj ,
struct damon_sysfs_scheme_filter , kobj ) ;
char * path = kmalloc ( sizeof ( * path ) * ( count + 1 ) , GFP_KERNEL ) ;
if ( ! path )
return - ENOMEM ;
strncpy ( path , buf , count ) ;
path [ count ] = ' \0 ' ;
filter - > memcg_path = path ;
return count ;
}
static void damon_sysfs_scheme_filter_release ( struct kobject * kobj )
{
struct damon_sysfs_scheme_filter * filter = container_of ( kobj ,
struct damon_sysfs_scheme_filter , kobj ) ;
kfree ( filter - > memcg_path ) ;
kfree ( filter ) ;
}
static struct kobj_attribute damon_sysfs_scheme_filter_type_attr =
__ATTR_RW_MODE ( type , 0600 ) ;
static struct kobj_attribute damon_sysfs_scheme_filter_matching_attr =
__ATTR_RW_MODE ( matching , 0600 ) ;
static struct kobj_attribute damon_sysfs_scheme_filter_memcg_path_attr =
__ATTR_RW_MODE ( memcg_path , 0600 ) ;
static struct attribute * damon_sysfs_scheme_filter_attrs [ ] = {
& damon_sysfs_scheme_filter_type_attr . attr ,
& damon_sysfs_scheme_filter_matching_attr . attr ,
& damon_sysfs_scheme_filter_memcg_path_attr . attr ,
NULL ,
} ;
ATTRIBUTE_GROUPS ( damon_sysfs_scheme_filter ) ;
static struct kobj_type damon_sysfs_scheme_filter_ktype = {
. release = damon_sysfs_scheme_filter_release ,
. sysfs_ops = & kobj_sysfs_ops ,
. default_groups = damon_sysfs_scheme_filter_groups ,
} ;
2022-12-06 02:08:24 +03:00
/*
* filters directory
*/
struct damon_sysfs_scheme_filters {
struct kobject kobj ;
2022-12-06 02:08:26 +03:00
struct damon_sysfs_scheme_filter * * filters_arr ;
2022-12-06 02:08:24 +03:00
int nr ;
} ;
static struct damon_sysfs_scheme_filters *
damon_sysfs_scheme_filters_alloc ( void )
{
return kzalloc ( sizeof ( struct damon_sysfs_scheme_filters ) , GFP_KERNEL ) ;
}
2022-12-06 02:08:26 +03:00
static void damon_sysfs_scheme_filters_rm_dirs (
struct damon_sysfs_scheme_filters * filters )
{
struct damon_sysfs_scheme_filter * * filters_arr = filters - > filters_arr ;
int i ;
for ( i = 0 ; i < filters - > nr ; i + + )
kobject_put ( & filters_arr [ i ] - > kobj ) ;
filters - > nr = 0 ;
kfree ( filters_arr ) ;
filters - > filters_arr = NULL ;
}
static int damon_sysfs_scheme_filters_add_dirs (
struct damon_sysfs_scheme_filters * filters , int nr_filters )
{
struct damon_sysfs_scheme_filter * * filters_arr , * filter ;
int err , i ;
damon_sysfs_scheme_filters_rm_dirs ( filters ) ;
if ( ! nr_filters )
return 0 ;
filters_arr = kmalloc_array ( nr_filters , sizeof ( * filters_arr ) ,
GFP_KERNEL | __GFP_NOWARN ) ;
if ( ! filters_arr )
return - ENOMEM ;
filters - > filters_arr = filters_arr ;
for ( i = 0 ; i < nr_filters ; i + + ) {
filter = damon_sysfs_scheme_filter_alloc ( ) ;
if ( ! filter ) {
damon_sysfs_scheme_filters_rm_dirs ( filters ) ;
return - ENOMEM ;
}
err = kobject_init_and_add ( & filter - > kobj ,
& damon_sysfs_scheme_filter_ktype ,
& filters - > kobj , " %d " , i ) ;
if ( err ) {
kobject_put ( & filter - > kobj ) ;
damon_sysfs_scheme_filters_rm_dirs ( filters ) ;
return err ;
}
filters_arr [ i ] = filter ;
filters - > nr + + ;
}
return 0 ;
}
2022-12-06 02:08:24 +03:00
static ssize_t nr_filters_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * buf )
{
struct damon_sysfs_scheme_filters * filters = container_of ( kobj ,
struct damon_sysfs_scheme_filters , kobj ) ;
return sysfs_emit ( buf , " %d \n " , filters - > nr ) ;
}
static ssize_t nr_filters_store ( struct kobject * kobj ,
struct kobj_attribute * attr , const char * buf , size_t count )
{
2022-12-06 02:08:26 +03:00
struct damon_sysfs_scheme_filters * filters ;
2022-12-06 02:08:24 +03:00
int nr , err = kstrtoint ( buf , 0 , & nr ) ;
if ( err )
return err ;
if ( nr < 0 )
return - EINVAL ;
2022-12-06 02:08:26 +03:00
filters = container_of ( kobj , struct damon_sysfs_scheme_filters , kobj ) ;
if ( ! mutex_trylock ( & damon_sysfs_lock ) )
return - EBUSY ;
err = damon_sysfs_scheme_filters_add_dirs ( filters , nr ) ;
mutex_unlock ( & damon_sysfs_lock ) ;
if ( err )
return err ;
2022-12-06 02:08:24 +03:00
return count ;
}
static void damon_sysfs_scheme_filters_release ( struct kobject * kobj )
{
kfree ( container_of ( kobj , struct damon_sysfs_scheme_filters , kobj ) ) ;
}
static struct kobj_attribute damon_sysfs_scheme_filters_nr_attr =
__ATTR_RW_MODE ( nr_filters , 0600 ) ;
static struct attribute * damon_sysfs_scheme_filters_attrs [ ] = {
& damon_sysfs_scheme_filters_nr_attr . attr ,
NULL ,
} ;
ATTRIBUTE_GROUPS ( damon_sysfs_scheme_filters ) ;
static struct kobj_type damon_sysfs_scheme_filters_ktype = {
. release = damon_sysfs_scheme_filters_release ,
. sysfs_ops = & kobj_sysfs_ops ,
. default_groups = damon_sysfs_scheme_filters_groups ,
} ;
2022-10-27 01:59:41 +03:00
/*
* watermarks directory
*/
struct damon_sysfs_watermarks {
struct kobject kobj ;
enum damos_wmark_metric metric ;
unsigned long interval_us ;
unsigned long high ;
unsigned long mid ;
unsigned long low ;
} ;
static struct damon_sysfs_watermarks * damon_sysfs_watermarks_alloc (
enum damos_wmark_metric metric , unsigned long interval_us ,
unsigned long high , unsigned long mid , unsigned long low )
{
struct damon_sysfs_watermarks * watermarks = kmalloc (
sizeof ( * watermarks ) , GFP_KERNEL ) ;
if ( ! watermarks )
return NULL ;
watermarks - > kobj = ( struct kobject ) { } ;
watermarks - > metric = metric ;
watermarks - > interval_us = interval_us ;
watermarks - > high = high ;
watermarks - > mid = mid ;
watermarks - > low = low ;
return watermarks ;
}
/* Should match with enum damos_wmark_metric */
static const char * const damon_sysfs_wmark_metric_strs [ ] = {
" none " ,
" free_mem_rate " ,
} ;
static ssize_t metric_show ( struct kobject * kobj , struct kobj_attribute * attr ,
char * buf )
{
struct damon_sysfs_watermarks * watermarks = container_of ( kobj ,
struct damon_sysfs_watermarks , kobj ) ;
return sysfs_emit ( buf , " %s \n " ,
damon_sysfs_wmark_metric_strs [ watermarks - > metric ] ) ;
}
static ssize_t metric_store ( struct kobject * kobj , struct kobj_attribute * attr ,
const char * buf , size_t count )
{
struct damon_sysfs_watermarks * watermarks = container_of ( kobj ,
struct damon_sysfs_watermarks , kobj ) ;
enum damos_wmark_metric metric ;
for ( metric = 0 ; metric < NR_DAMOS_WMARK_METRICS ; metric + + ) {
if ( sysfs_streq ( buf , damon_sysfs_wmark_metric_strs [ metric ] ) ) {
watermarks - > metric = metric ;
return count ;
}
}
return - EINVAL ;
}
static ssize_t interval_us_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * buf )
{
struct damon_sysfs_watermarks * watermarks = container_of ( kobj ,
struct damon_sysfs_watermarks , kobj ) ;
return sysfs_emit ( buf , " %lu \n " , watermarks - > interval_us ) ;
}
static ssize_t interval_us_store ( struct kobject * kobj ,
struct kobj_attribute * attr , const char * buf , size_t count )
{
struct damon_sysfs_watermarks * watermarks = container_of ( kobj ,
struct damon_sysfs_watermarks , kobj ) ;
int err = kstrtoul ( buf , 0 , & watermarks - > interval_us ) ;
return err ? err : count ;
}
static ssize_t high_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * buf )
{
struct damon_sysfs_watermarks * watermarks = container_of ( kobj ,
struct damon_sysfs_watermarks , kobj ) ;
return sysfs_emit ( buf , " %lu \n " , watermarks - > high ) ;
}
static ssize_t high_store ( struct kobject * kobj ,
struct kobj_attribute * attr , const char * buf , size_t count )
{
struct damon_sysfs_watermarks * watermarks = container_of ( kobj ,
struct damon_sysfs_watermarks , kobj ) ;
int err = kstrtoul ( buf , 0 , & watermarks - > high ) ;
return err ? err : count ;
}
static ssize_t mid_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * buf )
{
struct damon_sysfs_watermarks * watermarks = container_of ( kobj ,
struct damon_sysfs_watermarks , kobj ) ;
return sysfs_emit ( buf , " %lu \n " , watermarks - > mid ) ;
}
static ssize_t mid_store ( struct kobject * kobj ,
struct kobj_attribute * attr , const char * buf , size_t count )
{
struct damon_sysfs_watermarks * watermarks = container_of ( kobj ,
struct damon_sysfs_watermarks , kobj ) ;
int err = kstrtoul ( buf , 0 , & watermarks - > mid ) ;
return err ? err : count ;
}
static ssize_t low_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * buf )
{
struct damon_sysfs_watermarks * watermarks = container_of ( kobj ,
struct damon_sysfs_watermarks , kobj ) ;
return sysfs_emit ( buf , " %lu \n " , watermarks - > low ) ;
}
static ssize_t low_store ( struct kobject * kobj ,
struct kobj_attribute * attr , const char * buf , size_t count )
{
struct damon_sysfs_watermarks * watermarks = container_of ( kobj ,
struct damon_sysfs_watermarks , kobj ) ;
int err = kstrtoul ( buf , 0 , & watermarks - > low ) ;
return err ? err : count ;
}
static void damon_sysfs_watermarks_release ( struct kobject * kobj )
{
kfree ( container_of ( kobj , struct damon_sysfs_watermarks , kobj ) ) ;
}
static struct kobj_attribute damon_sysfs_watermarks_metric_attr =
__ATTR_RW_MODE ( metric , 0600 ) ;
static struct kobj_attribute damon_sysfs_watermarks_interval_us_attr =
__ATTR_RW_MODE ( interval_us , 0600 ) ;
static struct kobj_attribute damon_sysfs_watermarks_high_attr =
__ATTR_RW_MODE ( high , 0600 ) ;
static struct kobj_attribute damon_sysfs_watermarks_mid_attr =
__ATTR_RW_MODE ( mid , 0600 ) ;
static struct kobj_attribute damon_sysfs_watermarks_low_attr =
__ATTR_RW_MODE ( low , 0600 ) ;
static struct attribute * damon_sysfs_watermarks_attrs [ ] = {
& damon_sysfs_watermarks_metric_attr . attr ,
& damon_sysfs_watermarks_interval_us_attr . attr ,
& damon_sysfs_watermarks_high_attr . attr ,
& damon_sysfs_watermarks_mid_attr . attr ,
& damon_sysfs_watermarks_low_attr . attr ,
NULL ,
} ;
ATTRIBUTE_GROUPS ( damon_sysfs_watermarks ) ;
static struct kobj_type damon_sysfs_watermarks_ktype = {
. release = damon_sysfs_watermarks_release ,
. sysfs_ops = & kobj_sysfs_ops ,
. default_groups = damon_sysfs_watermarks_groups ,
} ;
/*
* scheme / weights directory
*/
struct damon_sysfs_weights {
struct kobject kobj ;
unsigned int sz ;
unsigned int nr_accesses ;
unsigned int age ;
} ;
static struct damon_sysfs_weights * damon_sysfs_weights_alloc ( unsigned int sz ,
unsigned int nr_accesses , unsigned int age )
{
struct damon_sysfs_weights * weights = kmalloc ( sizeof ( * weights ) ,
GFP_KERNEL ) ;
if ( ! weights )
return NULL ;
weights - > kobj = ( struct kobject ) { } ;
weights - > sz = sz ;
weights - > nr_accesses = nr_accesses ;
weights - > age = age ;
return weights ;
}
static ssize_t sz_permil_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * buf )
{
struct damon_sysfs_weights * weights = container_of ( kobj ,
struct damon_sysfs_weights , kobj ) ;
return sysfs_emit ( buf , " %u \n " , weights - > sz ) ;
}
static ssize_t sz_permil_store ( struct kobject * kobj ,
struct kobj_attribute * attr , const char * buf , size_t count )
{
struct damon_sysfs_weights * weights = container_of ( kobj ,
struct damon_sysfs_weights , kobj ) ;
int err = kstrtouint ( buf , 0 , & weights - > sz ) ;
return err ? err : count ;
}
static ssize_t nr_accesses_permil_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * buf )
{
struct damon_sysfs_weights * weights = container_of ( kobj ,
struct damon_sysfs_weights , kobj ) ;
return sysfs_emit ( buf , " %u \n " , weights - > nr_accesses ) ;
}
static ssize_t nr_accesses_permil_store ( struct kobject * kobj ,
struct kobj_attribute * attr , const char * buf , size_t count )
{
struct damon_sysfs_weights * weights = container_of ( kobj ,
struct damon_sysfs_weights , kobj ) ;
int err = kstrtouint ( buf , 0 , & weights - > nr_accesses ) ;
return err ? err : count ;
}
static ssize_t age_permil_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * buf )
{
struct damon_sysfs_weights * weights = container_of ( kobj ,
struct damon_sysfs_weights , kobj ) ;
return sysfs_emit ( buf , " %u \n " , weights - > age ) ;
}
static ssize_t age_permil_store ( struct kobject * kobj ,
struct kobj_attribute * attr , const char * buf , size_t count )
{
struct damon_sysfs_weights * weights = container_of ( kobj ,
struct damon_sysfs_weights , kobj ) ;
int err = kstrtouint ( buf , 0 , & weights - > age ) ;
return err ? err : count ;
}
static void damon_sysfs_weights_release ( struct kobject * kobj )
{
kfree ( container_of ( kobj , struct damon_sysfs_weights , kobj ) ) ;
}
static struct kobj_attribute damon_sysfs_weights_sz_attr =
__ATTR_RW_MODE ( sz_permil , 0600 ) ;
static struct kobj_attribute damon_sysfs_weights_nr_accesses_attr =
__ATTR_RW_MODE ( nr_accesses_permil , 0600 ) ;
static struct kobj_attribute damon_sysfs_weights_age_attr =
__ATTR_RW_MODE ( age_permil , 0600 ) ;
static struct attribute * damon_sysfs_weights_attrs [ ] = {
& damon_sysfs_weights_sz_attr . attr ,
& damon_sysfs_weights_nr_accesses_attr . attr ,
& damon_sysfs_weights_age_attr . attr ,
NULL ,
} ;
ATTRIBUTE_GROUPS ( damon_sysfs_weights ) ;
static struct kobj_type damon_sysfs_weights_ktype = {
. release = damon_sysfs_weights_release ,
. sysfs_ops = & kobj_sysfs_ops ,
. default_groups = damon_sysfs_weights_groups ,
} ;
/*
* quotas directory
*/
struct damon_sysfs_quotas {
struct kobject kobj ;
struct damon_sysfs_weights * weights ;
unsigned long ms ;
unsigned long sz ;
unsigned long reset_interval_ms ;
} ;
static struct damon_sysfs_quotas * damon_sysfs_quotas_alloc ( void )
{
return kzalloc ( sizeof ( struct damon_sysfs_quotas ) , GFP_KERNEL ) ;
}
static int damon_sysfs_quotas_add_dirs ( struct damon_sysfs_quotas * quotas )
{
struct damon_sysfs_weights * weights ;
int err ;
weights = damon_sysfs_weights_alloc ( 0 , 0 , 0 ) ;
if ( ! weights )
return - ENOMEM ;
err = kobject_init_and_add ( & weights - > kobj , & damon_sysfs_weights_ktype ,
& quotas - > kobj , " weights " ) ;
if ( err )
kobject_put ( & weights - > kobj ) ;
else
quotas - > weights = weights ;
return err ;
}
static void damon_sysfs_quotas_rm_dirs ( struct damon_sysfs_quotas * quotas )
{
kobject_put ( & quotas - > weights - > kobj ) ;
}
static ssize_t ms_show ( struct kobject * kobj , struct kobj_attribute * attr ,
char * buf )
{
struct damon_sysfs_quotas * quotas = container_of ( kobj ,
struct damon_sysfs_quotas , kobj ) ;
return sysfs_emit ( buf , " %lu \n " , quotas - > ms ) ;
}
static ssize_t ms_store ( struct kobject * kobj , struct kobj_attribute * attr ,
const char * buf , size_t count )
{
struct damon_sysfs_quotas * quotas = container_of ( kobj ,
struct damon_sysfs_quotas , kobj ) ;
int err = kstrtoul ( buf , 0 , & quotas - > ms ) ;
if ( err )
return - EINVAL ;
return count ;
}
static ssize_t bytes_show ( struct kobject * kobj , struct kobj_attribute * attr ,
char * buf )
{
struct damon_sysfs_quotas * quotas = container_of ( kobj ,
struct damon_sysfs_quotas , kobj ) ;
return sysfs_emit ( buf , " %lu \n " , quotas - > sz ) ;
}
static ssize_t bytes_store ( struct kobject * kobj ,
struct kobj_attribute * attr , const char * buf , size_t count )
{
struct damon_sysfs_quotas * quotas = container_of ( kobj ,
struct damon_sysfs_quotas , kobj ) ;
int err = kstrtoul ( buf , 0 , & quotas - > sz ) ;
if ( err )
return - EINVAL ;
return count ;
}
static ssize_t reset_interval_ms_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * buf )
{
struct damon_sysfs_quotas * quotas = container_of ( kobj ,
struct damon_sysfs_quotas , kobj ) ;
return sysfs_emit ( buf , " %lu \n " , quotas - > reset_interval_ms ) ;
}
static ssize_t reset_interval_ms_store ( struct kobject * kobj ,
struct kobj_attribute * attr , const char * buf , size_t count )
{
struct damon_sysfs_quotas * quotas = container_of ( kobj ,
struct damon_sysfs_quotas , kobj ) ;
int err = kstrtoul ( buf , 0 , & quotas - > reset_interval_ms ) ;
if ( err )
return - EINVAL ;
return count ;
}
static void damon_sysfs_quotas_release ( struct kobject * kobj )
{
kfree ( container_of ( kobj , struct damon_sysfs_quotas , kobj ) ) ;
}
static struct kobj_attribute damon_sysfs_quotas_ms_attr =
__ATTR_RW_MODE ( ms , 0600 ) ;
static struct kobj_attribute damon_sysfs_quotas_sz_attr =
__ATTR_RW_MODE ( bytes , 0600 ) ;
static struct kobj_attribute damon_sysfs_quotas_reset_interval_ms_attr =
__ATTR_RW_MODE ( reset_interval_ms , 0600 ) ;
static struct attribute * damon_sysfs_quotas_attrs [ ] = {
& damon_sysfs_quotas_ms_attr . attr ,
& damon_sysfs_quotas_sz_attr . attr ,
& damon_sysfs_quotas_reset_interval_ms_attr . attr ,
NULL ,
} ;
ATTRIBUTE_GROUPS ( damon_sysfs_quotas ) ;
static struct kobj_type damon_sysfs_quotas_ktype = {
. release = damon_sysfs_quotas_release ,
. sysfs_ops = & kobj_sysfs_ops ,
. default_groups = damon_sysfs_quotas_groups ,
} ;
/*
* access_pattern directory
*/
struct damon_sysfs_access_pattern {
struct kobject kobj ;
struct damon_sysfs_ul_range * sz ;
struct damon_sysfs_ul_range * nr_accesses ;
struct damon_sysfs_ul_range * age ;
} ;
static
struct damon_sysfs_access_pattern * damon_sysfs_access_pattern_alloc ( void )
{
struct damon_sysfs_access_pattern * access_pattern =
kmalloc ( sizeof ( * access_pattern ) , GFP_KERNEL ) ;
if ( ! access_pattern )
return NULL ;
access_pattern - > kobj = ( struct kobject ) { } ;
return access_pattern ;
}
static int damon_sysfs_access_pattern_add_range_dir (
struct damon_sysfs_access_pattern * access_pattern ,
struct damon_sysfs_ul_range * * range_dir_ptr ,
char * name )
{
struct damon_sysfs_ul_range * range = damon_sysfs_ul_range_alloc ( 0 , 0 ) ;
int err ;
if ( ! range )
return - ENOMEM ;
err = kobject_init_and_add ( & range - > kobj , & damon_sysfs_ul_range_ktype ,
& access_pattern - > kobj , name ) ;
if ( err )
kobject_put ( & range - > kobj ) ;
else
* range_dir_ptr = range ;
return err ;
}
static int damon_sysfs_access_pattern_add_dirs (
struct damon_sysfs_access_pattern * access_pattern )
{
int err ;
err = damon_sysfs_access_pattern_add_range_dir ( access_pattern ,
& access_pattern - > sz , " sz " ) ;
if ( err )
goto put_sz_out ;
err = damon_sysfs_access_pattern_add_range_dir ( access_pattern ,
& access_pattern - > nr_accesses , " nr_accesses " ) ;
if ( err )
goto put_nr_accesses_sz_out ;
err = damon_sysfs_access_pattern_add_range_dir ( access_pattern ,
& access_pattern - > age , " age " ) ;
if ( err )
goto put_age_nr_accesses_sz_out ;
return 0 ;
put_age_nr_accesses_sz_out :
kobject_put ( & access_pattern - > age - > kobj ) ;
access_pattern - > age = NULL ;
put_nr_accesses_sz_out :
kobject_put ( & access_pattern - > nr_accesses - > kobj ) ;
access_pattern - > nr_accesses = NULL ;
put_sz_out :
kobject_put ( & access_pattern - > sz - > kobj ) ;
access_pattern - > sz = NULL ;
return err ;
}
static void damon_sysfs_access_pattern_rm_dirs (
struct damon_sysfs_access_pattern * access_pattern )
{
kobject_put ( & access_pattern - > sz - > kobj ) ;
kobject_put ( & access_pattern - > nr_accesses - > kobj ) ;
kobject_put ( & access_pattern - > age - > kobj ) ;
}
static void damon_sysfs_access_pattern_release ( struct kobject * kobj )
{
kfree ( container_of ( kobj , struct damon_sysfs_access_pattern , kobj ) ) ;
}
static struct attribute * damon_sysfs_access_pattern_attrs [ ] = {
NULL ,
} ;
ATTRIBUTE_GROUPS ( damon_sysfs_access_pattern ) ;
static struct kobj_type damon_sysfs_access_pattern_ktype = {
. release = damon_sysfs_access_pattern_release ,
. sysfs_ops = & kobj_sysfs_ops ,
. default_groups = damon_sysfs_access_pattern_groups ,
} ;
/*
* scheme directory
*/
struct damon_sysfs_scheme {
struct kobject kobj ;
enum damos_action action ;
struct damon_sysfs_access_pattern * access_pattern ;
struct damon_sysfs_quotas * quotas ;
struct damon_sysfs_watermarks * watermarks ;
2022-12-06 02:08:24 +03:00
struct damon_sysfs_scheme_filters * filters ;
2022-10-27 01:59:41 +03:00
struct damon_sysfs_stats * stats ;
2022-11-02 01:03:22 +03:00
struct damon_sysfs_scheme_regions * tried_regions ;
2022-10-27 01:59:41 +03:00
} ;
/* This should match with enum damos_action */
static const char * const damon_sysfs_damos_action_strs [ ] = {
" willneed " ,
" cold " ,
" pageout " ,
" hugepage " ,
" nohugepage " ,
" lru_prio " ,
" lru_deprio " ,
" stat " ,
} ;
static struct damon_sysfs_scheme * damon_sysfs_scheme_alloc (
enum damos_action action )
{
struct damon_sysfs_scheme * scheme = kmalloc ( sizeof ( * scheme ) ,
GFP_KERNEL ) ;
if ( ! scheme )
return NULL ;
scheme - > kobj = ( struct kobject ) { } ;
scheme - > action = action ;
return scheme ;
}
static int damon_sysfs_scheme_set_access_pattern (
struct damon_sysfs_scheme * scheme )
{
struct damon_sysfs_access_pattern * access_pattern ;
int err ;
access_pattern = damon_sysfs_access_pattern_alloc ( ) ;
if ( ! access_pattern )
return - ENOMEM ;
err = kobject_init_and_add ( & access_pattern - > kobj ,
& damon_sysfs_access_pattern_ktype , & scheme - > kobj ,
" access_pattern " ) ;
if ( err )
goto out ;
err = damon_sysfs_access_pattern_add_dirs ( access_pattern ) ;
if ( err )
goto out ;
scheme - > access_pattern = access_pattern ;
return 0 ;
out :
kobject_put ( & access_pattern - > kobj ) ;
return err ;
}
static int damon_sysfs_scheme_set_quotas ( struct damon_sysfs_scheme * scheme )
{
struct damon_sysfs_quotas * quotas = damon_sysfs_quotas_alloc ( ) ;
int err ;
if ( ! quotas )
return - ENOMEM ;
err = kobject_init_and_add ( & quotas - > kobj , & damon_sysfs_quotas_ktype ,
& scheme - > kobj , " quotas " ) ;
if ( err )
goto out ;
err = damon_sysfs_quotas_add_dirs ( quotas ) ;
if ( err )
goto out ;
scheme - > quotas = quotas ;
return 0 ;
out :
kobject_put ( & quotas - > kobj ) ;
return err ;
}
static int damon_sysfs_scheme_set_watermarks ( struct damon_sysfs_scheme * scheme )
{
struct damon_sysfs_watermarks * watermarks =
damon_sysfs_watermarks_alloc ( DAMOS_WMARK_NONE , 0 , 0 , 0 , 0 ) ;
int err ;
if ( ! watermarks )
return - ENOMEM ;
err = kobject_init_and_add ( & watermarks - > kobj ,
& damon_sysfs_watermarks_ktype , & scheme - > kobj ,
" watermarks " ) ;
if ( err )
kobject_put ( & watermarks - > kobj ) ;
else
scheme - > watermarks = watermarks ;
return err ;
}
2022-12-06 02:08:24 +03:00
static int damon_sysfs_scheme_set_filters ( struct damon_sysfs_scheme * scheme )
{
struct damon_sysfs_scheme_filters * filters =
damon_sysfs_scheme_filters_alloc ( ) ;
int err ;
if ( ! filters )
return - ENOMEM ;
err = kobject_init_and_add ( & filters - > kobj ,
& damon_sysfs_scheme_filters_ktype , & scheme - > kobj ,
" filters " ) ;
if ( err )
kobject_put ( & filters - > kobj ) ;
else
scheme - > filters = filters ;
return err ;
}
2022-10-27 01:59:41 +03:00
static int damon_sysfs_scheme_set_stats ( struct damon_sysfs_scheme * scheme )
{
struct damon_sysfs_stats * stats = damon_sysfs_stats_alloc ( ) ;
int err ;
if ( ! stats )
return - ENOMEM ;
err = kobject_init_and_add ( & stats - > kobj , & damon_sysfs_stats_ktype ,
& scheme - > kobj , " stats " ) ;
if ( err )
kobject_put ( & stats - > kobj ) ;
else
scheme - > stats = stats ;
return err ;
}
2022-11-02 01:03:22 +03:00
static int damon_sysfs_scheme_set_tried_regions (
struct damon_sysfs_scheme * scheme )
{
struct damon_sysfs_scheme_regions * tried_regions =
damon_sysfs_scheme_regions_alloc ( ) ;
int err ;
if ( ! tried_regions )
return - ENOMEM ;
err = kobject_init_and_add ( & tried_regions - > kobj ,
& damon_sysfs_scheme_regions_ktype , & scheme - > kobj ,
" tried_regions " ) ;
if ( err )
kobject_put ( & tried_regions - > kobj ) ;
else
scheme - > tried_regions = tried_regions ;
return err ;
}
2022-10-27 01:59:41 +03:00
static int damon_sysfs_scheme_add_dirs ( struct damon_sysfs_scheme * scheme )
{
int err ;
err = damon_sysfs_scheme_set_access_pattern ( scheme ) ;
if ( err )
return err ;
err = damon_sysfs_scheme_set_quotas ( scheme ) ;
if ( err )
goto put_access_pattern_out ;
err = damon_sysfs_scheme_set_watermarks ( scheme ) ;
if ( err )
goto put_quotas_access_pattern_out ;
2022-12-06 02:08:24 +03:00
err = damon_sysfs_scheme_set_filters ( scheme ) ;
2022-10-27 01:59:41 +03:00
if ( err )
goto put_watermarks_quotas_access_pattern_out ;
2022-12-06 02:08:24 +03:00
err = damon_sysfs_scheme_set_stats ( scheme ) ;
if ( err )
goto put_filters_watermarks_quotas_access_pattern_out ;
2022-11-02 01:03:22 +03:00
err = damon_sysfs_scheme_set_tried_regions ( scheme ) ;
if ( err )
goto put_tried_regions_out ;
2022-10-27 01:59:41 +03:00
return 0 ;
2022-11-02 01:03:22 +03:00
put_tried_regions_out :
kobject_put ( & scheme - > tried_regions - > kobj ) ;
scheme - > tried_regions = NULL ;
2022-12-06 02:08:24 +03:00
put_filters_watermarks_quotas_access_pattern_out :
kobject_put ( & scheme - > filters - > kobj ) ;
scheme - > filters = NULL ;
2022-10-27 01:59:41 +03:00
put_watermarks_quotas_access_pattern_out :
kobject_put ( & scheme - > watermarks - > kobj ) ;
scheme - > watermarks = NULL ;
put_quotas_access_pattern_out :
kobject_put ( & scheme - > quotas - > kobj ) ;
scheme - > quotas = NULL ;
put_access_pattern_out :
kobject_put ( & scheme - > access_pattern - > kobj ) ;
scheme - > access_pattern = NULL ;
return err ;
}
static void damon_sysfs_scheme_rm_dirs ( struct damon_sysfs_scheme * scheme )
{
damon_sysfs_access_pattern_rm_dirs ( scheme - > access_pattern ) ;
kobject_put ( & scheme - > access_pattern - > kobj ) ;
damon_sysfs_quotas_rm_dirs ( scheme - > quotas ) ;
kobject_put ( & scheme - > quotas - > kobj ) ;
kobject_put ( & scheme - > watermarks - > kobj ) ;
2022-12-06 02:08:26 +03:00
damon_sysfs_scheme_filters_rm_dirs ( scheme - > filters ) ;
2022-12-06 02:08:24 +03:00
kobject_put ( & scheme - > filters - > kobj ) ;
2022-10-27 01:59:41 +03:00
kobject_put ( & scheme - > stats - > kobj ) ;
2022-11-02 01:03:23 +03:00
damon_sysfs_scheme_regions_rm_dirs ( scheme - > tried_regions ) ;
2022-11-02 01:03:22 +03:00
kobject_put ( & scheme - > tried_regions - > kobj ) ;
2022-10-27 01:59:41 +03:00
}
static ssize_t action_show ( struct kobject * kobj , struct kobj_attribute * attr ,
char * buf )
{
struct damon_sysfs_scheme * scheme = container_of ( kobj ,
struct damon_sysfs_scheme , kobj ) ;
return sysfs_emit ( buf , " %s \n " ,
damon_sysfs_damos_action_strs [ scheme - > action ] ) ;
}
static ssize_t action_store ( struct kobject * kobj , struct kobj_attribute * attr ,
const char * buf , size_t count )
{
struct damon_sysfs_scheme * scheme = container_of ( kobj ,
struct damon_sysfs_scheme , kobj ) ;
enum damos_action action ;
for ( action = 0 ; action < NR_DAMOS_ACTIONS ; action + + ) {
if ( sysfs_streq ( buf , damon_sysfs_damos_action_strs [ action ] ) ) {
scheme - > action = action ;
return count ;
}
}
return - EINVAL ;
}
static void damon_sysfs_scheme_release ( struct kobject * kobj )
{
kfree ( container_of ( kobj , struct damon_sysfs_scheme , kobj ) ) ;
}
static struct kobj_attribute damon_sysfs_scheme_action_attr =
__ATTR_RW_MODE ( action , 0600 ) ;
static struct attribute * damon_sysfs_scheme_attrs [ ] = {
& damon_sysfs_scheme_action_attr . attr ,
NULL ,
} ;
ATTRIBUTE_GROUPS ( damon_sysfs_scheme ) ;
static struct kobj_type damon_sysfs_scheme_ktype = {
. release = damon_sysfs_scheme_release ,
. sysfs_ops = & kobj_sysfs_ops ,
. default_groups = damon_sysfs_scheme_groups ,
} ;
/*
* schemes directory
*/
struct damon_sysfs_schemes * damon_sysfs_schemes_alloc ( void )
{
return kzalloc ( sizeof ( struct damon_sysfs_schemes ) , GFP_KERNEL ) ;
}
void damon_sysfs_schemes_rm_dirs ( struct damon_sysfs_schemes * schemes )
{
struct damon_sysfs_scheme * * schemes_arr = schemes - > schemes_arr ;
int i ;
for ( i = 0 ; i < schemes - > nr ; i + + ) {
damon_sysfs_scheme_rm_dirs ( schemes_arr [ i ] ) ;
kobject_put ( & schemes_arr [ i ] - > kobj ) ;
}
schemes - > nr = 0 ;
kfree ( schemes_arr ) ;
schemes - > schemes_arr = NULL ;
}
static int damon_sysfs_schemes_add_dirs ( struct damon_sysfs_schemes * schemes ,
int nr_schemes )
{
struct damon_sysfs_scheme * * schemes_arr , * scheme ;
int err , i ;
damon_sysfs_schemes_rm_dirs ( schemes ) ;
if ( ! nr_schemes )
return 0 ;
schemes_arr = kmalloc_array ( nr_schemes , sizeof ( * schemes_arr ) ,
GFP_KERNEL | __GFP_NOWARN ) ;
if ( ! schemes_arr )
return - ENOMEM ;
schemes - > schemes_arr = schemes_arr ;
for ( i = 0 ; i < nr_schemes ; i + + ) {
scheme = damon_sysfs_scheme_alloc ( DAMOS_STAT ) ;
if ( ! scheme ) {
damon_sysfs_schemes_rm_dirs ( schemes ) ;
return - ENOMEM ;
}
err = kobject_init_and_add ( & scheme - > kobj ,
& damon_sysfs_scheme_ktype , & schemes - > kobj ,
" %d " , i ) ;
if ( err )
goto out ;
err = damon_sysfs_scheme_add_dirs ( scheme ) ;
if ( err )
goto out ;
schemes_arr [ i ] = scheme ;
schemes - > nr + + ;
}
return 0 ;
out :
damon_sysfs_schemes_rm_dirs ( schemes ) ;
kobject_put ( & scheme - > kobj ) ;
return err ;
}
static ssize_t nr_schemes_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * buf )
{
struct damon_sysfs_schemes * schemes = container_of ( kobj ,
struct damon_sysfs_schemes , kobj ) ;
return sysfs_emit ( buf , " %d \n " , schemes - > nr ) ;
}
static ssize_t nr_schemes_store ( struct kobject * kobj ,
struct kobj_attribute * attr , const char * buf , size_t count )
{
struct damon_sysfs_schemes * schemes ;
int nr , err = kstrtoint ( buf , 0 , & nr ) ;
if ( err )
return err ;
if ( nr < 0 )
return - EINVAL ;
schemes = container_of ( kobj , struct damon_sysfs_schemes , kobj ) ;
if ( ! mutex_trylock ( & damon_sysfs_lock ) )
return - EBUSY ;
err = damon_sysfs_schemes_add_dirs ( schemes , nr ) ;
mutex_unlock ( & damon_sysfs_lock ) ;
if ( err )
return err ;
return count ;
}
static void damon_sysfs_schemes_release ( struct kobject * kobj )
{
kfree ( container_of ( kobj , struct damon_sysfs_schemes , kobj ) ) ;
}
static struct kobj_attribute damon_sysfs_schemes_nr_attr =
__ATTR_RW_MODE ( nr_schemes , 0600 ) ;
static struct attribute * damon_sysfs_schemes_attrs [ ] = {
& damon_sysfs_schemes_nr_attr . attr ,
NULL ,
} ;
ATTRIBUTE_GROUPS ( damon_sysfs_schemes ) ;
struct kobj_type damon_sysfs_schemes_ktype = {
. release = damon_sysfs_schemes_release ,
. sysfs_ops = & kobj_sysfs_ops ,
. default_groups = damon_sysfs_schemes_groups ,
} ;
2022-12-06 02:08:27 +03:00
static bool damon_sysfs_memcg_path_eq ( struct mem_cgroup * memcg ,
char * memcg_path_buf , char * path )
{
# ifdef CONFIG_MEMCG
cgroup_path ( memcg - > css . cgroup , memcg_path_buf , PATH_MAX ) ;
if ( sysfs_streq ( memcg_path_buf , path ) )
return true ;
# endif /* CONFIG_MEMCG */
return false ;
}
static int damon_sysfs_memcg_path_to_id ( char * memcg_path , unsigned short * id )
{
struct mem_cgroup * memcg ;
char * path ;
bool found = false ;
if ( ! memcg_path )
return - EINVAL ;
path = kmalloc ( sizeof ( * path ) * PATH_MAX , GFP_KERNEL ) ;
if ( ! path )
return - ENOMEM ;
for ( memcg = mem_cgroup_iter ( NULL , NULL , NULL ) ; memcg ;
memcg = mem_cgroup_iter ( NULL , memcg , NULL ) ) {
/* skip removed memcg */
if ( ! mem_cgroup_id ( memcg ) )
continue ;
if ( damon_sysfs_memcg_path_eq ( memcg , path , memcg_path ) ) {
* id = mem_cgroup_id ( memcg ) ;
found = true ;
break ;
}
}
kfree ( path ) ;
return found ? 0 : - EINVAL ;
}
static int damon_sysfs_set_scheme_filters ( struct damos * scheme ,
struct damon_sysfs_scheme_filters * sysfs_filters )
{
int i ;
struct damos_filter * filter , * next ;
damos_for_each_filter_safe ( filter , next , scheme )
damos_destroy_filter ( filter ) ;
for ( i = 0 ; i < sysfs_filters - > nr ; i + + ) {
struct damon_sysfs_scheme_filter * sysfs_filter =
sysfs_filters - > filters_arr [ i ] ;
struct damos_filter * filter =
damos_new_filter ( sysfs_filter - > type ,
sysfs_filter - > matching ) ;
int err ;
if ( ! filter )
return - ENOMEM ;
if ( filter - > type = = DAMOS_FILTER_TYPE_MEMCG ) {
err = damon_sysfs_memcg_path_to_id (
sysfs_filter - > memcg_path ,
& filter - > memcg_id ) ;
if ( err ) {
damos_destroy_filter ( filter ) ;
return err ;
}
}
damos_add_filter ( scheme , filter ) ;
}
return 0 ;
}
2022-10-27 01:59:41 +03:00
static struct damos * damon_sysfs_mk_scheme (
struct damon_sysfs_scheme * sysfs_scheme )
{
struct damon_sysfs_access_pattern * access_pattern =
sysfs_scheme - > access_pattern ;
struct damon_sysfs_quotas * sysfs_quotas = sysfs_scheme - > quotas ;
struct damon_sysfs_weights * sysfs_weights = sysfs_quotas - > weights ;
struct damon_sysfs_watermarks * sysfs_wmarks = sysfs_scheme - > watermarks ;
2022-12-06 02:08:27 +03:00
struct damon_sysfs_scheme_filters * sysfs_filters =
sysfs_scheme - > filters ;
struct damos * scheme ;
int err ;
2022-10-27 01:59:41 +03:00
struct damos_access_pattern pattern = {
. min_sz_region = access_pattern - > sz - > min ,
. max_sz_region = access_pattern - > sz - > max ,
. min_nr_accesses = access_pattern - > nr_accesses - > min ,
. max_nr_accesses = access_pattern - > nr_accesses - > max ,
. min_age_region = access_pattern - > age - > min ,
. max_age_region = access_pattern - > age - > max ,
} ;
struct damos_quota quota = {
. ms = sysfs_quotas - > ms ,
. sz = sysfs_quotas - > sz ,
. reset_interval = sysfs_quotas - > reset_interval_ms ,
. weight_sz = sysfs_weights - > sz ,
. weight_nr_accesses = sysfs_weights - > nr_accesses ,
. weight_age = sysfs_weights - > age ,
} ;
struct damos_watermarks wmarks = {
. metric = sysfs_wmarks - > metric ,
. interval = sysfs_wmarks - > interval_us ,
. high = sysfs_wmarks - > high ,
. mid = sysfs_wmarks - > mid ,
. low = sysfs_wmarks - > low ,
} ;
2022-12-06 02:08:27 +03:00
scheme = damon_new_scheme ( & pattern , sysfs_scheme - > action , & quota ,
2022-10-27 01:59:41 +03:00
& wmarks ) ;
2022-12-06 02:08:27 +03:00
if ( ! scheme )
return NULL ;
err = damon_sysfs_set_scheme_filters ( scheme , sysfs_filters ) ;
if ( err ) {
damon_destroy_scheme ( scheme ) ;
return NULL ;
}
return scheme ;
2022-10-27 01:59:41 +03:00
}
static void damon_sysfs_update_scheme ( struct damos * scheme ,
struct damon_sysfs_scheme * sysfs_scheme )
{
struct damon_sysfs_access_pattern * access_pattern =
sysfs_scheme - > access_pattern ;
struct damon_sysfs_quotas * sysfs_quotas = sysfs_scheme - > quotas ;
struct damon_sysfs_weights * sysfs_weights = sysfs_quotas - > weights ;
struct damon_sysfs_watermarks * sysfs_wmarks = sysfs_scheme - > watermarks ;
2022-12-06 02:08:27 +03:00
int err ;
2022-10-27 01:59:41 +03:00
scheme - > pattern . min_sz_region = access_pattern - > sz - > min ;
scheme - > pattern . max_sz_region = access_pattern - > sz - > max ;
scheme - > pattern . min_nr_accesses = access_pattern - > nr_accesses - > min ;
scheme - > pattern . max_nr_accesses = access_pattern - > nr_accesses - > max ;
scheme - > pattern . min_age_region = access_pattern - > age - > min ;
scheme - > pattern . max_age_region = access_pattern - > age - > max ;
scheme - > action = sysfs_scheme - > action ;
scheme - > quota . ms = sysfs_quotas - > ms ;
scheme - > quota . sz = sysfs_quotas - > sz ;
scheme - > quota . reset_interval = sysfs_quotas - > reset_interval_ms ;
scheme - > quota . weight_sz = sysfs_weights - > sz ;
scheme - > quota . weight_nr_accesses = sysfs_weights - > nr_accesses ;
scheme - > quota . weight_age = sysfs_weights - > age ;
scheme - > wmarks . metric = sysfs_wmarks - > metric ;
scheme - > wmarks . interval = sysfs_wmarks - > interval_us ;
scheme - > wmarks . high = sysfs_wmarks - > high ;
scheme - > wmarks . mid = sysfs_wmarks - > mid ;
scheme - > wmarks . low = sysfs_wmarks - > low ;
2022-12-06 02:08:27 +03:00
err = damon_sysfs_set_scheme_filters ( scheme , sysfs_scheme - > filters ) ;
if ( err )
damon_destroy_scheme ( scheme ) ;
2022-10-27 01:59:41 +03:00
}
int damon_sysfs_set_schemes ( struct damon_ctx * ctx ,
struct damon_sysfs_schemes * sysfs_schemes )
{
struct damos * scheme , * next ;
int i = 0 ;
damon_for_each_scheme_safe ( scheme , next , ctx ) {
if ( i < sysfs_schemes - > nr )
damon_sysfs_update_scheme ( scheme ,
sysfs_schemes - > schemes_arr [ i ] ) ;
else
damon_destroy_scheme ( scheme ) ;
i + + ;
}
for ( ; i < sysfs_schemes - > nr ; i + + ) {
struct damos * scheme , * next ;
scheme = damon_sysfs_mk_scheme ( sysfs_schemes - > schemes_arr [ i ] ) ;
if ( ! scheme ) {
damon_for_each_scheme_safe ( scheme , next , ctx )
damon_destroy_scheme ( scheme ) ;
return - ENOMEM ;
}
damon_add_scheme ( ctx , scheme ) ;
}
return 0 ;
}
void damon_sysfs_schemes_update_stats (
struct damon_sysfs_schemes * sysfs_schemes ,
struct damon_ctx * ctx )
{
struct damos * scheme ;
int schemes_idx = 0 ;
damon_for_each_scheme ( scheme , ctx ) {
struct damon_sysfs_stats * sysfs_stats ;
/* user could have removed the scheme sysfs dir */
if ( schemes_idx > = sysfs_schemes - > nr )
break ;
sysfs_stats = sysfs_schemes - > schemes_arr [ schemes_idx + + ] - > stats ;
sysfs_stats - > nr_tried = scheme - > stat . nr_tried ;
sysfs_stats - > sz_tried = scheme - > stat . sz_tried ;
sysfs_stats - > nr_applied = scheme - > stat . nr_applied ;
sysfs_stats - > sz_applied = scheme - > stat . sz_applied ;
sysfs_stats - > qt_exceeds = scheme - > stat . qt_exceeds ;
}
}
2022-11-02 01:03:24 +03:00
/*
* damon_sysfs_schemes that need to update its schemes regions dir . Protected
* by damon_sysfs_lock
*/
static struct damon_sysfs_schemes * damon_sysfs_schemes_for_damos_callback ;
static int damon_sysfs_schemes_region_idx ;
/*
* DAMON callback that called before damos apply . While this callback is
* registered , damon_sysfs_lock should be held to ensure the regions
* directories exist .
*/
static int damon_sysfs_before_damos_apply ( struct damon_ctx * ctx ,
struct damon_target * t , struct damon_region * r ,
struct damos * s )
{
struct damos * scheme ;
struct damon_sysfs_scheme_regions * sysfs_regions ;
struct damon_sysfs_scheme_region * region ;
struct damon_sysfs_schemes * sysfs_schemes =
damon_sysfs_schemes_for_damos_callback ;
int schemes_idx = 0 ;
damon_for_each_scheme ( scheme , ctx ) {
if ( scheme = = s )
break ;
schemes_idx + + ;
}
/* user could have removed the scheme sysfs dir */
if ( schemes_idx > = sysfs_schemes - > nr )
return 0 ;
sysfs_regions = sysfs_schemes - > schemes_arr [ schemes_idx ] - > tried_regions ;
region = damon_sysfs_scheme_region_alloc ( r ) ;
list_add_tail ( & region - > list , & sysfs_regions - > regions_list ) ;
sysfs_regions - > nr_regions + + ;
if ( kobject_init_and_add ( & region - > kobj ,
& damon_sysfs_scheme_region_ktype ,
& sysfs_regions - > kobj , " %d " ,
damon_sysfs_schemes_region_idx + + ) ) {
kobject_put ( & region - > kobj ) ;
}
return 0 ;
}
/* Called from damon_sysfs_cmd_request_callback under damon_sysfs_lock */
2022-11-02 01:03:25 +03:00
int damon_sysfs_schemes_clear_regions (
2022-11-02 01:03:24 +03:00
struct damon_sysfs_schemes * sysfs_schemes ,
struct damon_ctx * ctx )
{
struct damos * scheme ;
int schemes_idx = 0 ;
damon_for_each_scheme ( scheme , ctx ) {
struct damon_sysfs_scheme * sysfs_scheme ;
2022-11-02 01:03:25 +03:00
/* user could have removed the scheme sysfs dir */
if ( schemes_idx > = sysfs_schemes - > nr )
break ;
2022-11-02 01:03:24 +03:00
sysfs_scheme = sysfs_schemes - > schemes_arr [ schemes_idx + + ] ;
damon_sysfs_scheme_regions_rm_dirs (
sysfs_scheme - > tried_regions ) ;
}
2022-11-02 01:03:25 +03:00
return 0 ;
}
2022-11-02 01:03:24 +03:00
2022-11-02 01:03:25 +03:00
/* Called from damon_sysfs_cmd_request_callback under damon_sysfs_lock */
int damon_sysfs_schemes_update_regions_start (
struct damon_sysfs_schemes * sysfs_schemes ,
struct damon_ctx * ctx )
{
damon_sysfs_schemes_clear_regions ( sysfs_schemes , ctx ) ;
2022-11-02 01:03:24 +03:00
damon_sysfs_schemes_for_damos_callback = sysfs_schemes ;
ctx - > callback . before_damos_apply = damon_sysfs_before_damos_apply ;
return 0 ;
}
/*
* Called from damon_sysfs_cmd_request_callback under damon_sysfs_lock . Caller
* should unlock damon_sysfs_lock which held before
* damon_sysfs_schemes_update_regions_start ( )
*/
int damon_sysfs_schemes_update_regions_stop ( struct damon_ctx * ctx )
{
damon_sysfs_schemes_for_damos_callback = NULL ;
ctx - > callback . before_damos_apply = NULL ;
damon_sysfs_schemes_region_idx = 0 ;
return 0 ;
}