2021-08-18 13:40:12 -06:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( c ) 2020 Linaro Limited , All rights reserved .
* Author : Mike Leach < mike . leach @ linaro . org >
*/
# include <linux/platform_device.h>
2021-09-13 10:46:13 -06:00
# include <linux/slab.h>
2021-08-18 13:40:12 -06:00
# include "coresight-config.h"
2021-08-18 13:40:15 -06:00
# include "coresight-etm-perf.h"
2021-08-18 13:40:12 -06:00
# include "coresight-syscfg.h"
2021-08-18 13:40:20 -06:00
# include "coresight-syscfg-configfs.h"
2021-08-18 13:40:12 -06:00
/*
* cscfg_ API manages configurations and features for the entire coresight
* infrastructure .
*
* It allows the loading of configurations and features , and loads these into
* coresight devices as appropriate .
*/
/* protect the cscsg_data and device */
static DEFINE_MUTEX ( cscfg_mutex ) ;
/* only one of these */
static struct cscfg_manager * cscfg_mgr ;
/* load features and configuations into the lists */
2021-08-18 13:40:13 -06:00
/* get name feature instance from a coresight device list of features */
static struct cscfg_feature_csdev *
cscfg_get_feat_csdev ( struct coresight_device * csdev , const char * name )
{
struct cscfg_feature_csdev * feat_csdev = NULL ;
list_for_each_entry ( feat_csdev , & csdev - > feature_csdev_list , node ) {
if ( strcmp ( feat_csdev - > feat_desc - > name , name ) = = 0 )
return feat_csdev ;
}
return NULL ;
}
/* allocate the device config instance - with max number of used features */
static struct cscfg_config_csdev *
cscfg_alloc_csdev_cfg ( struct coresight_device * csdev , int nr_feats )
{
struct cscfg_config_csdev * config_csdev = NULL ;
struct device * dev = csdev - > dev . parent ;
/* this is being allocated using the devm for the coresight device */
config_csdev = devm_kzalloc ( dev ,
offsetof ( struct cscfg_config_csdev , feats_csdev [ nr_feats ] ) ,
GFP_KERNEL ) ;
if ( ! config_csdev )
return NULL ;
config_csdev - > csdev = csdev ;
return config_csdev ;
}
/* Load a config into a device if there are any feature matches between config and device */
static int cscfg_add_csdev_cfg ( struct coresight_device * csdev ,
struct cscfg_config_desc * config_desc )
{
struct cscfg_config_csdev * config_csdev = NULL ;
struct cscfg_feature_csdev * feat_csdev ;
unsigned long flags ;
int i ;
/* look at each required feature and see if it matches any feature on the device */
for ( i = 0 ; i < config_desc - > nr_feat_refs ; i + + ) {
/* look for a matching name */
feat_csdev = cscfg_get_feat_csdev ( csdev , config_desc - > feat_ref_names [ i ] ) ;
if ( feat_csdev ) {
/*
* At least one feature on this device matches the config
* add a config instance to the device and a reference to the feature .
*/
if ( ! config_csdev ) {
config_csdev = cscfg_alloc_csdev_cfg ( csdev ,
config_desc - > nr_feat_refs ) ;
if ( ! config_csdev )
return - ENOMEM ;
config_csdev - > config_desc = config_desc ;
}
config_csdev - > feats_csdev [ config_csdev - > nr_feat + + ] = feat_csdev ;
}
}
/* if matched features, add config to device.*/
if ( config_csdev ) {
spin_lock_irqsave ( & csdev - > cscfg_csdev_lock , flags ) ;
list_add ( & config_csdev - > node , & csdev - > config_csdev_list ) ;
spin_unlock_irqrestore ( & csdev - > cscfg_csdev_lock , flags ) ;
}
return 0 ;
}
/*
* Add the config to the set of registered devices - call with mutex locked .
* Iterates through devices - any device that matches one or more of the
* configuration features will load it , the others will ignore it .
*/
static int cscfg_add_cfg_to_csdevs ( struct cscfg_config_desc * config_desc )
{
struct cscfg_registered_csdev * csdev_item ;
int err ;
list_for_each_entry ( csdev_item , & cscfg_mgr - > csdev_desc_list , item ) {
err = cscfg_add_csdev_cfg ( csdev_item - > csdev , config_desc ) ;
if ( err )
return err ;
}
return 0 ;
}
/*
* Allocate a feature object for load into a csdev .
* memory allocated using the csdev - > dev object using devm managed allocator .
*/
static struct cscfg_feature_csdev *
cscfg_alloc_csdev_feat ( struct coresight_device * csdev , struct cscfg_feature_desc * feat_desc )
{
struct cscfg_feature_csdev * feat_csdev = NULL ;
struct device * dev = csdev - > dev . parent ;
int i ;
feat_csdev = devm_kzalloc ( dev , sizeof ( struct cscfg_feature_csdev ) , GFP_KERNEL ) ;
if ( ! feat_csdev )
return NULL ;
/* parameters are optional - could be 0 */
feat_csdev - > nr_params = feat_desc - > nr_params ;
/*
* if we need parameters , zero alloc the space here , the load routine in
* the csdev device driver will fill out some information according to
* feature descriptor .
*/
if ( feat_csdev - > nr_params ) {
feat_csdev - > params_csdev = devm_kcalloc ( dev , feat_csdev - > nr_params ,
sizeof ( struct cscfg_parameter_csdev ) ,
GFP_KERNEL ) ;
if ( ! feat_csdev - > params_csdev )
return NULL ;
/*
* fill in the feature reference in the param - other fields
* handled by loader in csdev .
*/
for ( i = 0 ; i < feat_csdev - > nr_params ; i + + )
feat_csdev - > params_csdev [ i ] . feat_csdev = feat_csdev ;
}
/*
* Always have registers to program - again the load routine in csdev device
* will fill out according to feature descriptor and device requirements .
*/
feat_csdev - > nr_regs = feat_desc - > nr_regs ;
feat_csdev - > regs_csdev = devm_kcalloc ( dev , feat_csdev - > nr_regs ,
sizeof ( struct cscfg_regval_csdev ) ,
GFP_KERNEL ) ;
if ( ! feat_csdev - > regs_csdev )
return NULL ;
/* load the feature default values */
feat_csdev - > feat_desc = feat_desc ;
feat_csdev - > csdev = csdev ;
return feat_csdev ;
}
/* load one feature into one coresight device */
static int cscfg_load_feat_csdev ( struct coresight_device * csdev ,
struct cscfg_feature_desc * feat_desc ,
struct cscfg_csdev_feat_ops * ops )
{
struct cscfg_feature_csdev * feat_csdev ;
unsigned long flags ;
int err ;
if ( ! ops - > load_feat )
return - EINVAL ;
feat_csdev = cscfg_alloc_csdev_feat ( csdev , feat_desc ) ;
if ( ! feat_csdev )
return - ENOMEM ;
/* load the feature into the device */
err = ops - > load_feat ( csdev , feat_csdev ) ;
if ( err )
return err ;
2021-08-18 13:40:14 -06:00
/* add to internal csdev feature list & initialise using reset call */
cscfg_reset_feat ( feat_csdev ) ;
2021-08-18 13:40:13 -06:00
spin_lock_irqsave ( & csdev - > cscfg_csdev_lock , flags ) ;
list_add ( & feat_csdev - > node , & csdev - > feature_csdev_list ) ;
spin_unlock_irqrestore ( & csdev - > cscfg_csdev_lock , flags ) ;
return 0 ;
}
/*
* Add feature to any matching devices - call with mutex locked .
* Iterates through devices - any device that matches the feature will be
* called to load it .
*/
static int cscfg_add_feat_to_csdevs ( struct cscfg_feature_desc * feat_desc )
{
struct cscfg_registered_csdev * csdev_item ;
int err ;
list_for_each_entry ( csdev_item , & cscfg_mgr - > csdev_desc_list , item ) {
if ( csdev_item - > match_flags & feat_desc - > match_flags ) {
err = cscfg_load_feat_csdev ( csdev_item - > csdev , feat_desc , & csdev_item - > ops ) ;
if ( err )
return err ;
}
}
return 0 ;
}
2021-08-18 13:40:12 -06:00
/* check feature list for a named feature - call with mutex locked. */
static bool cscfg_match_list_feat ( const char * name )
{
struct cscfg_feature_desc * feat_desc ;
list_for_each_entry ( feat_desc , & cscfg_mgr - > feat_desc_list , item ) {
if ( strcmp ( feat_desc - > name , name ) = = 0 )
return true ;
}
return false ;
}
/* check all feat needed for cfg are in the list - call with mutex locked. */
static int cscfg_check_feat_for_cfg ( struct cscfg_config_desc * config_desc )
{
int i ;
for ( i = 0 ; i < config_desc - > nr_feat_refs ; i + + )
if ( ! cscfg_match_list_feat ( config_desc - > feat_ref_names [ i ] ) )
return - EINVAL ;
return 0 ;
}
/*
* load feature - add to feature list .
*/
static int cscfg_load_feat ( struct cscfg_feature_desc * feat_desc )
{
2021-08-18 13:40:13 -06:00
int err ;
2021-11-24 20:00:34 +00:00
struct cscfg_feature_desc * feat_desc_exist ;
/* new feature must have unique name */
list_for_each_entry ( feat_desc_exist , & cscfg_mgr - > feat_desc_list , item ) {
if ( ! strcmp ( feat_desc_exist - > name , feat_desc - > name ) )
return - EEXIST ;
}
2021-08-18 13:40:13 -06:00
/* add feature to any matching registered devices */
err = cscfg_add_feat_to_csdevs ( feat_desc ) ;
if ( err )
return err ;
2021-08-18 13:40:12 -06:00
2021-08-18 13:40:13 -06:00
list_add ( & feat_desc - > item , & cscfg_mgr - > feat_desc_list ) ;
2021-08-18 13:40:12 -06:00
return 0 ;
}
/*
* load config into the system - validate used features exist then add to
* config list .
*/
static int cscfg_load_config ( struct cscfg_config_desc * config_desc )
{
int err ;
2021-11-24 20:00:34 +00:00
struct cscfg_config_desc * config_desc_exist ;
/* new configuration must have a unique name */
list_for_each_entry ( config_desc_exist , & cscfg_mgr - > config_desc_list , item ) {
if ( ! strcmp ( config_desc_exist - > name , config_desc - > name ) )
return - EEXIST ;
}
2021-08-18 13:40:12 -06:00
/* validate features are present */
err = cscfg_check_feat_for_cfg ( config_desc ) ;
if ( err )
return err ;
2021-08-18 13:40:13 -06:00
/* add config to any matching registered device */
err = cscfg_add_cfg_to_csdevs ( config_desc ) ;
if ( err )
return err ;
2021-08-18 13:40:15 -06:00
/* add config to perf fs to allow selection */
err = etm_perf_add_symlink_cscfg ( cscfg_device ( ) , config_desc ) ;
if ( err )
return err ;
2021-08-18 13:40:12 -06:00
list_add ( & config_desc - > item , & cscfg_mgr - > config_desc_list ) ;
2021-08-18 13:40:16 -06:00
atomic_set ( & config_desc - > active_cnt , 0 ) ;
2021-08-18 13:40:12 -06:00
return 0 ;
}
2021-08-18 13:40:20 -06:00
/* get a feature descriptor by name */
const struct cscfg_feature_desc * cscfg_get_named_feat_desc ( const char * name )
{
const struct cscfg_feature_desc * feat_desc = NULL , * feat_desc_item ;
mutex_lock ( & cscfg_mutex ) ;
list_for_each_entry ( feat_desc_item , & cscfg_mgr - > feat_desc_list , item ) {
if ( strcmp ( feat_desc_item - > name , name ) = = 0 ) {
feat_desc = feat_desc_item ;
break ;
}
}
mutex_unlock ( & cscfg_mutex ) ;
return feat_desc ;
}
/* called with cscfg_mutex held */
static struct cscfg_feature_csdev *
cscfg_csdev_get_feat_from_desc ( struct coresight_device * csdev ,
struct cscfg_feature_desc * feat_desc )
{
struct cscfg_feature_csdev * feat_csdev ;
list_for_each_entry ( feat_csdev , & csdev - > feature_csdev_list , node ) {
if ( feat_csdev - > feat_desc = = feat_desc )
return feat_csdev ;
}
return NULL ;
}
int cscfg_update_feat_param_val ( struct cscfg_feature_desc * feat_desc ,
int param_idx , u64 value )
{
int err = 0 ;
struct cscfg_feature_csdev * feat_csdev ;
struct cscfg_registered_csdev * csdev_item ;
mutex_lock ( & cscfg_mutex ) ;
/* check if any config active & return busy */
if ( atomic_read ( & cscfg_mgr - > sys_active_cnt ) ) {
err = - EBUSY ;
goto unlock_exit ;
}
/* set the value */
if ( ( param_idx < 0 ) | | ( param_idx > = feat_desc - > nr_params ) ) {
err = - EINVAL ;
goto unlock_exit ;
}
feat_desc - > params_desc [ param_idx ] . value = value ;
/* update loaded instances.*/
list_for_each_entry ( csdev_item , & cscfg_mgr - > csdev_desc_list , item ) {
feat_csdev = cscfg_csdev_get_feat_from_desc ( csdev_item - > csdev , feat_desc ) ;
if ( feat_csdev )
feat_csdev - > params_csdev [ param_idx ] . current_value = value ;
}
unlock_exit :
mutex_unlock ( & cscfg_mutex ) ;
return err ;
}
2021-11-24 20:00:35 +00:00
/*
* Conditionally up reference count on owner to prevent unload .
*
* module loaded configs need to be locked in to prevent premature unload .
*/
static int cscfg_owner_get ( struct cscfg_load_owner_info * owner_info )
{
if ( ( owner_info - > type = = CSCFG_OWNER_MODULE ) & &
( ! try_module_get ( owner_info - > owner_handle ) ) )
return - EINVAL ;
return 0 ;
}
/* conditionally lower ref count on an owner */
static void cscfg_owner_put ( struct cscfg_load_owner_info * owner_info )
{
if ( owner_info - > type = = CSCFG_OWNER_MODULE )
module_put ( owner_info - > owner_handle ) ;
}
2021-11-24 20:00:34 +00:00
static void cscfg_remove_owned_csdev_configs ( struct coresight_device * csdev , void * load_owner )
{
struct cscfg_config_csdev * config_csdev , * tmp ;
if ( list_empty ( & csdev - > config_csdev_list ) )
return ;
list_for_each_entry_safe ( config_csdev , tmp , & csdev - > config_csdev_list , node ) {
if ( config_csdev - > config_desc - > load_owner = = load_owner )
list_del ( & config_csdev - > node ) ;
}
}
static void cscfg_remove_owned_csdev_features ( struct coresight_device * csdev , void * load_owner )
{
struct cscfg_feature_csdev * feat_csdev , * tmp ;
if ( list_empty ( & csdev - > feature_csdev_list ) )
return ;
list_for_each_entry_safe ( feat_csdev , tmp , & csdev - > feature_csdev_list , node ) {
if ( feat_csdev - > feat_desc - > load_owner = = load_owner )
list_del ( & feat_csdev - > node ) ;
}
}
2022-06-28 18:30:03 +01:00
/*
* Unregister all configuration and features from configfs owned by load_owner .
* Although this is called without the list mutex being held , it is in the
* context of an unload operation which are strictly serialised ,
* so the lists cannot change during this call .
*/
static void cscfg_fs_unregister_cfgs_feats ( void * load_owner )
{
struct cscfg_config_desc * config_desc ;
struct cscfg_feature_desc * feat_desc ;
list_for_each_entry ( config_desc , & cscfg_mgr - > config_desc_list , item ) {
if ( config_desc - > load_owner = = load_owner )
cscfg_configfs_del_config ( config_desc ) ;
}
list_for_each_entry ( feat_desc , & cscfg_mgr - > feat_desc_list , item ) {
if ( feat_desc - > load_owner = = load_owner )
cscfg_configfs_del_feature ( feat_desc ) ;
}
}
2021-11-24 20:00:34 +00:00
/*
* removal is relatively easy - just remove from all lists , anything that
* matches the owner . Memory for the descriptors will be managed by the owner ,
* memory for the csdev items is devm_ allocated with the individual csdev
* devices .
*/
static void cscfg_unload_owned_cfgs_feats ( void * load_owner )
{
struct cscfg_config_desc * config_desc , * cfg_tmp ;
struct cscfg_feature_desc * feat_desc , * feat_tmp ;
struct cscfg_registered_csdev * csdev_item ;
2022-06-28 18:30:04 +01:00
lockdep_assert_held ( & cscfg_mutex ) ;
2021-11-24 20:00:34 +00:00
/* remove from each csdev instance feature and config lists */
list_for_each_entry ( csdev_item , & cscfg_mgr - > csdev_desc_list , item ) {
/*
* for each csdev , check the loaded lists and remove if
* referenced descriptor is owned
*/
cscfg_remove_owned_csdev_configs ( csdev_item - > csdev , load_owner ) ;
cscfg_remove_owned_csdev_features ( csdev_item - > csdev , load_owner ) ;
}
/* remove from the config descriptor lists */
list_for_each_entry_safe ( config_desc , cfg_tmp , & cscfg_mgr - > config_desc_list , item ) {
if ( config_desc - > load_owner = = load_owner ) {
etm_perf_del_symlink_cscfg ( config_desc ) ;
list_del ( & config_desc - > item ) ;
}
}
/* remove from the feature descriptor lists */
list_for_each_entry_safe ( feat_desc , feat_tmp , & cscfg_mgr - > feat_desc_list , item ) {
if ( feat_desc - > load_owner = = load_owner ) {
list_del ( & feat_desc - > item ) ;
}
}
}
2022-06-28 18:30:04 +01:00
/*
* load the features and configs to the lists - called with list mutex held
*/
static int cscfg_load_owned_cfgs_feats ( struct cscfg_config_desc * * config_descs ,
struct cscfg_feature_desc * * feat_descs ,
struct cscfg_load_owner_info * owner_info )
{
int i , err ;
lockdep_assert_held ( & cscfg_mutex ) ;
/* load features first */
if ( feat_descs ) {
for ( i = 0 ; feat_descs [ i ] ; i + + ) {
err = cscfg_load_feat ( feat_descs [ i ] ) ;
if ( err ) {
pr_err ( " coresight-syscfg: Failed to load feature %s \n " ,
feat_descs [ i ] - > name ) ;
return err ;
}
feat_descs [ i ] - > load_owner = owner_info ;
}
}
/* next any configurations to check feature dependencies */
if ( config_descs ) {
for ( i = 0 ; config_descs [ i ] ; i + + ) {
err = cscfg_load_config ( config_descs [ i ] ) ;
if ( err ) {
pr_err ( " coresight-syscfg: Failed to load configuration %s \n " ,
config_descs [ i ] - > name ) ;
return err ;
}
config_descs [ i ] - > load_owner = owner_info ;
config_descs [ i ] - > available = false ;
}
}
return 0 ;
}
/* set configurations as available to activate at the end of the load process */
static void cscfg_set_configs_available ( struct cscfg_config_desc * * config_descs )
{
int i ;
lockdep_assert_held ( & cscfg_mutex ) ;
if ( config_descs ) {
for ( i = 0 ; config_descs [ i ] ; i + + )
config_descs [ i ] - > available = true ;
}
}
/*
* Create and register each of the configurations and features with configfs .
* Called without mutex being held .
*/
static int cscfg_fs_register_cfgs_feats ( struct cscfg_config_desc * * config_descs ,
struct cscfg_feature_desc * * feat_descs )
{
int i , err ;
if ( feat_descs ) {
for ( i = 0 ; feat_descs [ i ] ; i + + ) {
err = cscfg_configfs_add_feature ( feat_descs [ i ] ) ;
if ( err )
return err ;
}
}
if ( config_descs ) {
for ( i = 0 ; config_descs [ i ] ; i + + ) {
err = cscfg_configfs_add_config ( config_descs [ i ] ) ;
if ( err )
return err ;
}
}
return 0 ;
}
2021-08-18 13:40:12 -06:00
/**
* cscfg_load_config_sets - API function to load feature and config sets .
*
* Take a 0 terminated array of feature descriptors and / or configuration
* descriptors and load into the system .
* Features are loaded first to ensure configuration dependencies can be met .
*
2021-11-24 20:00:33 +00:00
* To facilitate dynamic loading and unloading , features and configurations
* have a " load_owner " , to allow later unload by the same owner . An owner may
* be a loadable module or configuration dynamically created via configfs .
* As later loaded configurations can use earlier loaded features , creating load
* dependencies , a load order list is maintained . Unload is strictly in the
* reverse order to load .
*
2021-08-18 13:40:12 -06:00
* @ config_descs : 0 terminated array of configuration descriptors .
* @ feat_descs : 0 terminated array of feature descriptors .
2021-11-24 20:00:33 +00:00
* @ owner_info : Information on the owner of this set .
2021-08-18 13:40:12 -06:00
*/
int cscfg_load_config_sets ( struct cscfg_config_desc * * config_descs ,
2021-11-24 20:00:33 +00:00
struct cscfg_feature_desc * * feat_descs ,
struct cscfg_load_owner_info * owner_info )
2021-08-18 13:40:12 -06:00
{
2022-06-28 18:30:04 +01:00
int err = 0 ;
2021-08-18 13:40:12 -06:00
mutex_lock ( & cscfg_mutex ) ;
2022-06-28 18:30:04 +01:00
if ( cscfg_mgr - > load_state ! = CSCFG_NONE ) {
mutex_unlock ( & cscfg_mutex ) ;
return - EBUSY ;
2021-08-18 13:40:12 -06:00
}
2022-06-28 18:30:04 +01:00
cscfg_mgr - > load_state = CSCFG_LOAD ;
2021-08-18 13:40:12 -06:00
2022-06-28 18:30:04 +01:00
/* first load and add to the lists */
err = cscfg_load_owned_cfgs_feats ( config_descs , feat_descs , owner_info ) ;
if ( err )
goto err_clean_load ;
2021-08-18 13:40:12 -06:00
2021-11-24 20:00:33 +00:00
/* add the load owner to the load order list */
list_add_tail ( & owner_info - > item , & cscfg_mgr - > load_order_list ) ;
2021-11-24 20:00:35 +00:00
if ( ! list_is_singular ( & cscfg_mgr - > load_order_list ) ) {
/* lock previous item in load order list */
err = cscfg_owner_get ( list_prev_entry ( owner_info , item ) ) ;
2022-06-28 18:30:04 +01:00
if ( err )
goto err_clean_owner_list ;
2021-11-24 20:00:35 +00:00
}
2021-11-24 20:00:33 +00:00
2022-06-28 18:30:04 +01:00
/*
* make visible to configfs - configfs manipulation must occur outside
* the list mutex lock to avoid circular lockdep issues with configfs
* built in mutexes and semaphores . This is safe as it is not possible
* to start a new load / unload operation till the current one is done .
*/
mutex_unlock ( & cscfg_mutex ) ;
/* create the configfs elements */
err = cscfg_fs_register_cfgs_feats ( config_descs , feat_descs ) ;
mutex_lock ( & cscfg_mutex ) ;
if ( err )
goto err_clean_cfs ;
/* mark any new configs as available for activation */
cscfg_set_configs_available ( config_descs ) ;
goto exit_unlock ;
err_clean_cfs :
/* cleanup after error registering with configfs */
cscfg_fs_unregister_cfgs_feats ( owner_info ) ;
if ( ! list_is_singular ( & cscfg_mgr - > load_order_list ) )
cscfg_owner_put ( list_prev_entry ( owner_info , item ) ) ;
err_clean_owner_list :
list_del ( & owner_info - > item ) ;
err_clean_load :
cscfg_unload_owned_cfgs_feats ( owner_info ) ;
2021-08-18 13:40:12 -06:00
exit_unlock :
2022-06-28 18:30:04 +01:00
cscfg_mgr - > load_state = CSCFG_NONE ;
2021-08-18 13:40:12 -06:00
mutex_unlock ( & cscfg_mutex ) ;
return err ;
}
EXPORT_SYMBOL_GPL ( cscfg_load_config_sets ) ;
2021-11-24 20:00:34 +00:00
/**
* cscfg_unload_config_sets - unload a set of configurations by owner .
*
* Dynamic unload of configuration and feature sets is done on the basis of
* the load owner of that set . Later loaded configurations can depend on
* features loaded earlier .
*
* Therefore , unload is only possible if : -
* 1 ) no configurations are active .
* 2 ) the set being unloaded was the last to be loaded to maintain dependencies .
*
2022-06-28 18:30:04 +01:00
* Once the unload operation commences , we disallow any configuration being
* made active until it is complete .
*
2021-11-24 20:00:34 +00:00
* @ owner_info : Information on owner for set being unloaded .
*/
int cscfg_unload_config_sets ( struct cscfg_load_owner_info * owner_info )
{
int err = 0 ;
struct cscfg_load_owner_info * load_list_item = NULL ;
mutex_lock ( & cscfg_mutex ) ;
2022-06-28 18:30:04 +01:00
if ( cscfg_mgr - > load_state ! = CSCFG_NONE ) {
mutex_unlock ( & cscfg_mutex ) ;
return - EBUSY ;
}
/* unload op in progress also prevents activation of any config */
cscfg_mgr - > load_state = CSCFG_UNLOAD ;
2021-11-24 20:00:34 +00:00
/* cannot unload if anything is active */
if ( atomic_read ( & cscfg_mgr - > sys_active_cnt ) ) {
err = - EBUSY ;
goto exit_unlock ;
}
/* cannot unload if not last loaded in load order */
if ( ! list_empty ( & cscfg_mgr - > load_order_list ) ) {
load_list_item = list_last_entry ( & cscfg_mgr - > load_order_list ,
struct cscfg_load_owner_info , item ) ;
if ( load_list_item ! = owner_info )
load_list_item = NULL ;
}
if ( ! load_list_item ) {
err = - EINVAL ;
goto exit_unlock ;
}
2022-06-28 18:30:04 +01:00
/* remove from configfs - again outside the scope of the list mutex */
mutex_unlock ( & cscfg_mutex ) ;
cscfg_fs_unregister_cfgs_feats ( owner_info ) ;
mutex_lock ( & cscfg_mutex ) ;
/* unload everything from lists belonging to load_owner */
2021-11-24 20:00:34 +00:00
cscfg_unload_owned_cfgs_feats ( owner_info ) ;
/* remove from load order list */
2021-11-24 20:00:35 +00:00
if ( ! list_is_singular ( & cscfg_mgr - > load_order_list ) ) {
/* unlock previous item in load order list */
cscfg_owner_put ( list_prev_entry ( owner_info , item ) ) ;
}
list_del ( & owner_info - > item ) ;
2021-11-24 20:00:34 +00:00
exit_unlock :
2022-06-28 18:30:04 +01:00
cscfg_mgr - > load_state = CSCFG_NONE ;
2021-11-24 20:00:34 +00:00
mutex_unlock ( & cscfg_mutex ) ;
return err ;
}
EXPORT_SYMBOL_GPL ( cscfg_unload_config_sets ) ;
2021-08-18 13:40:13 -06:00
/* Handle coresight device registration and add configs and features to devices */
/* iterate through config lists and load matching configs to device */
static int cscfg_add_cfgs_csdev ( struct coresight_device * csdev )
{
struct cscfg_config_desc * config_desc ;
int err = 0 ;
list_for_each_entry ( config_desc , & cscfg_mgr - > config_desc_list , item ) {
err = cscfg_add_csdev_cfg ( csdev , config_desc ) ;
if ( err )
break ;
}
return err ;
}
/* iterate through feature lists and load matching features to device */
static int cscfg_add_feats_csdev ( struct coresight_device * csdev ,
u32 match_flags ,
struct cscfg_csdev_feat_ops * ops )
{
struct cscfg_feature_desc * feat_desc ;
int err = 0 ;
if ( ! ops - > load_feat )
return - EINVAL ;
list_for_each_entry ( feat_desc , & cscfg_mgr - > feat_desc_list , item ) {
if ( feat_desc - > match_flags & match_flags ) {
err = cscfg_load_feat_csdev ( csdev , feat_desc , ops ) ;
if ( err )
break ;
}
}
return err ;
}
/* Add coresight device to list and copy its matching info */
static int cscfg_list_add_csdev ( struct coresight_device * csdev ,
u32 match_flags ,
struct cscfg_csdev_feat_ops * ops )
{
struct cscfg_registered_csdev * csdev_item ;
/* allocate the list entry structure */
csdev_item = kzalloc ( sizeof ( struct cscfg_registered_csdev ) , GFP_KERNEL ) ;
if ( ! csdev_item )
return - ENOMEM ;
csdev_item - > csdev = csdev ;
csdev_item - > match_flags = match_flags ;
csdev_item - > ops . load_feat = ops - > load_feat ;
list_add ( & csdev_item - > item , & cscfg_mgr - > csdev_desc_list ) ;
INIT_LIST_HEAD ( & csdev - > feature_csdev_list ) ;
INIT_LIST_HEAD ( & csdev - > config_csdev_list ) ;
spin_lock_init ( & csdev - > cscfg_csdev_lock ) ;
return 0 ;
}
/* remove a coresight device from the list and free data */
static void cscfg_list_remove_csdev ( struct coresight_device * csdev )
{
struct cscfg_registered_csdev * csdev_item , * tmp ;
list_for_each_entry_safe ( csdev_item , tmp , & cscfg_mgr - > csdev_desc_list , item ) {
if ( csdev_item - > csdev = = csdev ) {
list_del ( & csdev_item - > item ) ;
kfree ( csdev_item ) ;
break ;
}
}
}
/**
* cscfg_register_csdev - register a coresight device with the syscfg manager .
*
* Registers the coresight device with the system . @ match_flags used to check
* if the device is a match for registered features . Any currently registered
* configurations and features that match the device will be loaded onto it .
*
* @ csdev : The coresight device to register .
* @ match_flags : Matching information to load features .
* @ ops : Standard operations supported by the device .
*/
int cscfg_register_csdev ( struct coresight_device * csdev ,
u32 match_flags ,
struct cscfg_csdev_feat_ops * ops )
{
int ret = 0 ;
mutex_lock ( & cscfg_mutex ) ;
/* add device to list of registered devices */
ret = cscfg_list_add_csdev ( csdev , match_flags , ops ) ;
if ( ret )
goto reg_csdev_unlock ;
/* now load any registered features and configs matching the device. */
ret = cscfg_add_feats_csdev ( csdev , match_flags , ops ) ;
if ( ret ) {
cscfg_list_remove_csdev ( csdev ) ;
goto reg_csdev_unlock ;
}
ret = cscfg_add_cfgs_csdev ( csdev ) ;
if ( ret ) {
cscfg_list_remove_csdev ( csdev ) ;
goto reg_csdev_unlock ;
}
pr_info ( " CSCFG registered %s " , dev_name ( & csdev - > dev ) ) ;
reg_csdev_unlock :
mutex_unlock ( & cscfg_mutex ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( cscfg_register_csdev ) ;
/**
* cscfg_unregister_csdev - remove coresight device from syscfg manager .
*
* @ csdev : Device to remove .
*/
void cscfg_unregister_csdev ( struct coresight_device * csdev )
{
mutex_lock ( & cscfg_mutex ) ;
cscfg_list_remove_csdev ( csdev ) ;
mutex_unlock ( & cscfg_mutex ) ;
}
EXPORT_SYMBOL_GPL ( cscfg_unregister_csdev ) ;
2021-08-18 13:40:16 -06:00
/**
* cscfg_csdev_reset_feats - reset features for a CoreSight device .
*
* Resets all parameters and register values for any features loaded
* into @ csdev to their default values .
*
* @ csdev : The CoreSight device .
*/
void cscfg_csdev_reset_feats ( struct coresight_device * csdev )
{
struct cscfg_feature_csdev * feat_csdev ;
unsigned long flags ;
spin_lock_irqsave ( & csdev - > cscfg_csdev_lock , flags ) ;
if ( list_empty ( & csdev - > feature_csdev_list ) )
goto unlock_exit ;
list_for_each_entry ( feat_csdev , & csdev - > feature_csdev_list , node )
cscfg_reset_feat ( feat_csdev ) ;
unlock_exit :
spin_unlock_irqrestore ( & csdev - > cscfg_csdev_lock , flags ) ;
}
EXPORT_SYMBOL_GPL ( cscfg_csdev_reset_feats ) ;
2021-11-24 20:00:37 +00:00
/*
* This activate configuration for either perf or sysfs . Perf can have multiple
* active configs , selected per event , sysfs is limited to one .
2021-08-18 13:40:16 -06:00
*
* Increments the configuration descriptor active count and the global active
* count .
*
* @ cfg_hash : Hash value of the selected configuration name .
*/
2021-11-24 20:00:37 +00:00
static int _cscfg_activate_config ( unsigned long cfg_hash )
2021-08-18 13:40:16 -06:00
{
struct cscfg_config_desc * config_desc ;
int err = - EINVAL ;
2022-06-28 18:30:04 +01:00
if ( cscfg_mgr - > load_state = = CSCFG_UNLOAD )
return - EBUSY ;
2021-08-18 13:40:16 -06:00
list_for_each_entry ( config_desc , & cscfg_mgr - > config_desc_list , item ) {
if ( ( unsigned long ) config_desc - > event_ea - > var = = cfg_hash ) {
2022-06-28 18:30:04 +01:00
/* if we happen upon a partly loaded config, can't use it */
if ( config_desc - > available = = false )
return - EBUSY ;
2021-11-24 20:00:35 +00:00
/* must ensure that config cannot be unloaded in use */
err = cscfg_owner_get ( config_desc - > load_owner ) ;
if ( err )
break ;
2021-08-18 13:40:16 -06:00
/*
* increment the global active count - control changes to
* active configurations
*/
atomic_inc ( & cscfg_mgr - > sys_active_cnt ) ;
/*
* mark the descriptor as active so enable config on a
* device instance will use it
*/
atomic_inc ( & config_desc - > active_cnt ) ;
err = 0 ;
dev_dbg ( cscfg_device ( ) , " Activate config %s. \n " , config_desc - > name ) ;
break ;
}
}
2021-11-24 20:00:37 +00:00
return err ;
}
static void _cscfg_deactivate_config ( unsigned long cfg_hash )
{
struct cscfg_config_desc * config_desc ;
list_for_each_entry ( config_desc , & cscfg_mgr - > config_desc_list , item ) {
if ( ( unsigned long ) config_desc - > event_ea - > var = = cfg_hash ) {
atomic_dec ( & config_desc - > active_cnt ) ;
atomic_dec ( & cscfg_mgr - > sys_active_cnt ) ;
cscfg_owner_put ( config_desc - > load_owner ) ;
dev_dbg ( cscfg_device ( ) , " Deactivate config %s. \n " , config_desc - > name ) ;
break ;
}
}
}
/*
* called from configfs to set / clear the active configuration for use when
* using sysfs to control trace .
*/
int cscfg_config_sysfs_activate ( struct cscfg_config_desc * config_desc , bool activate )
{
unsigned long cfg_hash ;
int err = 0 ;
mutex_lock ( & cscfg_mutex ) ;
cfg_hash = ( unsigned long ) config_desc - > event_ea - > var ;
if ( activate ) {
/* cannot be a current active value to activate this */
if ( cscfg_mgr - > sysfs_active_config ) {
err = - EBUSY ;
goto exit_unlock ;
}
err = _cscfg_activate_config ( cfg_hash ) ;
if ( ! err )
cscfg_mgr - > sysfs_active_config = cfg_hash ;
} else {
/* disable if matching current value */
if ( cscfg_mgr - > sysfs_active_config = = cfg_hash ) {
_cscfg_deactivate_config ( cfg_hash ) ;
cscfg_mgr - > sysfs_active_config = 0 ;
} else
err = - EINVAL ;
}
exit_unlock :
mutex_unlock ( & cscfg_mutex ) ;
return err ;
}
/* set the sysfs preset value */
void cscfg_config_sysfs_set_preset ( int preset )
{
mutex_lock ( & cscfg_mutex ) ;
cscfg_mgr - > sysfs_active_preset = preset ;
mutex_unlock ( & cscfg_mutex ) ;
}
/*
* Used by a device to get the config and preset selected as active in configfs ,
* when using sysfs to control trace .
*/
void cscfg_config_sysfs_get_active_cfg ( unsigned long * cfg_hash , int * preset )
{
mutex_lock ( & cscfg_mutex ) ;
* preset = cscfg_mgr - > sysfs_active_preset ;
* cfg_hash = cscfg_mgr - > sysfs_active_config ;
mutex_unlock ( & cscfg_mutex ) ;
}
EXPORT_SYMBOL_GPL ( cscfg_config_sysfs_get_active_cfg ) ;
/**
* cscfg_activate_config - Mark a configuration descriptor as active .
*
* This will be seen when csdev devices are enabled in the system .
* Only activated configurations can be enabled on individual devices .
* Activation protects the configuration from alteration or removal while
* active .
*
* Selection by hash value - generated from the configuration name when it
* was loaded and added to the cs_etm / configurations file system for selection
* by perf .
*
* @ cfg_hash : Hash value of the selected configuration name .
*/
int cscfg_activate_config ( unsigned long cfg_hash )
{
int err = 0 ;
mutex_lock ( & cscfg_mutex ) ;
err = _cscfg_activate_config ( cfg_hash ) ;
2021-08-18 13:40:16 -06:00
mutex_unlock ( & cscfg_mutex ) ;
return err ;
}
EXPORT_SYMBOL_GPL ( cscfg_activate_config ) ;
/**
* cscfg_deactivate_config - Mark a config descriptor as inactive .
*
* Decrement the configuration and global active counts .
*
* @ cfg_hash : Hash value of the selected configuration name .
*/
void cscfg_deactivate_config ( unsigned long cfg_hash )
{
mutex_lock ( & cscfg_mutex ) ;
2021-11-24 20:00:37 +00:00
_cscfg_deactivate_config ( cfg_hash ) ;
2021-08-18 13:40:16 -06:00
mutex_unlock ( & cscfg_mutex ) ;
}
EXPORT_SYMBOL_GPL ( cscfg_deactivate_config ) ;
/**
* cscfg_csdev_enable_active_config - Enable matching active configuration for device .
*
* Enables the configuration selected by @ cfg_hash if the configuration is supported
* on the device and has been activated .
*
* If active and supported the CoreSight device @ csdev will be programmed with the
* configuration , using @ preset parameters .
*
* Should be called before driver hardware enable for the requested device , prior to
* programming and enabling the physical hardware .
*
* @ csdev : CoreSight device to program .
* @ cfg_hash : Selector for the configuration .
* @ preset : Preset parameter values to use , 0 for current / default values .
*/
int cscfg_csdev_enable_active_config ( struct coresight_device * csdev ,
unsigned long cfg_hash , int preset )
{
struct cscfg_config_csdev * config_csdev_active = NULL , * config_csdev_item ;
const struct cscfg_config_desc * config_desc ;
unsigned long flags ;
int err = 0 ;
/* quickly check global count */
if ( ! atomic_read ( & cscfg_mgr - > sys_active_cnt ) )
return 0 ;
/*
* Look for matching configuration - set the active configuration
* context if found .
*/
spin_lock_irqsave ( & csdev - > cscfg_csdev_lock , flags ) ;
list_for_each_entry ( config_csdev_item , & csdev - > config_csdev_list , node ) {
config_desc = config_csdev_item - > config_desc ;
if ( ( atomic_read ( & config_desc - > active_cnt ) ) & &
( ( unsigned long ) config_desc - > event_ea - > var = = cfg_hash ) ) {
config_csdev_active = config_csdev_item ;
csdev - > active_cscfg_ctxt = ( void * ) config_csdev_active ;
break ;
}
}
spin_unlock_irqrestore ( & csdev - > cscfg_csdev_lock , flags ) ;
/*
* If found , attempt to enable
*/
if ( config_csdev_active ) {
/*
* Call the generic routine that will program up the internal
* driver structures prior to programming up the hardware .
* This routine takes the driver spinlock saved in the configs .
*/
err = cscfg_csdev_enable_config ( config_csdev_active , preset ) ;
if ( ! err ) {
/*
* Successful programming . Check the active_cscfg_ctxt
* pointer to ensure no pre - emption disabled it via
* cscfg_csdev_disable_active_config ( ) before
* we could start .
*
* Set enabled if OK , err if not .
*/
spin_lock_irqsave ( & csdev - > cscfg_csdev_lock , flags ) ;
if ( csdev - > active_cscfg_ctxt )
config_csdev_active - > enabled = true ;
else
err = - EBUSY ;
spin_unlock_irqrestore ( & csdev - > cscfg_csdev_lock , flags ) ;
}
}
return err ;
}
EXPORT_SYMBOL_GPL ( cscfg_csdev_enable_active_config ) ;
/**
* cscfg_csdev_disable_active_config - disable an active config on the device .
*
* Disables the active configuration on the CoreSight device @ csdev .
* Disable will save the values of any registers marked in the configurations
* as save on disable .
*
* Should be called after driver hardware disable for the requested device ,
* after disabling the physical hardware and reading back registers .
*
* @ csdev : The CoreSight device .
*/
void cscfg_csdev_disable_active_config ( struct coresight_device * csdev )
{
struct cscfg_config_csdev * config_csdev ;
unsigned long flags ;
/*
* Check if we have an active config , and that it was successfully enabled .
* If it was not enabled , we have no work to do , otherwise mark as disabled .
* Clear the active config pointer .
*/
spin_lock_irqsave ( & csdev - > cscfg_csdev_lock , flags ) ;
config_csdev = ( struct cscfg_config_csdev * ) csdev - > active_cscfg_ctxt ;
if ( config_csdev ) {
if ( ! config_csdev - > enabled )
config_csdev = NULL ;
else
config_csdev - > enabled = false ;
}
csdev - > active_cscfg_ctxt = NULL ;
spin_unlock_irqrestore ( & csdev - > cscfg_csdev_lock , flags ) ;
/* true if there was an enabled active config */
if ( config_csdev )
cscfg_csdev_disable_config ( config_csdev ) ;
}
EXPORT_SYMBOL_GPL ( cscfg_csdev_disable_active_config ) ;
2021-08-18 13:40:12 -06:00
/* Initialise system configuration management device. */
struct device * cscfg_device ( void )
{
return cscfg_mgr ? & cscfg_mgr - > dev : NULL ;
}
/* Must have a release function or the kernel will complain on module unload */
static void cscfg_dev_release ( struct device * dev )
{
2022-06-28 18:30:03 +01:00
mutex_lock ( & cscfg_mutex ) ;
2021-08-18 13:40:12 -06:00
kfree ( cscfg_mgr ) ;
cscfg_mgr = NULL ;
2022-06-28 18:30:03 +01:00
mutex_unlock ( & cscfg_mutex ) ;
2021-08-18 13:40:12 -06:00
}
/* a device is needed to "own" some kernel elements such as sysfs entries. */
static int cscfg_create_device ( void )
{
struct device * dev ;
int err = - ENOMEM ;
mutex_lock ( & cscfg_mutex ) ;
if ( cscfg_mgr ) {
err = - EINVAL ;
goto create_dev_exit_unlock ;
}
cscfg_mgr = kzalloc ( sizeof ( struct cscfg_manager ) , GFP_KERNEL ) ;
if ( ! cscfg_mgr )
goto create_dev_exit_unlock ;
2022-06-28 18:30:03 +01:00
/* initialise the cscfg_mgr structure */
INIT_LIST_HEAD ( & cscfg_mgr - > csdev_desc_list ) ;
INIT_LIST_HEAD ( & cscfg_mgr - > feat_desc_list ) ;
INIT_LIST_HEAD ( & cscfg_mgr - > config_desc_list ) ;
INIT_LIST_HEAD ( & cscfg_mgr - > load_order_list ) ;
atomic_set ( & cscfg_mgr - > sys_active_cnt , 0 ) ;
2022-06-28 18:30:04 +01:00
cscfg_mgr - > load_state = CSCFG_NONE ;
2022-06-28 18:30:03 +01:00
2021-08-18 13:40:12 -06:00
/* setup the device */
dev = cscfg_device ( ) ;
dev - > release = cscfg_dev_release ;
dev - > init_name = " cs_system_cfg " ;
err = device_register ( dev ) ;
if ( err )
2022-01-24 12:41:21 +00:00
put_device ( dev ) ;
2021-08-18 13:40:12 -06:00
create_dev_exit_unlock :
mutex_unlock ( & cscfg_mutex ) ;
return err ;
}
2022-06-28 18:30:03 +01:00
/*
* Loading and unloading is generally on user discretion .
* If exiting due to coresight module unload , we need to unload any configurations that remain ,
* before we unregister the configfs intrastructure .
*
* Do this by walking the load_owner list and taking appropriate action , depending on the load
* owner type .
*/
static void cscfg_unload_cfgs_on_exit ( void )
2021-08-18 13:40:12 -06:00
{
2022-06-28 18:30:03 +01:00
struct cscfg_load_owner_info * owner_info = NULL ;
2021-08-18 13:40:15 -06:00
2022-06-28 18:30:03 +01:00
/*
* grab the mutex - even though we are exiting , some configfs files
* may still be live till we dump them , so ensure list data is
* protected from a race condition .
*/
2021-08-18 13:40:12 -06:00
mutex_lock ( & cscfg_mutex ) ;
2022-06-28 18:30:03 +01:00
while ( ! list_empty ( & cscfg_mgr - > load_order_list ) ) {
/* remove in reverse order of loading */
owner_info = list_last_entry ( & cscfg_mgr - > load_order_list ,
struct cscfg_load_owner_info , item ) ;
/* action according to type */
switch ( owner_info - > type ) {
case CSCFG_OWNER_PRELOAD :
/*
* preloaded descriptors are statically allocated in
* this module - just need to unload dynamic items from
* csdev lists , and remove from configfs directories .
*/
pr_info ( " cscfg: unloading preloaded configurations \n " ) ;
break ;
case CSCFG_OWNER_MODULE :
/*
* this is an error - the loadable module must have been unloaded prior
* to the coresight module unload . Therefore that module has not
* correctly unloaded configs in its own exit code .
* Nothing to do other than emit an error string as the static descriptor
* references we need to unload will have disappeared with the module .
*/
pr_err ( " cscfg: ERROR: prior module failed to unload configuration \n " ) ;
goto list_remove ;
}
/* remove from configfs - outside the scope of the list mutex */
mutex_unlock ( & cscfg_mutex ) ;
cscfg_fs_unregister_cfgs_feats ( owner_info ) ;
mutex_lock ( & cscfg_mutex ) ;
/* Next unload from csdev lists. */
cscfg_unload_owned_cfgs_feats ( owner_info ) ;
list_remove :
/* remove from load order list */
list_del ( & owner_info - > item ) ;
2021-08-18 13:40:15 -06:00
}
2022-06-28 18:30:03 +01:00
mutex_unlock ( & cscfg_mutex ) ;
}
static void cscfg_clear_device ( void )
{
cscfg_unload_cfgs_on_exit ( ) ;
2021-08-18 13:40:20 -06:00
cscfg_configfs_release ( cscfg_mgr ) ;
2021-08-18 13:40:12 -06:00
device_unregister ( cscfg_device ( ) ) ;
}
/* Initialise system config management API device */
int __init cscfg_init ( void )
{
int err = 0 ;
2022-06-28 18:30:03 +01:00
/* create the device and init cscfg_mgr */
2021-08-18 13:40:12 -06:00
err = cscfg_create_device ( ) ;
if ( err )
return err ;
2022-06-28 18:30:03 +01:00
/* initialise configfs subsystem */
2021-08-18 13:40:20 -06:00
err = cscfg_configfs_init ( cscfg_mgr ) ;
if ( err )
goto exit_err ;
2021-08-18 13:40:19 -06:00
/* preload built-in configurations */
2021-11-24 20:00:33 +00:00
err = cscfg_preload ( THIS_MODULE ) ;
2021-08-18 13:40:19 -06:00
if ( err )
goto exit_err ;
2021-08-18 13:40:12 -06:00
dev_info ( cscfg_device ( ) , " CoreSight Configuration manager initialised " ) ;
return 0 ;
2021-08-18 13:40:19 -06:00
exit_err :
cscfg_clear_device ( ) ;
return err ;
2021-08-18 13:40:12 -06:00
}
void cscfg_exit ( void )
{
cscfg_clear_device ( ) ;
}