2016-10-28 15:04:42 -07:00
/*
* User interface for Resource Alloction in Resource Director Technology ( RDT )
*
* Copyright ( C ) 2016 Intel Corporation
*
* Author : Fenghua Yu < fenghua . yu @ intel . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* More information about RDT be found in the Intel ( R ) x86 Architecture
* Software Developer Manual .
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2016-10-28 15:04:45 -07:00
# include <linux/cpu.h>
2016-10-28 15:04:42 -07:00
# include <linux/fs.h>
# include <linux/sysfs.h>
# include <linux/kernfs.h>
2016-10-28 15:04:43 -07:00
# include <linux/seq_file.h>
2017-02-08 18:51:30 +01:00
# include <linux/sched/signal.h>
2017-02-08 18:51:36 +01:00
# include <linux/sched/task.h>
2016-10-28 15:04:42 -07:00
# include <linux/slab.h>
2016-10-28 15:04:46 -07:00
# include <linux/task_work.h>
2016-10-28 15:04:42 -07:00
# include <uapi/linux/magic.h>
2017-07-25 14:14:23 -07:00
# include <asm/intel_rdt_sched.h>
# include "intel_rdt.h"
2016-10-28 15:04:42 -07:00
2017-07-25 14:14:25 -07:00
DEFINE_STATIC_KEY_FALSE ( rdt_alloc_enable_key ) ;
2017-07-25 14:14:24 -07:00
static struct kernfs_root * rdt_root ;
2016-10-28 15:04:42 -07:00
struct rdtgroup rdtgroup_default ;
LIST_HEAD ( rdt_all_groups ) ;
2016-10-28 15:04:43 -07:00
/* Kernel fs node for "info" directory under root */
static struct kernfs_node * kn_info ;
2016-10-28 15:04:44 -07:00
/*
* Trivial allocator for CLOSIDs . Since h / w only supports a small number ,
* we can keep a bitmap of free CLOSIDs in a single integer .
*
* Using a global CLOSID across all resources has some advantages and
* some drawbacks :
* + We can simply set " current->closid " to assign a task to a resource
* group .
* + Context switch code can avoid extra memory references deciding which
* CLOSID to load into the PQR_ASSOC MSR
* - We give up some options in configuring resource groups across multi - socket
* systems .
* - Our choices on how to configure each resource become progressively more
* limited as the number of resources grows .
*/
static int closid_free_map ;
static void closid_init ( void )
{
struct rdt_resource * r ;
int rdt_min_closid = 32 ;
/* Compute rdt_min_closid across all resources */
2017-07-25 14:14:25 -07:00
for_each_alloc_enabled_rdt_resource ( r )
2016-10-28 15:04:44 -07:00
rdt_min_closid = min ( rdt_min_closid , r - > num_closid ) ;
closid_free_map = BIT_MASK ( rdt_min_closid ) - 1 ;
/* CLOSID 0 is always reserved for the default group */
closid_free_map & = ~ 1 ;
}
2017-07-25 14:14:24 -07:00
static int closid_alloc ( void )
2016-10-28 15:04:44 -07:00
{
int closid = ffs ( closid_free_map ) ;
if ( closid = = 0 )
return - ENOSPC ;
closid - - ;
closid_free_map & = ~ ( 1 < < closid ) ;
return closid ;
}
static void closid_free ( int closid )
{
closid_free_map | = 1 < < closid ;
}
2016-10-28 15:04:43 -07:00
/* set uid and gid of rdtgroup dirs and files to that of the creator */
static int rdtgroup_kn_set_ugid ( struct kernfs_node * kn )
{
struct iattr iattr = { . ia_valid = ATTR_UID | ATTR_GID ,
. ia_uid = current_fsuid ( ) ,
. ia_gid = current_fsgid ( ) , } ;
if ( uid_eq ( iattr . ia_uid , GLOBAL_ROOT_UID ) & &
gid_eq ( iattr . ia_gid , GLOBAL_ROOT_GID ) )
return 0 ;
return kernfs_setattr ( kn , & iattr ) ;
}
static int rdtgroup_add_file ( struct kernfs_node * parent_kn , struct rftype * rft )
{
struct kernfs_node * kn ;
int ret ;
kn = __kernfs_create_file ( parent_kn , rft - > name , rft - > mode ,
0 , rft - > kf_ops , rft , NULL , NULL ) ;
if ( IS_ERR ( kn ) )
return PTR_ERR ( kn ) ;
ret = rdtgroup_kn_set_ugid ( kn ) ;
if ( ret ) {
kernfs_remove ( kn ) ;
return ret ;
}
return 0 ;
}
static int rdtgroup_seqfile_show ( struct seq_file * m , void * arg )
{
struct kernfs_open_file * of = m - > private ;
struct rftype * rft = of - > kn - > priv ;
if ( rft - > seq_show )
return rft - > seq_show ( of , m , arg ) ;
return 0 ;
}
static ssize_t rdtgroup_file_write ( struct kernfs_open_file * of , char * buf ,
size_t nbytes , loff_t off )
{
struct rftype * rft = of - > kn - > priv ;
if ( rft - > write )
return rft - > write ( of , buf , nbytes , off ) ;
return - EINVAL ;
}
static struct kernfs_ops rdtgroup_kf_single_ops = {
. atomic_write_len = PAGE_SIZE ,
. write = rdtgroup_file_write ,
. seq_show = rdtgroup_seqfile_show ,
} ;
2017-04-10 16:52:32 +02:00
static bool is_cpu_list ( struct kernfs_open_file * of )
{
struct rftype * rft = of - > kn - > priv ;
return rft - > flags & RFTYPE_FLAGS_CPUS_LIST ;
}
2016-10-28 15:04:45 -07:00
static int rdtgroup_cpus_show ( struct kernfs_open_file * of ,
struct seq_file * s , void * v )
{
struct rdtgroup * rdtgrp ;
int ret = 0 ;
rdtgrp = rdtgroup_kn_lock_live ( of - > kn ) ;
2017-04-10 16:52:32 +02:00
if ( rdtgrp ) {
seq_printf ( s , is_cpu_list ( of ) ? " %*pbl \n " : " %*pb \n " ,
cpumask_pr_args ( & rdtgrp - > cpu_mask ) ) ;
} else {
2016-10-28 15:04:45 -07:00
ret = - ENOENT ;
2017-04-10 16:52:32 +02:00
}
2016-10-28 15:04:45 -07:00
rdtgroup_kn_unlock ( of - > kn ) ;
return ret ;
}
2016-11-11 17:02:38 -08:00
/*
* This is safe against intel_rdt_sched_in ( ) called from __switch_to ( )
* because __switch_to ( ) is executed with interrupts disabled . A local call
2016-11-18 15:18:04 -08:00
* from rdt_update_closid ( ) is proteced against __switch_to ( ) because
2016-11-11 17:02:38 -08:00
* preemption is disabled .
*/
2016-11-18 15:18:04 -08:00
static void rdt_update_cpu_closid ( void * closid )
2016-11-11 17:02:38 -08:00
{
2016-11-18 15:18:04 -08:00
if ( closid )
this_cpu_write ( cpu_closid , * ( int * ) closid ) ;
2016-11-11 17:02:38 -08:00
/*
* We cannot unconditionally write the MSR because the current
* executing task might have its own closid selected . Just reuse
* the context switch code .
*/
intel_rdt_sched_in ( ) ;
}
2016-11-18 15:18:04 -08:00
/*
* Update the PGR_ASSOC MSR on all cpus in @ cpu_mask ,
*
* Per task closids must have been set up before calling this function .
*
* The per cpu closids are updated with the smp function call , when @ closid
* is not NULL . If @ closid is NULL then all affected percpu closids must
* have been set up before calling this function .
*/
static void
rdt_update_closid ( const struct cpumask * cpu_mask , int * closid )
2016-11-11 17:02:38 -08:00
{
int cpu = get_cpu ( ) ;
if ( cpumask_test_cpu ( cpu , cpu_mask ) )
2016-11-18 15:18:04 -08:00
rdt_update_cpu_closid ( closid ) ;
smp_call_function_many ( cpu_mask , rdt_update_cpu_closid , closid , 1 ) ;
2016-11-11 17:02:38 -08:00
put_cpu ( ) ;
}
2016-10-28 15:04:45 -07:00
static ssize_t rdtgroup_cpus_write ( struct kernfs_open_file * of ,
char * buf , size_t nbytes , loff_t off )
{
cpumask_var_t tmpmask , newmask ;
struct rdtgroup * rdtgrp , * r ;
2016-11-11 17:02:38 -08:00
int ret ;
2016-10-28 15:04:45 -07:00
if ( ! buf )
return - EINVAL ;
if ( ! zalloc_cpumask_var ( & tmpmask , GFP_KERNEL ) )
return - ENOMEM ;
if ( ! zalloc_cpumask_var ( & newmask , GFP_KERNEL ) ) {
free_cpumask_var ( tmpmask ) ;
return - ENOMEM ;
}
2016-11-15 15:12:13 +01:00
2016-10-28 15:04:45 -07:00
rdtgrp = rdtgroup_kn_lock_live ( of - > kn ) ;
if ( ! rdtgrp ) {
ret = - ENOENT ;
goto unlock ;
}
2017-04-10 16:52:32 +02:00
if ( is_cpu_list ( of ) )
ret = cpulist_parse ( buf , newmask ) ;
else
ret = cpumask_parse ( buf , newmask ) ;
2016-10-28 15:04:45 -07:00
if ( ret )
goto unlock ;
/* check that user didn't specify any offline cpus */
cpumask_andnot ( tmpmask , newmask , cpu_online_mask ) ;
if ( cpumask_weight ( tmpmask ) ) {
ret = - EINVAL ;
2016-11-15 15:12:13 +01:00
goto unlock ;
2016-10-28 15:04:45 -07:00
}
/* Check whether cpus are dropped from this group */
cpumask_andnot ( tmpmask , & rdtgrp - > cpu_mask , newmask ) ;
if ( cpumask_weight ( tmpmask ) ) {
/* Can't drop from default group */
if ( rdtgrp = = & rdtgroup_default ) {
ret = - EINVAL ;
2016-11-15 15:12:13 +01:00
goto unlock ;
2016-10-28 15:04:45 -07:00
}
/* Give any dropped cpus to rdtgroup_default */
cpumask_or ( & rdtgroup_default . cpu_mask ,
& rdtgroup_default . cpu_mask , tmpmask ) ;
2016-11-18 15:18:04 -08:00
rdt_update_closid ( tmpmask , & rdtgroup_default . closid ) ;
2016-10-28 15:04:45 -07:00
}
/*
* If we added cpus , remove them from previous group that owned them
* and update per - cpu closid
*/
cpumask_andnot ( tmpmask , newmask , & rdtgrp - > cpu_mask ) ;
if ( cpumask_weight ( tmpmask ) ) {
list_for_each_entry ( r , & rdt_all_groups , rdtgroup_list ) {
if ( r = = rdtgrp )
continue ;
cpumask_andnot ( & r - > cpu_mask , & r - > cpu_mask , tmpmask ) ;
}
2016-11-18 15:18:04 -08:00
rdt_update_closid ( tmpmask , & rdtgrp - > closid ) ;
2016-10-28 15:04:45 -07:00
}
/* Done pushing/pulling - update this group with new mask */
cpumask_copy ( & rdtgrp - > cpu_mask , newmask ) ;
unlock :
rdtgroup_kn_unlock ( of - > kn ) ;
free_cpumask_var ( tmpmask ) ;
free_cpumask_var ( newmask ) ;
return ret ? : nbytes ;
}
2016-10-28 15:04:46 -07:00
struct task_move_callback {
struct callback_head work ;
struct rdtgroup * rdtgrp ;
} ;
static void move_myself ( struct callback_head * head )
{
struct task_move_callback * callback ;
struct rdtgroup * rdtgrp ;
callback = container_of ( head , struct task_move_callback , work ) ;
rdtgrp = callback - > rdtgrp ;
/*
* If resource group was deleted before this task work callback
* was invoked , then assign the task to root group and free the
* resource group .
*/
if ( atomic_dec_and_test ( & rdtgrp - > waitcount ) & &
( rdtgrp - > flags & RDT_DELETED ) ) {
current - > closid = 0 ;
kfree ( rdtgrp ) ;
}
2016-12-01 12:55:14 -08:00
preempt_disable ( ) ;
2016-10-28 15:04:48 -07:00
/* update PQR_ASSOC MSR to make resource group go into effect */
intel_rdt_sched_in ( ) ;
2016-12-01 12:55:14 -08:00
preempt_enable ( ) ;
2016-10-28 15:04:48 -07:00
2016-10-28 15:04:46 -07:00
kfree ( callback ) ;
}
static int __rdtgroup_move_task ( struct task_struct * tsk ,
struct rdtgroup * rdtgrp )
{
struct task_move_callback * callback ;
int ret ;
callback = kzalloc ( sizeof ( * callback ) , GFP_KERNEL ) ;
if ( ! callback )
return - ENOMEM ;
callback - > work . func = move_myself ;
callback - > rdtgrp = rdtgrp ;
/*
* Take a refcount , so rdtgrp cannot be freed before the
* callback has been invoked .
*/
atomic_inc ( & rdtgrp - > waitcount ) ;
ret = task_work_add ( tsk , & callback - > work , true ) ;
if ( ret ) {
/*
* Task is exiting . Drop the refcount and free the callback .
* No need to check the refcount as the group cannot be
* deleted before the write function unlocks rdtgroup_mutex .
*/
atomic_dec ( & rdtgrp - > waitcount ) ;
kfree ( callback ) ;
} else {
tsk - > closid = rdtgrp - > closid ;
}
return ret ;
}
static int rdtgroup_task_write_permission ( struct task_struct * task ,
struct kernfs_open_file * of )
{
const struct cred * tcred = get_task_cred ( task ) ;
const struct cred * cred = current_cred ( ) ;
int ret = 0 ;
/*
* Even if we ' re attaching all tasks in the thread group , we only
* need to check permissions on one of them .
*/
if ( ! uid_eq ( cred - > euid , GLOBAL_ROOT_UID ) & &
! uid_eq ( cred - > euid , tcred - > uid ) & &
! uid_eq ( cred - > euid , tcred - > suid ) )
ret = - EPERM ;
put_cred ( tcred ) ;
return ret ;
}
static int rdtgroup_move_task ( pid_t pid , struct rdtgroup * rdtgrp ,
struct kernfs_open_file * of )
{
struct task_struct * tsk ;
int ret ;
rcu_read_lock ( ) ;
if ( pid ) {
tsk = find_task_by_vpid ( pid ) ;
if ( ! tsk ) {
rcu_read_unlock ( ) ;
return - ESRCH ;
}
} else {
tsk = current ;
}
get_task_struct ( tsk ) ;
rcu_read_unlock ( ) ;
ret = rdtgroup_task_write_permission ( tsk , of ) ;
if ( ! ret )
ret = __rdtgroup_move_task ( tsk , rdtgrp ) ;
put_task_struct ( tsk ) ;
return ret ;
}
static ssize_t rdtgroup_tasks_write ( struct kernfs_open_file * of ,
char * buf , size_t nbytes , loff_t off )
{
struct rdtgroup * rdtgrp ;
int ret = 0 ;
pid_t pid ;
if ( kstrtoint ( strstrip ( buf ) , 0 , & pid ) | | pid < 0 )
return - EINVAL ;
rdtgrp = rdtgroup_kn_lock_live ( of - > kn ) ;
if ( rdtgrp )
ret = rdtgroup_move_task ( pid , rdtgrp , of ) ;
else
ret = - ENOENT ;
rdtgroup_kn_unlock ( of - > kn ) ;
return ret ? : nbytes ;
}
static void show_rdt_tasks ( struct rdtgroup * r , struct seq_file * s )
{
struct task_struct * p , * t ;
rcu_read_lock ( ) ;
for_each_process_thread ( p , t ) {
if ( t - > closid = = r - > closid )
seq_printf ( s , " %d \n " , t - > pid ) ;
}
rcu_read_unlock ( ) ;
}
static int rdtgroup_tasks_show ( struct kernfs_open_file * of ,
struct seq_file * s , void * v )
{
struct rdtgroup * rdtgrp ;
int ret = 0 ;
rdtgrp = rdtgroup_kn_lock_live ( of - > kn ) ;
if ( rdtgrp )
show_rdt_tasks ( rdtgrp , s ) ;
else
ret = - ENOENT ;
rdtgroup_kn_unlock ( of - > kn ) ;
return ret ;
}
2016-10-28 15:04:43 -07:00
static int rdt_num_closids_show ( struct kernfs_open_file * of ,
struct seq_file * seq , void * v )
{
struct rdt_resource * r = of - > kn - > parent - > priv ;
seq_printf ( seq , " %d \n " , r - > num_closid ) ;
return 0 ;
}
2017-04-07 17:33:51 -07:00
static int rdt_default_ctrl_show ( struct kernfs_open_file * of ,
2016-10-28 15:04:43 -07:00
struct seq_file * seq , void * v )
{
struct rdt_resource * r = of - > kn - > parent - > priv ;
2017-04-07 17:33:51 -07:00
seq_printf ( seq , " %x \n " , r - > default_ctrl ) ;
2016-10-28 15:04:43 -07:00
return 0 ;
}
2016-11-03 14:09:06 -07:00
static int rdt_min_cbm_bits_show ( struct kernfs_open_file * of ,
struct seq_file * seq , void * v )
{
struct rdt_resource * r = of - > kn - > parent - > priv ;
2017-04-14 13:00:36 +02:00
seq_printf ( seq , " %u \n " , r - > cache . min_cbm_bits ) ;
2017-04-07 17:33:55 -07:00
return 0 ;
}
static int rdt_min_bw_show ( struct kernfs_open_file * of ,
struct seq_file * seq , void * v )
{
struct rdt_resource * r = of - > kn - > parent - > priv ;
2016-11-03 14:09:06 -07:00
2017-04-07 17:33:55 -07:00
seq_printf ( seq , " %u \n " , r - > membw . min_bw ) ;
return 0 ;
}
2017-07-25 14:14:30 -07:00
static int rdt_num_rmids_show ( struct kernfs_open_file * of ,
struct seq_file * seq , void * v )
{
struct rdt_resource * r = of - > kn - > parent - > priv ;
seq_printf ( seq , " %d \n " , r - > num_rmid ) ;
return 0 ;
}
static int rdt_mon_features_show ( struct kernfs_open_file * of ,
struct seq_file * seq , void * v )
{
struct rdt_resource * r = of - > kn - > parent - > priv ;
struct mon_evt * mevt ;
list_for_each_entry ( mevt , & r - > evt_list , list )
seq_printf ( seq , " %s \n " , mevt - > name ) ;
return 0 ;
}
2017-04-07 17:33:55 -07:00
static int rdt_bw_gran_show ( struct kernfs_open_file * of ,
struct seq_file * seq , void * v )
{
struct rdt_resource * r = of - > kn - > parent - > priv ;
seq_printf ( seq , " %u \n " , r - > membw . bw_gran ) ;
return 0 ;
}
static int rdt_delay_linear_show ( struct kernfs_open_file * of ,
struct seq_file * seq , void * v )
{
struct rdt_resource * r = of - > kn - > parent - > priv ;
seq_printf ( seq , " %u \n " , r - > membw . delay_linear ) ;
2016-11-03 14:09:06 -07:00
return 0 ;
}
2017-07-25 14:14:30 -07:00
static int max_threshold_occ_show ( struct kernfs_open_file * of ,
struct seq_file * seq , void * v )
{
struct rdt_resource * r = of - > kn - > parent - > priv ;
seq_printf ( seq , " %u \n " , intel_cqm_threshold * r - > mon_scale ) ;
return 0 ;
}
static ssize_t max_threshold_occ_write ( struct kernfs_open_file * of ,
char * buf , size_t nbytes , loff_t off )
{
struct rdt_resource * r = of - > kn - > parent - > priv ;
unsigned int bytes ;
int ret ;
ret = kstrtouint ( buf , 0 , & bytes ) ;
if ( ret )
return ret ;
if ( bytes > ( boot_cpu_data . x86_cache_size * 1024 ) )
return - EINVAL ;
intel_cqm_threshold = bytes / r - > mon_scale ;
return ret ? : nbytes ;
}
2016-10-28 15:04:43 -07:00
/* rdtgroup information files for one cache resource. */
2017-07-25 14:14:29 -07:00
static struct rftype res_common_files [ ] = {
2016-10-28 15:04:43 -07:00
{
. name = " num_closids " ,
. mode = 0444 ,
. kf_ops = & rdtgroup_kf_single_ops ,
. seq_show = rdt_num_closids_show ,
2017-07-25 14:14:29 -07:00
. fflags = RF_CTRL_INFO ,
2016-10-28 15:04:43 -07:00
} ,
2017-07-25 14:14:30 -07:00
{
. name = " mon_features " ,
. mode = 0444 ,
. kf_ops = & rdtgroup_kf_single_ops ,
. seq_show = rdt_mon_features_show ,
. fflags = RF_MON_INFO ,
} ,
{
. name = " num_rmids " ,
. mode = 0444 ,
. kf_ops = & rdtgroup_kf_single_ops ,
. seq_show = rdt_num_rmids_show ,
. fflags = RF_MON_INFO ,
} ,
2016-10-28 15:04:43 -07:00
{
. name = " cbm_mask " ,
. mode = 0444 ,
. kf_ops = & rdtgroup_kf_single_ops ,
2017-04-07 17:33:51 -07:00
. seq_show = rdt_default_ctrl_show ,
2017-07-25 14:14:29 -07:00
. fflags = RF_CTRL_INFO | RFTYPE_RES_CACHE ,
2016-10-28 15:04:43 -07:00
} ,
2016-11-03 14:09:06 -07:00
{
. name = " min_cbm_bits " ,
. mode = 0444 ,
. kf_ops = & rdtgroup_kf_single_ops ,
. seq_show = rdt_min_cbm_bits_show ,
2017-07-25 14:14:29 -07:00
. fflags = RF_CTRL_INFO | RFTYPE_RES_CACHE ,
2017-04-07 17:33:55 -07:00
} ,
{
. name = " min_bandwidth " ,
. mode = 0444 ,
. kf_ops = & rdtgroup_kf_single_ops ,
. seq_show = rdt_min_bw_show ,
2017-07-25 14:14:29 -07:00
. fflags = RF_CTRL_INFO | RFTYPE_RES_MB ,
2017-04-07 17:33:55 -07:00
} ,
{
. name = " bandwidth_gran " ,
. mode = 0444 ,
. kf_ops = & rdtgroup_kf_single_ops ,
. seq_show = rdt_bw_gran_show ,
2017-07-25 14:14:29 -07:00
. fflags = RF_CTRL_INFO | RFTYPE_RES_MB ,
2017-04-07 17:33:55 -07:00
} ,
{
. name = " delay_linear " ,
. mode = 0444 ,
. kf_ops = & rdtgroup_kf_single_ops ,
. seq_show = rdt_delay_linear_show ,
2017-07-25 14:14:29 -07:00
. fflags = RF_CTRL_INFO | RFTYPE_RES_MB ,
} ,
2017-07-25 14:14:30 -07:00
{
. name = " max_threshold_occupancy " ,
. mode = 0644 ,
. kf_ops = & rdtgroup_kf_single_ops ,
. write = max_threshold_occ_write ,
. seq_show = max_threshold_occ_show ,
. fflags = RF_MON_INFO | RFTYPE_RES_CACHE ,
} ,
2017-07-25 14:14:29 -07:00
{
. name = " cpus " ,
. mode = 0644 ,
. kf_ops = & rdtgroup_kf_single_ops ,
. write = rdtgroup_cpus_write ,
. seq_show = rdtgroup_cpus_show ,
. fflags = RFTYPE_BASE ,
} ,
{
. name = " cpus_list " ,
. mode = 0644 ,
. kf_ops = & rdtgroup_kf_single_ops ,
. write = rdtgroup_cpus_write ,
. seq_show = rdtgroup_cpus_show ,
. flags = RFTYPE_FLAGS_CPUS_LIST ,
. fflags = RFTYPE_BASE ,
} ,
{
. name = " tasks " ,
. mode = 0644 ,
. kf_ops = & rdtgroup_kf_single_ops ,
. write = rdtgroup_tasks_write ,
. seq_show = rdtgroup_tasks_show ,
. fflags = RFTYPE_BASE ,
} ,
{
. name = " schemata " ,
. mode = 0644 ,
. kf_ops = & rdtgroup_kf_single_ops ,
. write = rdtgroup_schemata_write ,
. seq_show = rdtgroup_schemata_show ,
. fflags = RF_CTRL_BASE ,
2017-04-07 17:33:55 -07:00
} ,
} ;
2017-07-25 14:14:29 -07:00
static int rdtgroup_add_files ( struct kernfs_node * kn , unsigned long fflags )
2017-04-07 17:33:55 -07:00
{
2017-07-25 14:14:29 -07:00
struct rftype * rfts , * rft ;
int ret , len ;
rfts = res_common_files ;
len = ARRAY_SIZE ( res_common_files ) ;
lockdep_assert_held ( & rdtgroup_mutex ) ;
for ( rft = rfts ; rft < rfts + len ; rft + + ) {
if ( ( fflags & rft - > fflags ) = = rft - > fflags ) {
ret = rdtgroup_add_file ( kn , rft ) ;
if ( ret )
goto error ;
}
}
return 0 ;
error :
pr_warn ( " Failed to add %s, err=%d \n " , rft - > name , ret ) ;
while ( - - rft > = rfts ) {
if ( ( fflags & rft - > fflags ) = = rft - > fflags )
kernfs_remove_by_name ( kn , rft - > name ) ;
}
return ret ;
2017-04-07 17:33:55 -07:00
}
2017-07-25 14:14:29 -07:00
static int rdtgroup_mkdir_info_resdir ( struct rdt_resource * r , char * name ,
unsigned long fflags )
2017-04-07 17:33:54 -07:00
{
2017-07-25 14:14:29 -07:00
struct kernfs_node * kn_subdir ;
int ret ;
kn_subdir = kernfs_create_dir ( kn_info , name ,
kn_info - > mode , r ) ;
if ( IS_ERR ( kn_subdir ) )
return PTR_ERR ( kn_subdir ) ;
kernfs_get ( kn_subdir ) ;
ret = rdtgroup_kn_set_ugid ( kn_subdir ) ;
if ( ret )
return ret ;
ret = rdtgroup_add_files ( kn_subdir , fflags ) ;
if ( ! ret )
kernfs_activate ( kn_subdir ) ;
return ret ;
2017-04-07 17:33:54 -07:00
}
2016-10-28 15:04:43 -07:00
static int rdtgroup_create_info_dir ( struct kernfs_node * parent_kn )
{
struct rdt_resource * r ;
2017-07-25 14:14:29 -07:00
unsigned long fflags ;
2017-07-25 14:14:30 -07:00
char name [ 32 ] ;
2017-07-25 14:14:29 -07:00
int ret ;
2016-10-28 15:04:43 -07:00
/* create the directory */
kn_info = kernfs_create_dir ( parent_kn , " info " , parent_kn - > mode , NULL ) ;
if ( IS_ERR ( kn_info ) )
return PTR_ERR ( kn_info ) ;
kernfs_get ( kn_info ) ;
2017-07-25 14:14:25 -07:00
for_each_alloc_enabled_rdt_resource ( r ) {
2017-07-25 14:14:29 -07:00
fflags = r - > fflags | RF_CTRL_INFO ;
ret = rdtgroup_mkdir_info_resdir ( r , r - > name , fflags ) ;
2016-10-28 15:04:43 -07:00
if ( ret )
goto out_destroy ;
}
2017-07-25 14:14:30 -07:00
for_each_mon_enabled_rdt_resource ( r ) {
fflags = r - > fflags | RF_MON_INFO ;
sprintf ( name , " %s_MON " , r - > name ) ;
ret = rdtgroup_mkdir_info_resdir ( r , name , fflags ) ;
if ( ret )
goto out_destroy ;
}
2016-10-28 15:04:43 -07:00
/*
* This extra ref will be put in kernfs_remove ( ) and guarantees
* that @ rdtgrp - > kn is always accessible .
*/
kernfs_get ( kn_info ) ;
ret = rdtgroup_kn_set_ugid ( kn_info ) ;
if ( ret )
goto out_destroy ;
kernfs_activate ( kn_info ) ;
return 0 ;
out_destroy :
kernfs_remove ( kn_info ) ;
return ret ;
}
2017-07-25 14:14:32 -07:00
static int
mongroup_create_dir ( struct kernfs_node * parent_kn , struct rdtgroup * prgrp ,
char * name , struct kernfs_node * * dest_kn )
{
struct kernfs_node * kn ;
int ret ;
/* create the directory */
kn = kernfs_create_dir ( parent_kn , name , parent_kn - > mode , prgrp ) ;
if ( IS_ERR ( kn ) )
return PTR_ERR ( kn ) ;
if ( dest_kn )
* dest_kn = kn ;
/*
* This extra ref will be put in kernfs_remove ( ) and guarantees
* that @ rdtgrp - > kn is always accessible .
*/
kernfs_get ( kn ) ;
ret = rdtgroup_kn_set_ugid ( kn ) ;
if ( ret )
goto out_destroy ;
kernfs_activate ( kn ) ;
return 0 ;
out_destroy :
kernfs_remove ( kn ) ;
return ret ;
}
2016-10-28 15:04:42 -07:00
static void l3_qos_cfg_update ( void * arg )
{
bool * enable = arg ;
wrmsrl ( IA32_L3_QOS_CFG , * enable ? L3_QOS_CDP_ENABLE : 0ULL ) ;
}
static int set_l3_qos_cfg ( struct rdt_resource * r , bool enable )
{
cpumask_var_t cpu_mask ;
struct rdt_domain * d ;
int cpu ;
if ( ! zalloc_cpumask_var ( & cpu_mask , GFP_KERNEL ) )
return - ENOMEM ;
list_for_each_entry ( d , & r - > domains , list ) {
/* Pick one CPU from each domain instance to update MSR */
cpumask_set_cpu ( cpumask_any ( & d - > cpu_mask ) , cpu_mask ) ;
}
cpu = get_cpu ( ) ;
/* Update QOS_CFG MSR on this cpu if it's in cpu_mask. */
if ( cpumask_test_cpu ( cpu , cpu_mask ) )
l3_qos_cfg_update ( & enable ) ;
/* Update QOS_CFG MSR on all other cpus in cpu_mask. */
smp_call_function_many ( cpu_mask , l3_qos_cfg_update , & enable , 1 ) ;
put_cpu ( ) ;
free_cpumask_var ( cpu_mask ) ;
return 0 ;
}
static int cdp_enable ( void )
{
struct rdt_resource * r_l3data = & rdt_resources_all [ RDT_RESOURCE_L3DATA ] ;
struct rdt_resource * r_l3code = & rdt_resources_all [ RDT_RESOURCE_L3CODE ] ;
struct rdt_resource * r_l3 = & rdt_resources_all [ RDT_RESOURCE_L3 ] ;
int ret ;
2017-07-25 14:14:25 -07:00
if ( ! r_l3 - > alloc_capable | | ! r_l3data - > alloc_capable | |
! r_l3code - > alloc_capable )
2016-10-28 15:04:42 -07:00
return - EINVAL ;
ret = set_l3_qos_cfg ( r_l3 , true ) ;
if ( ! ret ) {
2017-07-25 14:14:25 -07:00
r_l3 - > alloc_enabled = false ;
r_l3data - > alloc_enabled = true ;
r_l3code - > alloc_enabled = true ;
2016-10-28 15:04:42 -07:00
}
return ret ;
}
static void cdp_disable ( void )
{
struct rdt_resource * r = & rdt_resources_all [ RDT_RESOURCE_L3 ] ;
2017-07-25 14:14:25 -07:00
r - > alloc_enabled = r - > alloc_capable ;
2016-10-28 15:04:42 -07:00
2017-07-25 14:14:25 -07:00
if ( rdt_resources_all [ RDT_RESOURCE_L3DATA ] . alloc_enabled ) {
rdt_resources_all [ RDT_RESOURCE_L3DATA ] . alloc_enabled = false ;
rdt_resources_all [ RDT_RESOURCE_L3CODE ] . alloc_enabled = false ;
2016-10-28 15:04:42 -07:00
set_l3_qos_cfg ( r , false ) ;
}
}
static int parse_rdtgroupfs_options ( char * data )
{
char * token , * o = data ;
int ret = 0 ;
while ( ( token = strsep ( & o , " , " ) ) ! = NULL ) {
if ( ! * token )
return - EINVAL ;
if ( ! strcmp ( token , " cdp " ) )
ret = cdp_enable ( ) ;
}
return ret ;
}
2016-10-28 15:04:44 -07:00
/*
* We don ' t allow rdtgroup directories to be created anywhere
* except the root directory . Thus when looking for the rdtgroup
* structure for a kernfs node we are either looking at a directory ,
* in which case the rdtgroup structure is pointed at by the " priv "
* field , otherwise we have a file , and need only look to the parent
* to find the rdtgroup .
*/
static struct rdtgroup * kernfs_to_rdtgroup ( struct kernfs_node * kn )
{
2016-11-11 17:02:36 -08:00
if ( kernfs_type ( kn ) = = KERNFS_DIR ) {
/*
* All the resource directories use " kn->priv "
* to point to the " struct rdtgroup " for the
* resource . " info " and its subdirectories don ' t
* have rdtgroup structures , so return NULL here .
*/
if ( kn = = kn_info | | kn - > parent = = kn_info )
return NULL ;
else
return kn - > priv ;
} else {
2016-10-28 15:04:44 -07:00
return kn - > parent - > priv ;
2016-11-11 17:02:36 -08:00
}
2016-10-28 15:04:44 -07:00
}
struct rdtgroup * rdtgroup_kn_lock_live ( struct kernfs_node * kn )
{
struct rdtgroup * rdtgrp = kernfs_to_rdtgroup ( kn ) ;
2016-11-11 17:02:36 -08:00
if ( ! rdtgrp )
return NULL ;
2016-10-28 15:04:44 -07:00
atomic_inc ( & rdtgrp - > waitcount ) ;
kernfs_break_active_protection ( kn ) ;
mutex_lock ( & rdtgroup_mutex ) ;
/* Was this group deleted while we waited? */
if ( rdtgrp - > flags & RDT_DELETED )
return NULL ;
return rdtgrp ;
}
void rdtgroup_kn_unlock ( struct kernfs_node * kn )
{
struct rdtgroup * rdtgrp = kernfs_to_rdtgroup ( kn ) ;
2016-11-11 17:02:36 -08:00
if ( ! rdtgrp )
return ;
2016-10-28 15:04:44 -07:00
mutex_unlock ( & rdtgroup_mutex ) ;
if ( atomic_dec_and_test ( & rdtgrp - > waitcount ) & &
( rdtgrp - > flags & RDT_DELETED ) ) {
kernfs_unbreak_active_protection ( kn ) ;
2017-03-14 15:20:53 +01:00
kernfs_put ( rdtgrp - > kn ) ;
2016-10-28 15:04:44 -07:00
kfree ( rdtgrp ) ;
} else {
kernfs_unbreak_active_protection ( kn ) ;
}
}
2016-10-28 15:04:42 -07:00
static struct dentry * rdt_mount ( struct file_system_type * fs_type ,
int flags , const char * unused_dev_name ,
void * data )
{
struct dentry * dentry ;
int ret ;
mutex_lock ( & rdtgroup_mutex ) ;
/*
* resctrl file system can only be mounted once .
*/
2017-07-25 14:14:25 -07:00
if ( static_branch_unlikely ( & rdt_alloc_enable_key ) ) {
2016-10-28 15:04:42 -07:00
dentry = ERR_PTR ( - EBUSY ) ;
goto out ;
}
ret = parse_rdtgroupfs_options ( data ) ;
if ( ret ) {
dentry = ERR_PTR ( ret ) ;
goto out_cdp ;
}
2016-10-28 15:04:44 -07:00
closid_init ( ) ;
2016-10-28 15:04:43 -07:00
ret = rdtgroup_create_info_dir ( rdtgroup_default . kn ) ;
2016-11-03 14:09:05 -07:00
if ( ret ) {
dentry = ERR_PTR ( ret ) ;
2016-10-28 15:04:43 -07:00
goto out_cdp ;
2016-11-03 14:09:05 -07:00
}
2016-10-28 15:04:43 -07:00
2016-10-28 15:04:42 -07:00
dentry = kernfs_mount ( fs_type , flags , rdt_root ,
RDTGROUP_SUPER_MAGIC , NULL ) ;
if ( IS_ERR ( dentry ) )
2017-06-26 11:55:49 -07:00
goto out_destroy ;
2016-10-28 15:04:42 -07:00
2017-07-25 14:14:25 -07:00
static_branch_enable ( & rdt_alloc_enable_key ) ;
2016-10-28 15:04:42 -07:00
goto out ;
2017-06-26 11:55:49 -07:00
out_destroy :
kernfs_remove ( kn_info ) ;
2016-10-28 15:04:42 -07:00
out_cdp :
cdp_disable ( ) ;
out :
mutex_unlock ( & rdtgroup_mutex ) ;
return dentry ;
}
2017-04-07 17:33:51 -07:00
static int reset_all_ctrls ( struct rdt_resource * r )
2016-10-28 15:04:42 -07:00
{
struct msr_param msr_param ;
cpumask_var_t cpu_mask ;
struct rdt_domain * d ;
int i , cpu ;
if ( ! zalloc_cpumask_var ( & cpu_mask , GFP_KERNEL ) )
return - ENOMEM ;
msr_param . res = r ;
msr_param . low = 0 ;
msr_param . high = r - > num_closid ;
/*
* Disable resource control for this resource by setting all
* CBMs in all domains to the maximum mask value . Pick one CPU
* from each domain to update the MSRs below .
*/
list_for_each_entry ( d , & r - > domains , list ) {
cpumask_set_cpu ( cpumask_any ( & d - > cpu_mask ) , cpu_mask ) ;
for ( i = 0 ; i < r - > num_closid ; i + + )
2017-04-07 17:33:51 -07:00
d - > ctrl_val [ i ] = r - > default_ctrl ;
2016-10-28 15:04:42 -07:00
}
cpu = get_cpu ( ) ;
/* Update CBM on this cpu if it's in cpu_mask. */
if ( cpumask_test_cpu ( cpu , cpu_mask ) )
2017-04-07 17:33:51 -07:00
rdt_ctrl_update ( & msr_param ) ;
2016-10-28 15:04:42 -07:00
/* Update CBM on all other cpus in cpu_mask. */
2017-04-07 17:33:51 -07:00
smp_call_function_many ( cpu_mask , rdt_ctrl_update , & msr_param , 1 ) ;
2016-10-28 15:04:42 -07:00
put_cpu ( ) ;
free_cpumask_var ( cpu_mask ) ;
return 0 ;
}
2016-10-28 15:04:43 -07:00
/*
2016-11-18 15:18:04 -08:00
* Move tasks from one to the other group . If @ from is NULL , then all tasks
* in the systems are moved unconditionally ( used for teardown ) .
*
* If @ mask is not NULL the cpus on which moved tasks are running are set
* in that mask so the update smp function call is restricted to affected
* cpus .
2016-10-28 15:04:43 -07:00
*/
2016-11-18 15:18:04 -08:00
static void rdt_move_group_tasks ( struct rdtgroup * from , struct rdtgroup * to ,
struct cpumask * mask )
2016-10-28 15:04:43 -07:00
{
2016-10-28 15:04:46 -07:00
struct task_struct * p , * t ;
read_lock ( & tasklist_lock ) ;
2016-11-18 15:18:04 -08:00
for_each_process_thread ( p , t ) {
if ( ! from | | t - > closid = = from - > closid ) {
t - > closid = to - > closid ;
# ifdef CONFIG_SMP
/*
* This is safe on x86 w / o barriers as the ordering
* of writing to task_cpu ( ) and t - > on_cpu is
* reverse to the reading here . The detection is
* inaccurate as tasks might move or schedule
* before the smp function call takes place . In
* such a case the function call is pointless , but
* there is no other side effect .
*/
if ( mask & & t - > on_cpu )
cpumask_set_cpu ( task_cpu ( t ) , mask ) ;
# endif
}
}
2016-10-28 15:04:46 -07:00
read_unlock ( & tasklist_lock ) ;
2016-11-18 15:18:04 -08:00
}
/*
* Forcibly remove all of subdirectories under root .
*/
static void rmdir_all_sub ( void )
{
struct rdtgroup * rdtgrp , * tmp ;
/* Move all tasks to the default resource group */
rdt_move_group_tasks ( NULL , & rdtgroup_default , NULL ) ;
2016-10-28 15:04:44 -07:00
list_for_each_entry_safe ( rdtgrp , tmp , & rdt_all_groups , rdtgroup_list ) {
/* Remove each rdtgroup other than root */
if ( rdtgrp = = & rdtgroup_default )
continue ;
2016-11-11 17:02:37 -08:00
/*
* Give any CPUs back to the default group . We cannot copy
* cpu_online_mask because a CPU might have executed the
* offline callback already , but is still marked online .
*/
cpumask_or ( & rdtgroup_default . cpu_mask ,
& rdtgroup_default . cpu_mask , & rdtgrp - > cpu_mask ) ;
2016-10-28 15:04:44 -07:00
kernfs_remove ( rdtgrp - > kn ) ;
list_del ( & rdtgrp - > rdtgroup_list ) ;
kfree ( rdtgrp ) ;
}
2016-11-18 15:18:04 -08:00
/* Notify online CPUs to update per cpu storage and PQR_ASSOC MSR */
get_online_cpus ( ) ;
rdt_update_closid ( cpu_online_mask , & rdtgroup_default . closid ) ;
put_online_cpus ( ) ;
2016-10-28 15:04:43 -07:00
kernfs_remove ( kn_info ) ;
}
2016-10-28 15:04:42 -07:00
static void rdt_kill_sb ( struct super_block * sb )
{
struct rdt_resource * r ;
mutex_lock ( & rdtgroup_mutex ) ;
/*Put everything back to default values. */
2017-07-25 14:14:25 -07:00
for_each_alloc_enabled_rdt_resource ( r )
2017-04-07 17:33:51 -07:00
reset_all_ctrls ( r ) ;
2016-10-28 15:04:42 -07:00
cdp_disable ( ) ;
2016-10-28 15:04:43 -07:00
rmdir_all_sub ( ) ;
2017-07-25 14:14:25 -07:00
static_branch_disable ( & rdt_alloc_enable_key ) ;
2016-10-28 15:04:42 -07:00
kernfs_kill_sb ( sb ) ;
mutex_unlock ( & rdtgroup_mutex ) ;
}
static struct file_system_type rdt_fs_type = {
. name = " resctrl " ,
. mount = rdt_mount ,
. kill_sb = rdt_kill_sb ,
} ;
2017-07-25 14:14:31 -07:00
static int mkdir_rdt_prepare ( struct kernfs_node * parent_kn ,
struct kernfs_node * prgrp_kn ,
const char * name , umode_t mode ,
2017-07-25 14:14:32 -07:00
enum rdt_group_type rtype , struct rdtgroup * * r )
2016-10-28 15:04:44 -07:00
{
2017-07-25 14:14:31 -07:00
struct rdtgroup * prdtgrp , * rdtgrp ;
2016-10-28 15:04:44 -07:00
struct kernfs_node * kn ;
2017-07-25 14:14:31 -07:00
uint files = 0 ;
int ret ;
2016-10-28 15:04:44 -07:00
2017-07-25 14:14:31 -07:00
prdtgrp = rdtgroup_kn_lock_live ( prgrp_kn ) ;
if ( ! prdtgrp ) {
2016-10-28 15:04:44 -07:00
ret = - ENODEV ;
goto out_unlock ;
}
/* allocate the rdtgroup. */
rdtgrp = kzalloc ( sizeof ( * rdtgrp ) , GFP_KERNEL ) ;
if ( ! rdtgrp ) {
ret = - ENOSPC ;
2017-07-25 14:14:31 -07:00
goto out_unlock ;
2016-10-28 15:04:44 -07:00
}
2017-07-25 14:14:31 -07:00
* r = rdtgrp ;
2017-07-25 14:14:32 -07:00
rdtgrp - > mon . parent = prdtgrp ;
rdtgrp - > type = rtype ;
INIT_LIST_HEAD ( & rdtgrp - > mon . crdtgrp_list ) ;
2016-10-28 15:04:44 -07:00
/* kernfs creates the directory for rdtgrp */
2017-07-25 14:14:31 -07:00
kn = kernfs_create_dir ( parent_kn , name , mode , rdtgrp ) ;
2016-10-28 15:04:44 -07:00
if ( IS_ERR ( kn ) ) {
ret = PTR_ERR ( kn ) ;
2017-07-25 14:14:31 -07:00
goto out_free_rgrp ;
2016-10-28 15:04:44 -07:00
}
rdtgrp - > kn = kn ;
/*
* kernfs_remove ( ) will drop the reference count on " kn " which
* will free it . But we still need it to stick around for the
* rdtgroup_kn_unlock ( kn } call below . Take one extra reference
* here , which will be dropped inside rdtgroup_kn_unlock ( ) .
*/
kernfs_get ( kn ) ;
ret = rdtgroup_kn_set_ugid ( kn ) ;
if ( ret )
goto out_destroy ;
2017-07-25 14:14:31 -07:00
files = RFTYPE_BASE | RFTYPE_CTRL ;
2017-07-25 14:14:32 -07:00
files = RFTYPE_BASE | BIT ( RF_CTRLSHIFT + rtype ) ;
2017-07-25 14:14:31 -07:00
ret = rdtgroup_add_files ( kn , files ) ;
2016-10-28 15:04:45 -07:00
if ( ret )
goto out_destroy ;
2017-07-25 14:14:32 -07:00
if ( rdt_mon_capable ) {
ret = alloc_rmid ( ) ;
if ( ret < 0 )
goto out_destroy ;
rdtgrp - > mon . rmid = ret ;
}
2016-10-28 15:04:44 -07:00
kernfs_activate ( kn ) ;
2017-07-25 14:14:31 -07:00
/*
* The caller unlocks the prgrp_kn upon success .
*/
return 0 ;
2016-10-28 15:04:44 -07:00
out_destroy :
kernfs_remove ( rdtgrp - > kn ) ;
2017-07-25 14:14:31 -07:00
out_free_rgrp :
2016-10-28 15:04:44 -07:00
kfree ( rdtgrp ) ;
out_unlock :
2017-07-25 14:14:31 -07:00
rdtgroup_kn_unlock ( prgrp_kn ) ;
return ret ;
}
static void mkdir_rdt_prepare_clean ( struct rdtgroup * rgrp )
{
kernfs_remove ( rgrp - > kn ) ;
2017-07-25 14:14:32 -07:00
free_rmid ( rgrp - > mon . rmid ) ;
2017-07-25 14:14:31 -07:00
kfree ( rgrp ) ;
}
2017-07-25 14:14:32 -07:00
/*
* Create a monitor group under " mon_groups " directory of a control
* and monitor group ( ctrl_mon ) . This is a resource group
* to monitor a subset of tasks and cpus in its parent ctrl_mon group .
*/
static int rdtgroup_mkdir_mon ( struct kernfs_node * parent_kn ,
struct kernfs_node * prgrp_kn ,
const char * name ,
umode_t mode )
{
struct rdtgroup * rdtgrp , * prgrp ;
int ret ;
ret = mkdir_rdt_prepare ( parent_kn , prgrp_kn , name , mode , RDTMON_GROUP ,
& rdtgrp ) ;
if ( ret )
return ret ;
prgrp = rdtgrp - > mon . parent ;
rdtgrp - > closid = prgrp - > closid ;
/*
* Add the rdtgrp to the list of rdtgrps the parent
* ctrl_mon group has to track .
*/
list_add_tail ( & rdtgrp - > mon . crdtgrp_list , & prgrp - > mon . crdtgrp_list ) ;
rdtgroup_kn_unlock ( prgrp_kn ) ;
return ret ;
}
2017-07-25 14:14:31 -07:00
/*
* These are rdtgroups created under the root directory . Can be used
2017-07-25 14:14:32 -07:00
* to allocate and monitor resources .
2017-07-25 14:14:31 -07:00
*/
2017-07-25 14:14:32 -07:00
static int rdtgroup_mkdir_ctrl_mon ( struct kernfs_node * parent_kn ,
struct kernfs_node * prgrp_kn ,
const char * name , umode_t mode )
2017-07-25 14:14:31 -07:00
{
struct rdtgroup * rdtgrp ;
struct kernfs_node * kn ;
u32 closid ;
int ret ;
2017-07-25 14:14:32 -07:00
ret = mkdir_rdt_prepare ( parent_kn , prgrp_kn , name , mode , RDTCTRL_GROUP ,
& rdtgrp ) ;
2017-07-25 14:14:31 -07:00
if ( ret )
return ret ;
kn = rdtgrp - > kn ;
ret = closid_alloc ( ) ;
if ( ret < 0 )
goto out_common_fail ;
closid = ret ;
rdtgrp - > closid = closid ;
list_add ( & rdtgrp - > rdtgroup_list , & rdt_all_groups ) ;
2017-07-25 14:14:32 -07:00
if ( rdt_mon_capable ) {
/*
* Create an empty mon_groups directory to hold the subset
* of tasks and cpus to monitor .
*/
ret = mongroup_create_dir ( kn , NULL , " mon_groups " , NULL ) ;
if ( ret )
goto out_id_free ;
}
2017-07-25 14:14:31 -07:00
goto out_unlock ;
2017-07-25 14:14:32 -07:00
out_id_free :
closid_free ( closid ) ;
list_del ( & rdtgrp - > rdtgroup_list ) ;
2017-07-25 14:14:31 -07:00
out_common_fail :
mkdir_rdt_prepare_clean ( rdtgrp ) ;
out_unlock :
rdtgroup_kn_unlock ( prgrp_kn ) ;
2016-10-28 15:04:44 -07:00
return ret ;
}
2017-07-25 14:14:32 -07:00
/*
* We allow creating mon groups only with in a directory called " mon_groups "
* which is present in every ctrl_mon group . Check if this is a valid
* " mon_groups " directory .
*
* 1. The directory should be named " mon_groups " .
* 2. The mon group itself should " not " be named " mon_groups " .
* This makes sure " mon_groups " directory always has a ctrl_mon group
* as parent .
*/
static bool is_mon_groups ( struct kernfs_node * kn , const char * name )
{
return ( ! strcmp ( kn - > name , " mon_groups " ) & &
strcmp ( name , " mon_groups " ) ) ;
}
2017-07-25 14:14:31 -07:00
static int rdtgroup_mkdir ( struct kernfs_node * parent_kn , const char * name ,
umode_t mode )
{
/* Do not accept '\n' to avoid unparsable situation. */
if ( strchr ( name , ' \n ' ) )
return - EINVAL ;
/*
* If the parent directory is the root directory and RDT
2017-07-25 14:14:32 -07:00
* allocation is supported , add a control and monitoring
* subdirectory
2017-07-25 14:14:31 -07:00
*/
if ( rdt_alloc_capable & & parent_kn = = rdtgroup_default . kn )
2017-07-25 14:14:32 -07:00
return rdtgroup_mkdir_ctrl_mon ( parent_kn , parent_kn , name , mode ) ;
/*
* If RDT monitoring is supported and the parent directory is a valid
* " mon_groups " directory , add a monitoring subdirectory .
*/
if ( rdt_mon_capable & & is_mon_groups ( parent_kn , name ) )
return rdtgroup_mkdir_mon ( parent_kn , parent_kn - > parent , name , mode ) ;
2017-07-25 14:14:31 -07:00
return - EPERM ;
}
2016-10-28 15:04:44 -07:00
static int rdtgroup_rmdir ( struct kernfs_node * kn )
{
2016-11-18 15:18:04 -08:00
int ret , cpu , closid = rdtgroup_default . closid ;
2016-10-28 15:04:44 -07:00
struct rdtgroup * rdtgrp ;
2016-11-18 15:18:04 -08:00
cpumask_var_t tmpmask ;
if ( ! zalloc_cpumask_var ( & tmpmask , GFP_KERNEL ) )
return - ENOMEM ;
2016-10-28 15:04:44 -07:00
rdtgrp = rdtgroup_kn_lock_live ( kn ) ;
if ( ! rdtgrp ) {
2016-11-18 15:18:04 -08:00
ret = - EPERM ;
goto out ;
2016-10-28 15:04:44 -07:00
}
2016-10-28 15:04:46 -07:00
/* Give any tasks back to the default group */
2016-11-18 15:18:04 -08:00
rdt_move_group_tasks ( rdtgrp , & rdtgroup_default , tmpmask ) ;
2016-10-28 15:04:46 -07:00
2016-10-28 15:04:45 -07:00
/* Give any CPUs back to the default group */
cpumask_or ( & rdtgroup_default . cpu_mask ,
& rdtgroup_default . cpu_mask , & rdtgrp - > cpu_mask ) ;
2016-11-18 15:18:04 -08:00
/* Update per cpu closid of the moved CPUs first */
for_each_cpu ( cpu , & rdtgrp - > cpu_mask )
per_cpu ( cpu_closid , cpu ) = closid ;
/*
* Update the MSR on moved CPUs and CPUs which have moved
* task running on them .
*/
cpumask_or ( tmpmask , tmpmask , & rdtgrp - > cpu_mask ) ;
rdt_update_closid ( tmpmask , NULL ) ;
2016-10-28 15:04:45 -07:00
2016-10-28 15:04:44 -07:00
rdtgrp - > flags = RDT_DELETED ;
closid_free ( rdtgrp - > closid ) ;
list_del ( & rdtgrp - > rdtgroup_list ) ;
/*
* one extra hold on this , will drop when we kfree ( rdtgrp )
* in rdtgroup_kn_unlock ( )
*/
kernfs_get ( kn ) ;
kernfs_remove ( rdtgrp - > kn ) ;
2016-11-18 15:18:04 -08:00
ret = 0 ;
out :
2016-10-28 15:04:44 -07:00
rdtgroup_kn_unlock ( kn ) ;
2016-11-18 15:18:04 -08:00
free_cpumask_var ( tmpmask ) ;
return ret ;
2016-10-28 15:04:44 -07:00
}
2016-12-02 14:21:06 -08:00
static int rdtgroup_show_options ( struct seq_file * seq , struct kernfs_root * kf )
{
2017-07-25 14:14:25 -07:00
if ( rdt_resources_all [ RDT_RESOURCE_L3DATA ] . alloc_enabled )
2016-12-02 14:21:06 -08:00
seq_puts ( seq , " ,cdp " ) ;
return 0 ;
}
2016-10-28 15:04:42 -07:00
static struct kernfs_syscall_ops rdtgroup_kf_syscall_ops = {
2016-12-02 14:21:06 -08:00
. mkdir = rdtgroup_mkdir ,
. rmdir = rdtgroup_rmdir ,
. show_options = rdtgroup_show_options ,
2016-10-28 15:04:42 -07:00
} ;
static int __init rdtgroup_setup_root ( void )
{
2016-10-28 15:04:45 -07:00
int ret ;
2016-10-28 15:04:42 -07:00
rdt_root = kernfs_create_root ( & rdtgroup_kf_syscall_ops ,
KERNFS_ROOT_CREATE_DEACTIVATED ,
& rdtgroup_default ) ;
if ( IS_ERR ( rdt_root ) )
return PTR_ERR ( rdt_root ) ;
mutex_lock ( & rdtgroup_mutex ) ;
rdtgroup_default . closid = 0 ;
2017-07-25 14:14:32 -07:00
rdtgroup_default . mon . rmid = 0 ;
rdtgroup_default . type = RDTCTRL_GROUP ;
INIT_LIST_HEAD ( & rdtgroup_default . mon . crdtgrp_list ) ;
2016-10-28 15:04:42 -07:00
list_add ( & rdtgroup_default . rdtgroup_list , & rdt_all_groups ) ;
2017-07-25 14:14:29 -07:00
ret = rdtgroup_add_files ( rdt_root - > kn , RF_CTRL_BASE ) ;
2016-10-28 15:04:45 -07:00
if ( ret ) {
kernfs_destroy_root ( rdt_root ) ;
goto out ;
}
2016-10-28 15:04:42 -07:00
rdtgroup_default . kn = rdt_root - > kn ;
kernfs_activate ( rdtgroup_default . kn ) ;
2016-10-28 15:04:45 -07:00
out :
2016-10-28 15:04:42 -07:00
mutex_unlock ( & rdtgroup_mutex ) ;
2016-10-28 15:04:45 -07:00
return ret ;
2016-10-28 15:04:42 -07:00
}
/*
* rdtgroup_init - rdtgroup initialization
*
* Setup resctrl file system including set up root , create mount point ,
* register rdtgroup filesystem , and initialize files under root directory .
*
* Return : 0 on success or - errno
*/
int __init rdtgroup_init ( void )
{
int ret = 0 ;
ret = rdtgroup_setup_root ( ) ;
if ( ret )
return ret ;
ret = sysfs_create_mount_point ( fs_kobj , " resctrl " ) ;
if ( ret )
goto cleanup_root ;
ret = register_filesystem ( & rdt_fs_type ) ;
if ( ret )
goto cleanup_mountpoint ;
return 0 ;
cleanup_mountpoint :
sysfs_remove_mount_point ( fs_kobj , " resctrl " ) ;
cleanup_root :
kernfs_destroy_root ( rdt_root ) ;
return ret ;
}