This patchset represents the second phase of CoreSight configuration management.
1) API updated to allow dynamic load and unload of configurations and features. Dependency management between loaded sets is added.
2) New configuration and feature sets can be added using a loadable module. An example in /samples/coresight is provided to demonstrate this.
3) Resource management API is added. This allows the system to ensure that loaded configurations and features are only loaded onto devices that can support them.
Further - it ensures that configurations with multiple features cannot over allocate resources.
4) configfs can be used to activate a configuration which will then be used when controlling tracing using sysfs.
5) Resource management is added to ETMv4 configurations. This allows current and future features and configurations to be defined in terms of resources used as well as registers to be programmed.
Defining features in this way allows the resource management to operate correctly.
The perf event parsing is also adjusted to allow the ETM resources requested on the command line (e.g. address filters, etc) to be correctly handled using resoruce management alongside the complex configurations such as autofdo.
Applies to coresight/next - which is 5.13-rc1 + initial Coresight configuration patchset.
To follow in future revisions / sets:- a) load of additional config and features by configfs b) ECT and CTI and other Coresight components support for configuration and features.
Mike Leach (8): coresight: syscfg: Update API to allow dynamic load and unload coresight: syscfg: Update load API for config loadable modules coresight: syscfg: Example CoreSight configuration loadable module coresight: configfs: Allow configfs to activate configuration. coresight: syscfg: Add API to check and validate device resources. coresight: etm4x: syscfg: Add resource management to etm4x. coresight: etm4x: Update perf event resource handling. coresight: etm4x: Update configuration example.
MAINTAINERS | 1 + .../hwtracing/coresight/coresight-cfg-afdo.c | 38 +- .../coresight/coresight-cfg-preload.c | 9 +- .../hwtracing/coresight/coresight-config.c | 71 ++- .../hwtracing/coresight/coresight-config.h | 45 +- .../hwtracing/coresight/coresight-etm4x-cfg.c | 533 ++++++++++++++++++ .../hwtracing/coresight/coresight-etm4x-cfg.h | 196 ++++++- .../coresight/coresight-etm4x-core.c | 250 +++----- .../coresight/coresight-syscfg-configfs.c | 87 +++ .../coresight/coresight-syscfg-configfs.h | 4 + .../hwtracing/coresight/coresight-syscfg.c | 390 +++++++++++-- .../hwtracing/coresight/coresight-syscfg.h | 38 +- include/linux/coresight.h | 2 + samples/Kconfig | 9 + samples/Makefile | 1 + samples/coresight/Makefile | 4 + samples/coresight/coresight-cfg-sample.c | 73 +++ 17 files changed, 1511 insertions(+), 240 deletions(-) create mode 100644 samples/coresight/Makefile create mode 100644 samples/coresight/coresight-cfg-sample.c
Update the load API to permit the runtime loading and unloading of new configurations and features.
On load, configurations and features are tagged with a "load owner" that is used to determine sets that were loaded together in a given API call.
To unload the API uses the load owner to unload all elements previously loaded by that owner.
The API also uses records the order in which different owners loaded their elements into the system. Later loading configurations can use previously loaded features, creating load dependencies. Therefore unload is enforced strictly in the reverse order to load.
A load owner will be an additional loadable module, or a configuration created or loaded via configfs.
Signed-off-by: Mike Leach mike.leach@linaro.org --- .../coresight/coresight-cfg-preload.c | 9 +- .../hwtracing/coresight/coresight-config.h | 9 +- .../coresight/coresight-syscfg-configfs.c | 20 +++ .../coresight/coresight-syscfg-configfs.h | 2 + .../hwtracing/coresight/coresight-syscfg.c | 154 +++++++++++++++++- .../hwtracing/coresight/coresight-syscfg.h | 30 +++- 6 files changed, 216 insertions(+), 8 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cfg-preload.c b/drivers/hwtracing/coresight/coresight-cfg-preload.c index 751af3710d56..e237a4edfa09 100644 --- a/drivers/hwtracing/coresight/coresight-cfg-preload.c +++ b/drivers/hwtracing/coresight/coresight-cfg-preload.c @@ -24,8 +24,13 @@ static struct cscfg_config_desc *preload_cfgs[] = { NULL };
+static struct cscfg_load_owner_info preload_owner = { + .type = CSCFG_OWNER_PRELOAD, +}; + /* preload called on initialisation */ -int cscfg_preload(void) +int cscfg_preload(void *owner_handle) { - return cscfg_load_config_sets(preload_cfgs, preload_feats); + preload_owner.owner_handle = owner_handle; + return cscfg_load_config_sets(preload_cfgs, preload_feats, &preload_owner); } diff --git a/drivers/hwtracing/coresight/coresight-config.h b/drivers/hwtracing/coresight/coresight-config.h index 25eb6c632692..9bd44b940add 100644 --- a/drivers/hwtracing/coresight/coresight-config.h +++ b/drivers/hwtracing/coresight/coresight-config.h @@ -97,6 +97,8 @@ struct cscfg_regval_desc { * @params_desc: array of parameters used. * @nr_regs: number of registers used. * @regs_desc: array of registers used. + * @load_owner: handle to load owner for dynamic load and unload of features. + * @fs_group: reference to configfs group for dynamic unload. */ struct cscfg_feature_desc { const char *name; @@ -107,6 +109,8 @@ struct cscfg_feature_desc { struct cscfg_parameter_desc *params_desc; int nr_regs; struct cscfg_regval_desc *regs_desc; + void *load_owner; + struct config_group *fs_group; };
/** @@ -128,7 +132,8 @@ struct cscfg_feature_desc { * @presets: Array of preset values. * @event_ea: Extended attribute for perf event value * @active_cnt: ref count for activate on this configuration. - * + * @load_owner: handle to load owner for dynamic load and unload of configs. + * @fs_group: reference to configfs group for dynamic unload. */ struct cscfg_config_desc { const char *name; @@ -141,6 +146,8 @@ struct cscfg_config_desc { const u64 *presets; /* nr_presets * nr_total_params */ struct dev_ext_attribute *event_ea; atomic_t active_cnt; + void *load_owner; + struct config_group *fs_group; };
/** diff --git a/drivers/hwtracing/coresight/coresight-syscfg-configfs.c b/drivers/hwtracing/coresight/coresight-syscfg-configfs.c index c547816b9000..345a62f1b728 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg-configfs.c +++ b/drivers/hwtracing/coresight/coresight-syscfg-configfs.c @@ -334,9 +334,19 @@ int cscfg_configfs_add_config(struct cscfg_config_desc *config_desc) if (IS_ERR(new_group)) return PTR_ERR(new_group); err = configfs_register_group(&cscfg_configs_grp, new_group); + if (!err) + config_desc->fs_group = new_group; return err; }
+void cscfg_configfs_del_config(struct cscfg_config_desc *config_desc) +{ + if (config_desc->fs_group) { + configfs_unregister_group(config_desc->fs_group); + config_desc->fs_group = NULL; + } +} + static struct config_item_type cscfg_features_type = { .ct_owner = THIS_MODULE, }; @@ -358,9 +368,19 @@ int cscfg_configfs_add_feature(struct cscfg_feature_desc *feat_desc) if (IS_ERR(new_group)) return PTR_ERR(new_group); err = configfs_register_group(&cscfg_features_grp, new_group); + if (!err) + feat_desc->fs_group = new_group; return err; }
+void cscfg_configfs_del_feature(struct cscfg_feature_desc *feat_desc) +{ + if (feat_desc->fs_group) { + configfs_unregister_group(feat_desc->fs_group); + feat_desc->fs_group = NULL; + } +} + int cscfg_configfs_init(struct cscfg_manager *cscfg_mgr) { struct configfs_subsystem *subsys; diff --git a/drivers/hwtracing/coresight/coresight-syscfg-configfs.h b/drivers/hwtracing/coresight/coresight-syscfg-configfs.h index 7d6ffe35ca4c..ea1e54d29f7f 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg-configfs.h +++ b/drivers/hwtracing/coresight/coresight-syscfg-configfs.h @@ -41,5 +41,7 @@ int cscfg_configfs_init(struct cscfg_manager *cscfg_mgr); void cscfg_configfs_release(struct cscfg_manager *cscfg_mgr); int cscfg_configfs_add_config(struct cscfg_config_desc *config_desc); int cscfg_configfs_add_feature(struct cscfg_feature_desc *feat_desc); +void cscfg_configfs_del_config(struct cscfg_config_desc *config_desc); +void cscfg_configfs_del_feature(struct cscfg_feature_desc *feat_desc);
#endif /* CORESIGHT_SYSCFG_CONFIGFS_H */ diff --git a/drivers/hwtracing/coresight/coresight-syscfg.c b/drivers/hwtracing/coresight/coresight-syscfg.c index 180202402eb5..ab5ec43a9dad 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.c +++ b/drivers/hwtracing/coresight/coresight-syscfg.c @@ -250,6 +250,13 @@ static int cscfg_check_feat_for_cfg(struct cscfg_config_desc *config_desc) static int cscfg_load_feat(struct cscfg_feature_desc *feat_desc) { int err; + 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; + }
/* add feature to any matching registered devices */ err = cscfg_add_feat_to_csdevs(feat_desc); @@ -267,6 +274,13 @@ static int cscfg_load_feat(struct cscfg_feature_desc *feat_desc) static int cscfg_load_config(struct cscfg_config_desc *config_desc) { int err; + 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; + }
/* validate features are present */ err = cscfg_check_feat_for_cfg(config_desc); @@ -354,6 +368,72 @@ int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc, return err; }
+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); + } +} + +/* + * 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; + + /* 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) { + cscfg_configfs_del_config(config_desc); + 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) { + cscfg_configfs_del_feature(feat_desc); + list_del(&feat_desc->item); + } + } +} + /** * cscfg_load_config_sets - API function to load feature and config sets. * @@ -361,13 +441,22 @@ int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc, * descriptors and load into the system. * Features are loaded first to ensure configuration dependencies can be met. * + * 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. + * * @config_descs: 0 terminated array of configuration descriptors. * @feat_descs: 0 terminated array of feature descriptors. + * @owner_info: Information on the owner of this set. */ int cscfg_load_config_sets(struct cscfg_config_desc **config_descs, - struct cscfg_feature_desc **feat_descs) + struct cscfg_feature_desc **feat_descs, + struct cscfg_load_owner_info *owner_info) { - int err, i = 0; + int err = 0, i = 0;
mutex_lock(&cscfg_mutex);
@@ -380,8 +469,10 @@ int cscfg_load_config_sets(struct cscfg_config_desc **config_descs, if (err) { pr_err("coresight-syscfg: Failed to load feature %s\n", feat_descs[i]->name); + cscfg_unload_owned_cfgs_feats(owner_info); goto exit_unlock; } + feat_descs[i]->load_owner = owner_info; i++; } } @@ -396,18 +487,74 @@ int cscfg_load_config_sets(struct cscfg_config_desc **config_descs, if (err) { pr_err("coresight-syscfg: Failed to load configuration %s\n", config_descs[i]->name); + cscfg_unload_owned_cfgs_feats(owner_info); goto exit_unlock; } + config_descs[i]->load_owner = owner_info; i++; } }
+ /* add the load owner to the load order list */ + list_add_tail(&owner_info->item, &cscfg_mgr->load_order_list); + exit_unlock: mutex_unlock(&cscfg_mutex); return err; } EXPORT_SYMBOL_GPL(cscfg_load_config_sets);
+/** + * cscfg_unload_config_sets - unload a set of configurations be 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. + * + * @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); + + /* 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; + } + + /* unload all belonging to load_owner */ + cscfg_unload_owned_cfgs_feats(owner_info); + + /* remove from load order list */ + list_del(&load_list_item->item); + +exit_unlock: + mutex_unlock(&cscfg_mutex); + return err; +} +EXPORT_SYMBOL_GPL(cscfg_unload_config_sets); + /* Handle coresight device registration and add configs and features to devices */
/* iterate through config lists and load matching configs to device */ @@ -783,10 +930,11 @@ int __init cscfg_init(void) 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);
/* preload built-in configurations */ - err = cscfg_preload(); + err = cscfg_preload(THIS_MODULE); if (err) goto exit_err;
diff --git a/drivers/hwtracing/coresight/coresight-syscfg.h b/drivers/hwtracing/coresight/coresight-syscfg.h index 8d018efd6ead..e2b2bdab31aa 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.h +++ b/drivers/hwtracing/coresight/coresight-syscfg.h @@ -25,6 +25,7 @@ * @csdev_desc_list: List of coresight devices registered with the configuration manager. * @feat_desc_list: List of feature descriptors to load into registered devices. * @config_desc_list: List of system configuration descriptors to load into registered devices. + * @load_order_list: Ordered list of owners for dynamically loaded configurations. * @sys_active_cnt: Total number of active config descriptor references. * @cfgfs_subsys: configfs subsystem used to manage configurations. */ @@ -33,6 +34,7 @@ struct cscfg_manager { struct list_head csdev_desc_list; struct list_head feat_desc_list; struct list_head config_desc_list; + struct list_head load_order_list; atomic_t sys_active_cnt; struct configfs_subsystem cfgfs_subsys; }; @@ -56,10 +58,32 @@ struct cscfg_registered_csdev { struct list_head item; };
+/* owner types for loading and unloading of config and feature sets */ +enum cscfg_load_owner_type { + CSCFG_OWNER_PRELOAD, +}; + +/** + * Load item - item to add to the load order list allowing dynamic load and + * unload of configurations and features. Caller loading a config + * set provides a context handle for unload. API ensures that + * items unloaded strictly in reverse order from load to ensure + * dependencies are respected. + * + * @item: list entry for load order list. + * @type: type of owner - allows interpretation of owner_handle. + * @owner_handle: load context - handle for owner of loaded configs. + */ +struct cscfg_load_owner_info { + struct list_head item; + int type; + void *owner_handle; +}; + /* internal core operations for cscfg */ int __init cscfg_init(void); void cscfg_exit(void); -int cscfg_preload(void); +int cscfg_preload(void *owner_handle); const struct cscfg_feature_desc *cscfg_get_named_feat_desc(const char *name); int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc, int param_idx, u64 value); @@ -67,7 +91,9 @@ int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc,
/* syscfg manager external API */ int cscfg_load_config_sets(struct cscfg_config_desc **cfg_descs, - struct cscfg_feature_desc **feat_descs); + struct cscfg_feature_desc **feat_descs, + struct cscfg_load_owner_info *owner_info); +int cscfg_unload_config_sets(struct cscfg_load_owner_info *owner_info); int cscfg_register_csdev(struct coresight_device *csdev, u32 match_flags, struct cscfg_csdev_feat_ops *ops); void cscfg_unregister_csdev(struct coresight_device *csdev);
On Wed, May 12, 2021 at 10:17:45PM +0100, Mike Leach wrote:
Update the load API to permit the runtime loading and unloading of new configurations and features.
On load, configurations and features are tagged with a "load owner" that is used to determine sets that were loaded together in a given API call.
To unload the API uses the load owner to unload all elements previously loaded by that owner.
The API also uses records the order in which different owners loaded their elements into the system. Later loading configurations can use previously loaded features, creating load dependencies. Therefore unload is enforced strictly in the reverse order to load.
There seems to be something wrong with the first sentence - I think the word "uses" is extra. I found another one like that below.
Otherwise this patch works. Despite the relative simplicity of what it does it took me two tries to go through it, probably because of how spread out the changes are. I don't think you could have done things differently though.
Reviewed-by: Mathieu Poirier mathieu.poirier@linaro.org
A load owner will be an additional loadable module, or a configuration created or loaded via configfs.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../coresight/coresight-cfg-preload.c | 9 +- .../hwtracing/coresight/coresight-config.h | 9 +- .../coresight/coresight-syscfg-configfs.c | 20 +++ .../coresight/coresight-syscfg-configfs.h | 2 + .../hwtracing/coresight/coresight-syscfg.c | 154 +++++++++++++++++- .../hwtracing/coresight/coresight-syscfg.h | 30 +++- 6 files changed, 216 insertions(+), 8 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cfg-preload.c b/drivers/hwtracing/coresight/coresight-cfg-preload.c index 751af3710d56..e237a4edfa09 100644 --- a/drivers/hwtracing/coresight/coresight-cfg-preload.c +++ b/drivers/hwtracing/coresight/coresight-cfg-preload.c @@ -24,8 +24,13 @@ static struct cscfg_config_desc *preload_cfgs[] = { NULL }; +static struct cscfg_load_owner_info preload_owner = {
- .type = CSCFG_OWNER_PRELOAD,
+};
/* preload called on initialisation */ -int cscfg_preload(void) +int cscfg_preload(void *owner_handle) {
- return cscfg_load_config_sets(preload_cfgs, preload_feats);
- preload_owner.owner_handle = owner_handle;
- return cscfg_load_config_sets(preload_cfgs, preload_feats, &preload_owner);
} diff --git a/drivers/hwtracing/coresight/coresight-config.h b/drivers/hwtracing/coresight/coresight-config.h index 25eb6c632692..9bd44b940add 100644 --- a/drivers/hwtracing/coresight/coresight-config.h +++ b/drivers/hwtracing/coresight/coresight-config.h @@ -97,6 +97,8 @@ struct cscfg_regval_desc {
- @params_desc: array of parameters used.
- @nr_regs: number of registers used.
- @regs_desc: array of registers used.
- @load_owner: handle to load owner for dynamic load and unload of features.
*/
- @fs_group: reference to configfs group for dynamic unload.
struct cscfg_feature_desc { const char *name; @@ -107,6 +109,8 @@ struct cscfg_feature_desc { struct cscfg_parameter_desc *params_desc; int nr_regs; struct cscfg_regval_desc *regs_desc;
- void *load_owner;
- struct config_group *fs_group;
}; /** @@ -128,7 +132,8 @@ struct cscfg_feature_desc {
- @presets: Array of preset values.
- @event_ea: Extended attribute for perf event value
- @active_cnt: ref count for activate on this configuration.
- @load_owner: handle to load owner for dynamic load and unload of configs.
*/
- @fs_group: reference to configfs group for dynamic unload.
struct cscfg_config_desc { const char *name; @@ -141,6 +146,8 @@ struct cscfg_config_desc { const u64 *presets; /* nr_presets * nr_total_params */ struct dev_ext_attribute *event_ea; atomic_t active_cnt;
- void *load_owner;
- struct config_group *fs_group;
}; /** diff --git a/drivers/hwtracing/coresight/coresight-syscfg-configfs.c b/drivers/hwtracing/coresight/coresight-syscfg-configfs.c index c547816b9000..345a62f1b728 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg-configfs.c +++ b/drivers/hwtracing/coresight/coresight-syscfg-configfs.c @@ -334,9 +334,19 @@ int cscfg_configfs_add_config(struct cscfg_config_desc *config_desc) if (IS_ERR(new_group)) return PTR_ERR(new_group); err = configfs_register_group(&cscfg_configs_grp, new_group);
- if (!err)
return err;config_desc->fs_group = new_group;
} +void cscfg_configfs_del_config(struct cscfg_config_desc *config_desc) +{
- if (config_desc->fs_group) {
configfs_unregister_group(config_desc->fs_group);
config_desc->fs_group = NULL;
- }
+}
static struct config_item_type cscfg_features_type = { .ct_owner = THIS_MODULE, }; @@ -358,9 +368,19 @@ int cscfg_configfs_add_feature(struct cscfg_feature_desc *feat_desc) if (IS_ERR(new_group)) return PTR_ERR(new_group); err = configfs_register_group(&cscfg_features_grp, new_group);
- if (!err)
return err;feat_desc->fs_group = new_group;
} +void cscfg_configfs_del_feature(struct cscfg_feature_desc *feat_desc) +{
- if (feat_desc->fs_group) {
configfs_unregister_group(feat_desc->fs_group);
feat_desc->fs_group = NULL;
- }
+}
int cscfg_configfs_init(struct cscfg_manager *cscfg_mgr) { struct configfs_subsystem *subsys; diff --git a/drivers/hwtracing/coresight/coresight-syscfg-configfs.h b/drivers/hwtracing/coresight/coresight-syscfg-configfs.h index 7d6ffe35ca4c..ea1e54d29f7f 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg-configfs.h +++ b/drivers/hwtracing/coresight/coresight-syscfg-configfs.h @@ -41,5 +41,7 @@ int cscfg_configfs_init(struct cscfg_manager *cscfg_mgr); void cscfg_configfs_release(struct cscfg_manager *cscfg_mgr); int cscfg_configfs_add_config(struct cscfg_config_desc *config_desc); int cscfg_configfs_add_feature(struct cscfg_feature_desc *feat_desc); +void cscfg_configfs_del_config(struct cscfg_config_desc *config_desc); +void cscfg_configfs_del_feature(struct cscfg_feature_desc *feat_desc); #endif /* CORESIGHT_SYSCFG_CONFIGFS_H */ diff --git a/drivers/hwtracing/coresight/coresight-syscfg.c b/drivers/hwtracing/coresight/coresight-syscfg.c index 180202402eb5..ab5ec43a9dad 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.c +++ b/drivers/hwtracing/coresight/coresight-syscfg.c @@ -250,6 +250,13 @@ static int cscfg_check_feat_for_cfg(struct cscfg_config_desc *config_desc) static int cscfg_load_feat(struct cscfg_feature_desc *feat_desc) { int err;
- 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;
- }
/* add feature to any matching registered devices */ err = cscfg_add_feat_to_csdevs(feat_desc); @@ -267,6 +274,13 @@ static int cscfg_load_feat(struct cscfg_feature_desc *feat_desc) static int cscfg_load_config(struct cscfg_config_desc *config_desc) { int err;
- 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;
- }
/* validate features are present */ err = cscfg_check_feat_for_cfg(config_desc); @@ -354,6 +368,72 @@ int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc, return err; } +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);
- }
+}
+/*
- 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;
- /* 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) {
cscfg_configfs_del_config(config_desc);
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) {
cscfg_configfs_del_feature(feat_desc);
list_del(&feat_desc->item);
}
- }
+}
/**
- cscfg_load_config_sets - API function to load feature and config sets.
@@ -361,13 +441,22 @@ int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc,
- descriptors and load into the system.
- Features are loaded first to ensure configuration dependencies can be met.
- 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.
- @config_descs: 0 terminated array of configuration descriptors.
- @feat_descs: 0 terminated array of feature descriptors.
*/
- @owner_info: Information on the owner of this set.
int cscfg_load_config_sets(struct cscfg_config_desc **config_descs,
struct cscfg_feature_desc **feat_descs)
struct cscfg_feature_desc **feat_descs,
struct cscfg_load_owner_info *owner_info)
{
- int err, i = 0;
- int err = 0, i = 0;
mutex_lock(&cscfg_mutex); @@ -380,8 +469,10 @@ int cscfg_load_config_sets(struct cscfg_config_desc **config_descs, if (err) { pr_err("coresight-syscfg: Failed to load feature %s\n", feat_descs[i]->name);
cscfg_unload_owned_cfgs_feats(owner_info); goto exit_unlock; }
} }feat_descs[i]->load_owner = owner_info; i++;
@@ -396,18 +487,74 @@ int cscfg_load_config_sets(struct cscfg_config_desc **config_descs, if (err) { pr_err("coresight-syscfg: Failed to load configuration %s\n", config_descs[i]->name);
cscfg_unload_owned_cfgs_feats(owner_info); goto exit_unlock; }
} }config_descs[i]->load_owner = owner_info; i++;
- /* add the load owner to the load order list */
- list_add_tail(&owner_info->item, &cscfg_mgr->load_order_list);
exit_unlock: mutex_unlock(&cscfg_mutex); return err; } EXPORT_SYMBOL_GPL(cscfg_load_config_sets); +/**
- cscfg_unload_config_sets - unload a set of configurations be 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:-
- no configurations are active.
- the set being unloaded was the last to be loaded to maintain dependencies.
- @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);
- /* 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;
- }
- /* unload all belonging to load_owner */
- cscfg_unload_owned_cfgs_feats(owner_info);
- /* remove from load order list */
- list_del(&load_list_item->item);
+exit_unlock:
- mutex_unlock(&cscfg_mutex);
- return err;
+} +EXPORT_SYMBOL_GPL(cscfg_unload_config_sets);
/* Handle coresight device registration and add configs and features to devices */ /* iterate through config lists and load matching configs to device */ @@ -783,10 +930,11 @@ int __init cscfg_init(void) 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);
/* preload built-in configurations */
- err = cscfg_preload();
- err = cscfg_preload(THIS_MODULE); if (err) goto exit_err;
diff --git a/drivers/hwtracing/coresight/coresight-syscfg.h b/drivers/hwtracing/coresight/coresight-syscfg.h index 8d018efd6ead..e2b2bdab31aa 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.h +++ b/drivers/hwtracing/coresight/coresight-syscfg.h @@ -25,6 +25,7 @@
- @csdev_desc_list: List of coresight devices registered with the configuration manager.
- @feat_desc_list: List of feature descriptors to load into registered devices.
- @config_desc_list: List of system configuration descriptors to load into registered devices.
*/
- @load_order_list: Ordered list of owners for dynamically loaded configurations.
- @sys_active_cnt: Total number of active config descriptor references.
- @cfgfs_subsys: configfs subsystem used to manage configurations.
@@ -33,6 +34,7 @@ struct cscfg_manager { struct list_head csdev_desc_list; struct list_head feat_desc_list; struct list_head config_desc_list;
- struct list_head load_order_list; atomic_t sys_active_cnt; struct configfs_subsystem cfgfs_subsys;
}; @@ -56,10 +58,32 @@ struct cscfg_registered_csdev { struct list_head item; }; +/* owner types for loading and unloading of config and feature sets */ +enum cscfg_load_owner_type {
- CSCFG_OWNER_PRELOAD,
+};
+/**
- Load item - item to add to the load order list allowing dynamic load and
unload of configurations and features. Caller loading a config
set provides a context handle for unload. API ensures that
items unloaded strictly in reverse order from load to ensure
dependencies are respected.
- @item: list entry for load order list.
- @type: type of owner - allows interpretation of owner_handle.
- @owner_handle: load context - handle for owner of loaded configs.
- */
+struct cscfg_load_owner_info {
- struct list_head item;
- int type;
- void *owner_handle;
+};
/* internal core operations for cscfg */ int __init cscfg_init(void); void cscfg_exit(void); -int cscfg_preload(void); +int cscfg_preload(void *owner_handle); const struct cscfg_feature_desc *cscfg_get_named_feat_desc(const char *name); int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc, int param_idx, u64 value); @@ -67,7 +91,9 @@ int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc, /* syscfg manager external API */ int cscfg_load_config_sets(struct cscfg_config_desc **cfg_descs,
struct cscfg_feature_desc **feat_descs);
struct cscfg_feature_desc **feat_descs,
struct cscfg_load_owner_info *owner_info);
+int cscfg_unload_config_sets(struct cscfg_load_owner_info *owner_info); int cscfg_register_csdev(struct coresight_device *csdev, u32 match_flags, struct cscfg_csdev_feat_ops *ops); void cscfg_unregister_csdev(struct coresight_device *csdev); -- 2.17.1
On Mon, May 17, 2021 at 11:15:15AM -0600, Mathieu Poirier wrote:
On Wed, May 12, 2021 at 10:17:45PM +0100, Mike Leach wrote:
Update the load API to permit the runtime loading and unloading of new configurations and features.
On load, configurations and features are tagged with a "load owner" that is used to determine sets that were loaded together in a given API call.
To unload the API uses the load owner to unload all elements previously loaded by that owner.
The API also uses records the order in which different owners loaded their elements into the system. Later loading configurations can use previously loaded features, creating load dependencies. Therefore unload is enforced strictly in the reverse order to load.
There seems to be something wrong with the first sentence - I think the word "uses" is extra. I found another one like that below.
Otherwise this patch works. Despite the relative simplicity of what it does it took me two tries to go through it, probably because of how spread out the changes are. I don't think you could have done things differently though.
Reviewed-by: Mathieu Poirier mathieu.poirier@linaro.org
A load owner will be an additional loadable module, or a configuration created or loaded via configfs.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../coresight/coresight-cfg-preload.c | 9 +- .../hwtracing/coresight/coresight-config.h | 9 +- .../coresight/coresight-syscfg-configfs.c | 20 +++ .../coresight/coresight-syscfg-configfs.h | 2 + .../hwtracing/coresight/coresight-syscfg.c | 154 +++++++++++++++++- .../hwtracing/coresight/coresight-syscfg.h | 30 +++- 6 files changed, 216 insertions(+), 8 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cfg-preload.c b/drivers/hwtracing/coresight/coresight-cfg-preload.c index 751af3710d56..e237a4edfa09 100644 --- a/drivers/hwtracing/coresight/coresight-cfg-preload.c +++ b/drivers/hwtracing/coresight/coresight-cfg-preload.c @@ -24,8 +24,13 @@ static struct cscfg_config_desc *preload_cfgs[] = { NULL }; +static struct cscfg_load_owner_info preload_owner = {
- .type = CSCFG_OWNER_PRELOAD,
+};
/* preload called on initialisation */ -int cscfg_preload(void) +int cscfg_preload(void *owner_handle) {
- return cscfg_load_config_sets(preload_cfgs, preload_feats);
- preload_owner.owner_handle = owner_handle;
- return cscfg_load_config_sets(preload_cfgs, preload_feats, &preload_owner);
} diff --git a/drivers/hwtracing/coresight/coresight-config.h b/drivers/hwtracing/coresight/coresight-config.h index 25eb6c632692..9bd44b940add 100644 --- a/drivers/hwtracing/coresight/coresight-config.h +++ b/drivers/hwtracing/coresight/coresight-config.h @@ -97,6 +97,8 @@ struct cscfg_regval_desc {
- @params_desc: array of parameters used.
- @nr_regs: number of registers used.
- @regs_desc: array of registers used.
- @load_owner: handle to load owner for dynamic load and unload of features.
*/
- @fs_group: reference to configfs group for dynamic unload.
struct cscfg_feature_desc { const char *name; @@ -107,6 +109,8 @@ struct cscfg_feature_desc { struct cscfg_parameter_desc *params_desc; int nr_regs; struct cscfg_regval_desc *regs_desc;
- void *load_owner;
- struct config_group *fs_group;
}; /** @@ -128,7 +132,8 @@ struct cscfg_feature_desc {
- @presets: Array of preset values.
- @event_ea: Extended attribute for perf event value
- @active_cnt: ref count for activate on this configuration.
- @load_owner: handle to load owner for dynamic load and unload of configs.
*/
- @fs_group: reference to configfs group for dynamic unload.
struct cscfg_config_desc { const char *name; @@ -141,6 +146,8 @@ struct cscfg_config_desc { const u64 *presets; /* nr_presets * nr_total_params */ struct dev_ext_attribute *event_ea; atomic_t active_cnt;
- void *load_owner;
- struct config_group *fs_group;
}; /** diff --git a/drivers/hwtracing/coresight/coresight-syscfg-configfs.c b/drivers/hwtracing/coresight/coresight-syscfg-configfs.c index c547816b9000..345a62f1b728 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg-configfs.c +++ b/drivers/hwtracing/coresight/coresight-syscfg-configfs.c @@ -334,9 +334,19 @@ int cscfg_configfs_add_config(struct cscfg_config_desc *config_desc) if (IS_ERR(new_group)) return PTR_ERR(new_group); err = configfs_register_group(&cscfg_configs_grp, new_group);
- if (!err)
return err;config_desc->fs_group = new_group;
} +void cscfg_configfs_del_config(struct cscfg_config_desc *config_desc) +{
- if (config_desc->fs_group) {
configfs_unregister_group(config_desc->fs_group);
config_desc->fs_group = NULL;
- }
+}
static struct config_item_type cscfg_features_type = { .ct_owner = THIS_MODULE, }; @@ -358,9 +368,19 @@ int cscfg_configfs_add_feature(struct cscfg_feature_desc *feat_desc) if (IS_ERR(new_group)) return PTR_ERR(new_group); err = configfs_register_group(&cscfg_features_grp, new_group);
- if (!err)
return err;feat_desc->fs_group = new_group;
} +void cscfg_configfs_del_feature(struct cscfg_feature_desc *feat_desc) +{
- if (feat_desc->fs_group) {
configfs_unregister_group(feat_desc->fs_group);
feat_desc->fs_group = NULL;
- }
+}
int cscfg_configfs_init(struct cscfg_manager *cscfg_mgr) { struct configfs_subsystem *subsys; diff --git a/drivers/hwtracing/coresight/coresight-syscfg-configfs.h b/drivers/hwtracing/coresight/coresight-syscfg-configfs.h index 7d6ffe35ca4c..ea1e54d29f7f 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg-configfs.h +++ b/drivers/hwtracing/coresight/coresight-syscfg-configfs.h @@ -41,5 +41,7 @@ int cscfg_configfs_init(struct cscfg_manager *cscfg_mgr); void cscfg_configfs_release(struct cscfg_manager *cscfg_mgr); int cscfg_configfs_add_config(struct cscfg_config_desc *config_desc); int cscfg_configfs_add_feature(struct cscfg_feature_desc *feat_desc); +void cscfg_configfs_del_config(struct cscfg_config_desc *config_desc); +void cscfg_configfs_del_feature(struct cscfg_feature_desc *feat_desc); #endif /* CORESIGHT_SYSCFG_CONFIGFS_H */ diff --git a/drivers/hwtracing/coresight/coresight-syscfg.c b/drivers/hwtracing/coresight/coresight-syscfg.c index 180202402eb5..ab5ec43a9dad 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.c +++ b/drivers/hwtracing/coresight/coresight-syscfg.c @@ -250,6 +250,13 @@ static int cscfg_check_feat_for_cfg(struct cscfg_config_desc *config_desc) static int cscfg_load_feat(struct cscfg_feature_desc *feat_desc) { int err;
- 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;
- }
/* add feature to any matching registered devices */ err = cscfg_add_feat_to_csdevs(feat_desc); @@ -267,6 +274,13 @@ static int cscfg_load_feat(struct cscfg_feature_desc *feat_desc) static int cscfg_load_config(struct cscfg_config_desc *config_desc) { int err;
- 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;
- }
/* validate features are present */ err = cscfg_check_feat_for_cfg(config_desc); @@ -354,6 +368,72 @@ int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc, return err; } +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);
- }
+}
+/*
- 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;
- /* 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) {
cscfg_configfs_del_config(config_desc);
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) {
cscfg_configfs_del_feature(feat_desc);
list_del(&feat_desc->item);
}
- }
+}
/**
- cscfg_load_config_sets - API function to load feature and config sets.
@@ -361,13 +441,22 @@ int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc,
- descriptors and load into the system.
- Features are loaded first to ensure configuration dependencies can be met.
- 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.
- @config_descs: 0 terminated array of configuration descriptors.
- @feat_descs: 0 terminated array of feature descriptors.
*/
- @owner_info: Information on the owner of this set.
int cscfg_load_config_sets(struct cscfg_config_desc **config_descs,
struct cscfg_feature_desc **feat_descs)
struct cscfg_feature_desc **feat_descs,
struct cscfg_load_owner_info *owner_info)
{
- int err, i = 0;
- int err = 0, i = 0;
mutex_lock(&cscfg_mutex); @@ -380,8 +469,10 @@ int cscfg_load_config_sets(struct cscfg_config_desc **config_descs, if (err) { pr_err("coresight-syscfg: Failed to load feature %s\n", feat_descs[i]->name);
cscfg_unload_owned_cfgs_feats(owner_info); goto exit_unlock; }
} }feat_descs[i]->load_owner = owner_info; i++;
@@ -396,18 +487,74 @@ int cscfg_load_config_sets(struct cscfg_config_desc **config_descs, if (err) { pr_err("coresight-syscfg: Failed to load configuration %s\n", config_descs[i]->name);
cscfg_unload_owned_cfgs_feats(owner_info); goto exit_unlock; }
} }config_descs[i]->load_owner = owner_info; i++;
- /* add the load owner to the load order list */
- list_add_tail(&owner_info->item, &cscfg_mgr->load_order_list);
exit_unlock: mutex_unlock(&cscfg_mutex); return err; } EXPORT_SYMBOL_GPL(cscfg_load_config_sets); +/**
- cscfg_unload_config_sets - unload a set of configurations be owner.
Shouldn't this be "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:-
- no configurations are active.
- the set being unloaded was the last to be loaded to maintain dependencies.
- @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);
- /* 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;
- }
- /* unload all belonging to load_owner */
- cscfg_unload_owned_cfgs_feats(owner_info);
- /* remove from load order list */
- list_del(&load_list_item->item);
+exit_unlock:
- mutex_unlock(&cscfg_mutex);
- return err;
+} +EXPORT_SYMBOL_GPL(cscfg_unload_config_sets);
/* Handle coresight device registration and add configs and features to devices */ /* iterate through config lists and load matching configs to device */ @@ -783,10 +930,11 @@ int __init cscfg_init(void) 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);
/* preload built-in configurations */
- err = cscfg_preload();
- err = cscfg_preload(THIS_MODULE); if (err) goto exit_err;
diff --git a/drivers/hwtracing/coresight/coresight-syscfg.h b/drivers/hwtracing/coresight/coresight-syscfg.h index 8d018efd6ead..e2b2bdab31aa 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.h +++ b/drivers/hwtracing/coresight/coresight-syscfg.h @@ -25,6 +25,7 @@
- @csdev_desc_list: List of coresight devices registered with the configuration manager.
- @feat_desc_list: List of feature descriptors to load into registered devices.
- @config_desc_list: List of system configuration descriptors to load into registered devices.
*/
- @load_order_list: Ordered list of owners for dynamically loaded configurations.
- @sys_active_cnt: Total number of active config descriptor references.
- @cfgfs_subsys: configfs subsystem used to manage configurations.
@@ -33,6 +34,7 @@ struct cscfg_manager { struct list_head csdev_desc_list; struct list_head feat_desc_list; struct list_head config_desc_list;
- struct list_head load_order_list; atomic_t sys_active_cnt; struct configfs_subsystem cfgfs_subsys;
}; @@ -56,10 +58,32 @@ struct cscfg_registered_csdev { struct list_head item; }; +/* owner types for loading and unloading of config and feature sets */ +enum cscfg_load_owner_type {
- CSCFG_OWNER_PRELOAD,
+};
+/**
- Load item - item to add to the load order list allowing dynamic load and
unload of configurations and features. Caller loading a config
set provides a context handle for unload. API ensures that
items unloaded strictly in reverse order from load to ensure
dependencies are respected.
- @item: list entry for load order list.
- @type: type of owner - allows interpretation of owner_handle.
- @owner_handle: load context - handle for owner of loaded configs.
- */
+struct cscfg_load_owner_info {
- struct list_head item;
- int type;
- void *owner_handle;
+};
/* internal core operations for cscfg */ int __init cscfg_init(void); void cscfg_exit(void); -int cscfg_preload(void); +int cscfg_preload(void *owner_handle); const struct cscfg_feature_desc *cscfg_get_named_feat_desc(const char *name); int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc, int param_idx, u64 value); @@ -67,7 +91,9 @@ int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc, /* syscfg manager external API */ int cscfg_load_config_sets(struct cscfg_config_desc **cfg_descs,
struct cscfg_feature_desc **feat_descs);
struct cscfg_feature_desc **feat_descs,
struct cscfg_load_owner_info *owner_info);
+int cscfg_unload_config_sets(struct cscfg_load_owner_info *owner_info); int cscfg_register_csdev(struct coresight_device *csdev, u32 match_flags, struct cscfg_csdev_feat_ops *ops); void cscfg_unregister_csdev(struct coresight_device *csdev); -- 2.17.1
Thanks - I'll sort both comments.
Regards
Mike
On Mon, 17 May 2021 at 18:27, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Mon, May 17, 2021 at 11:15:15AM -0600, Mathieu Poirier wrote:
On Wed, May 12, 2021 at 10:17:45PM +0100, Mike Leach wrote:
Update the load API to permit the runtime loading and unloading of new configurations and features.
On load, configurations and features are tagged with a "load owner" that is used to determine sets that were loaded together in a given API call.
To unload the API uses the load owner to unload all elements previously loaded by that owner.
The API also uses records the order in which different owners loaded their elements into the system. Later loading configurations can use previously loaded features, creating load dependencies. Therefore unload is enforced strictly in the reverse order to load.
There seems to be something wrong with the first sentence - I think the word "uses" is extra. I found another one like that below.
Otherwise this patch works. Despite the relative simplicity of what it does it took me two tries to go through it, probably because of how spread out the changes are. I don't think you could have done things differently though.
Reviewed-by: Mathieu Poirier mathieu.poirier@linaro.org
A load owner will be an additional loadable module, or a configuration created or loaded via configfs.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../coresight/coresight-cfg-preload.c | 9 +- .../hwtracing/coresight/coresight-config.h | 9 +- .../coresight/coresight-syscfg-configfs.c | 20 +++ .../coresight/coresight-syscfg-configfs.h | 2 + .../hwtracing/coresight/coresight-syscfg.c | 154 +++++++++++++++++- .../hwtracing/coresight/coresight-syscfg.h | 30 +++- 6 files changed, 216 insertions(+), 8 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cfg-preload.c b/drivers/hwtracing/coresight/coresight-cfg-preload.c index 751af3710d56..e237a4edfa09 100644 --- a/drivers/hwtracing/coresight/coresight-cfg-preload.c +++ b/drivers/hwtracing/coresight/coresight-cfg-preload.c @@ -24,8 +24,13 @@ static struct cscfg_config_desc *preload_cfgs[] = { NULL };
+static struct cscfg_load_owner_info preload_owner = {
- .type = CSCFG_OWNER_PRELOAD,
+};
/* preload called on initialisation */ -int cscfg_preload(void) +int cscfg_preload(void *owner_handle) {
- return cscfg_load_config_sets(preload_cfgs, preload_feats);
- preload_owner.owner_handle = owner_handle;
- return cscfg_load_config_sets(preload_cfgs, preload_feats, &preload_owner);
} diff --git a/drivers/hwtracing/coresight/coresight-config.h b/drivers/hwtracing/coresight/coresight-config.h index 25eb6c632692..9bd44b940add 100644 --- a/drivers/hwtracing/coresight/coresight-config.h +++ b/drivers/hwtracing/coresight/coresight-config.h @@ -97,6 +97,8 @@ struct cscfg_regval_desc {
- @params_desc: array of parameters used.
- @nr_regs: number of registers used.
- @regs_desc: array of registers used.
- @load_owner: handle to load owner for dynamic load and unload of features.
*/
- @fs_group: reference to configfs group for dynamic unload.
struct cscfg_feature_desc { const char *name; @@ -107,6 +109,8 @@ struct cscfg_feature_desc { struct cscfg_parameter_desc *params_desc; int nr_regs; struct cscfg_regval_desc *regs_desc;
- void *load_owner;
- struct config_group *fs_group;
};
/** @@ -128,7 +132,8 @@ struct cscfg_feature_desc {
- @presets: Array of preset values.
- @event_ea: Extended attribute for perf event value
- @active_cnt: ref count for activate on this configuration.
- @load_owner: handle to load owner for dynamic load and unload of configs.
*/
- @fs_group: reference to configfs group for dynamic unload.
struct cscfg_config_desc { const char *name; @@ -141,6 +146,8 @@ struct cscfg_config_desc { const u64 *presets; /* nr_presets * nr_total_params */ struct dev_ext_attribute *event_ea; atomic_t active_cnt;
- void *load_owner;
- struct config_group *fs_group;
};
/** diff --git a/drivers/hwtracing/coresight/coresight-syscfg-configfs.c b/drivers/hwtracing/coresight/coresight-syscfg-configfs.c index c547816b9000..345a62f1b728 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg-configfs.c +++ b/drivers/hwtracing/coresight/coresight-syscfg-configfs.c @@ -334,9 +334,19 @@ int cscfg_configfs_add_config(struct cscfg_config_desc *config_desc) if (IS_ERR(new_group)) return PTR_ERR(new_group); err = configfs_register_group(&cscfg_configs_grp, new_group);
- if (!err)
return err;config_desc->fs_group = new_group;
}
+void cscfg_configfs_del_config(struct cscfg_config_desc *config_desc) +{
- if (config_desc->fs_group) {
configfs_unregister_group(config_desc->fs_group);
config_desc->fs_group = NULL;
- }
+}
static struct config_item_type cscfg_features_type = { .ct_owner = THIS_MODULE, }; @@ -358,9 +368,19 @@ int cscfg_configfs_add_feature(struct cscfg_feature_desc *feat_desc) if (IS_ERR(new_group)) return PTR_ERR(new_group); err = configfs_register_group(&cscfg_features_grp, new_group);
- if (!err)
return err;feat_desc->fs_group = new_group;
}
+void cscfg_configfs_del_feature(struct cscfg_feature_desc *feat_desc) +{
- if (feat_desc->fs_group) {
configfs_unregister_group(feat_desc->fs_group);
feat_desc->fs_group = NULL;
- }
+}
int cscfg_configfs_init(struct cscfg_manager *cscfg_mgr) { struct configfs_subsystem *subsys; diff --git a/drivers/hwtracing/coresight/coresight-syscfg-configfs.h b/drivers/hwtracing/coresight/coresight-syscfg-configfs.h index 7d6ffe35ca4c..ea1e54d29f7f 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg-configfs.h +++ b/drivers/hwtracing/coresight/coresight-syscfg-configfs.h @@ -41,5 +41,7 @@ int cscfg_configfs_init(struct cscfg_manager *cscfg_mgr); void cscfg_configfs_release(struct cscfg_manager *cscfg_mgr); int cscfg_configfs_add_config(struct cscfg_config_desc *config_desc); int cscfg_configfs_add_feature(struct cscfg_feature_desc *feat_desc); +void cscfg_configfs_del_config(struct cscfg_config_desc *config_desc); +void cscfg_configfs_del_feature(struct cscfg_feature_desc *feat_desc);
#endif /* CORESIGHT_SYSCFG_CONFIGFS_H */ diff --git a/drivers/hwtracing/coresight/coresight-syscfg.c b/drivers/hwtracing/coresight/coresight-syscfg.c index 180202402eb5..ab5ec43a9dad 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.c +++ b/drivers/hwtracing/coresight/coresight-syscfg.c @@ -250,6 +250,13 @@ static int cscfg_check_feat_for_cfg(struct cscfg_config_desc *config_desc) static int cscfg_load_feat(struct cscfg_feature_desc *feat_desc) { int err;
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;
}
/* add feature to any matching registered devices */ err = cscfg_add_feat_to_csdevs(feat_desc);
@@ -267,6 +274,13 @@ static int cscfg_load_feat(struct cscfg_feature_desc *feat_desc) static int cscfg_load_config(struct cscfg_config_desc *config_desc) { int err;
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;
}
/* validate features are present */ err = cscfg_check_feat_for_cfg(config_desc);
@@ -354,6 +368,72 @@ int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc, return err; }
+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);
- }
+}
+/*
- 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;
- /* 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) {
cscfg_configfs_del_config(config_desc);
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) {
cscfg_configfs_del_feature(feat_desc);
list_del(&feat_desc->item);
}
- }
+}
/**
- cscfg_load_config_sets - API function to load feature and config sets.
@@ -361,13 +441,22 @@ int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc,
- descriptors and load into the system.
- Features are loaded first to ensure configuration dependencies can be met.
- 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.
- @config_descs: 0 terminated array of configuration descriptors.
- @feat_descs: 0 terminated array of feature descriptors.
*/
- @owner_info: Information on the owner of this set.
int cscfg_load_config_sets(struct cscfg_config_desc **config_descs,
struct cscfg_feature_desc **feat_descs)
struct cscfg_feature_desc **feat_descs,
struct cscfg_load_owner_info *owner_info)
{
- int err, i = 0;
int err = 0, i = 0;
mutex_lock(&cscfg_mutex);
@@ -380,8 +469,10 @@ int cscfg_load_config_sets(struct cscfg_config_desc **config_descs, if (err) { pr_err("coresight-syscfg: Failed to load feature %s\n", feat_descs[i]->name);
cscfg_unload_owned_cfgs_feats(owner_info); goto exit_unlock; }
}feat_descs[i]->load_owner = owner_info; i++; }
@@ -396,18 +487,74 @@ int cscfg_load_config_sets(struct cscfg_config_desc **config_descs, if (err) { pr_err("coresight-syscfg: Failed to load configuration %s\n", config_descs[i]->name);
cscfg_unload_owned_cfgs_feats(owner_info); goto exit_unlock; }
config_descs[i]->load_owner = owner_info; i++; }
}
/* add the load owner to the load order list */
list_add_tail(&owner_info->item, &cscfg_mgr->load_order_list);
exit_unlock: mutex_unlock(&cscfg_mutex); return err; } EXPORT_SYMBOL_GPL(cscfg_load_config_sets);
+/**
- cscfg_unload_config_sets - unload a set of configurations be owner.
Shouldn't this be "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:-
- no configurations are active.
- the set being unloaded was the last to be loaded to maintain dependencies.
- @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);
- /* 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;
- }
- /* unload all belonging to load_owner */
- cscfg_unload_owned_cfgs_feats(owner_info);
- /* remove from load order list */
- list_del(&load_list_item->item);
+exit_unlock:
- mutex_unlock(&cscfg_mutex);
- return err;
+} +EXPORT_SYMBOL_GPL(cscfg_unload_config_sets);
/* Handle coresight device registration and add configs and features to devices */
/* iterate through config lists and load matching configs to device */ @@ -783,10 +930,11 @@ int __init cscfg_init(void) 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);
/* preload built-in configurations */
- err = cscfg_preload();
- err = cscfg_preload(THIS_MODULE); if (err) goto exit_err;
diff --git a/drivers/hwtracing/coresight/coresight-syscfg.h b/drivers/hwtracing/coresight/coresight-syscfg.h index 8d018efd6ead..e2b2bdab31aa 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.h +++ b/drivers/hwtracing/coresight/coresight-syscfg.h @@ -25,6 +25,7 @@
- @csdev_desc_list: List of coresight devices registered with the configuration manager.
- @feat_desc_list: List of feature descriptors to load into registered devices.
- @config_desc_list: List of system configuration descriptors to load into registered devices.
*/
- @load_order_list: Ordered list of owners for dynamically loaded configurations.
- @sys_active_cnt: Total number of active config descriptor references.
- @cfgfs_subsys: configfs subsystem used to manage configurations.
@@ -33,6 +34,7 @@ struct cscfg_manager { struct list_head csdev_desc_list; struct list_head feat_desc_list; struct list_head config_desc_list;
- struct list_head load_order_list; atomic_t sys_active_cnt; struct configfs_subsystem cfgfs_subsys;
}; @@ -56,10 +58,32 @@ struct cscfg_registered_csdev { struct list_head item; };
+/* owner types for loading and unloading of config and feature sets */ +enum cscfg_load_owner_type {
- CSCFG_OWNER_PRELOAD,
+};
+/**
- Load item - item to add to the load order list allowing dynamic load and
unload of configurations and features. Caller loading a config
set provides a context handle for unload. API ensures that
items unloaded strictly in reverse order from load to ensure
dependencies are respected.
- @item: list entry for load order list.
- @type: type of owner - allows interpretation of owner_handle.
- @owner_handle: load context - handle for owner of loaded configs.
- */
+struct cscfg_load_owner_info {
- struct list_head item;
- int type;
- void *owner_handle;
+};
/* internal core operations for cscfg */ int __init cscfg_init(void); void cscfg_exit(void); -int cscfg_preload(void); +int cscfg_preload(void *owner_handle); const struct cscfg_feature_desc *cscfg_get_named_feat_desc(const char *name); int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc, int param_idx, u64 value); @@ -67,7 +91,9 @@ int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc,
/* syscfg manager external API */ int cscfg_load_config_sets(struct cscfg_config_desc **cfg_descs,
struct cscfg_feature_desc **feat_descs);
struct cscfg_feature_desc **feat_descs,
struct cscfg_load_owner_info *owner_info);
+int cscfg_unload_config_sets(struct cscfg_load_owner_info *owner_info); int cscfg_register_csdev(struct coresight_device *csdev, u32 match_flags, struct cscfg_csdev_feat_ops *ops); void cscfg_unregister_csdev(struct coresight_device *csdev); -- 2.17.1
CoreSight configurations and features can be added as kernel loadable modules. This patch updates the load owner API to ensure that the module cannot be unloaded either:- 1) if the config it supplies is in use 2) if the module is not the last in the load order list.
Signed-off-by: Mike Leach mike.leach@linaro.org --- .../hwtracing/coresight/coresight-syscfg.c | 39 ++++++++++++++++++- .../hwtracing/coresight/coresight-syscfg.h | 1 + 2 files changed, 39 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-syscfg.c b/drivers/hwtracing/coresight/coresight-syscfg.c index ab5ec43a9dad..26c1a244c2b1 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.c +++ b/drivers/hwtracing/coresight/coresight-syscfg.c @@ -368,6 +368,26 @@ int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc, return err; }
+/* + * 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); +} + static void cscfg_remove_owned_csdev_configs(struct coresight_device *csdev, void *load_owner) { struct cscfg_config_csdev *config_csdev, *tmp; @@ -497,6 +517,14 @@ int cscfg_load_config_sets(struct cscfg_config_desc **config_descs,
/* add the load owner to the load order list */ list_add_tail(&owner_info->item, &cscfg_mgr->load_order_list); + 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)); + if (err) { + cscfg_unload_owned_cfgs_feats(owner_info); + list_del(&owner_info->item); + } + }
exit_unlock: mutex_unlock(&cscfg_mutex); @@ -547,7 +575,11 @@ int cscfg_unload_config_sets(struct cscfg_load_owner_info *owner_info) cscfg_unload_owned_cfgs_feats(owner_info);
/* remove from load order list */ - list_del(&load_list_item->item); + 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);
exit_unlock: mutex_unlock(&cscfg_mutex); @@ -737,6 +769,10 @@ int cscfg_activate_config(unsigned long cfg_hash)
list_for_each_entry(config_desc, &cscfg_mgr->config_desc_list, item) { if ((unsigned long)config_desc->event_ea->var == cfg_hash) { + /* must ensure that config cannot be unloaded in use */ + err = cscfg_owner_get(config_desc->load_owner); + if (err) + break; /* * increment the global active count - control changes to * active configurations @@ -777,6 +813,7 @@ void cscfg_deactivate_config(unsigned long cfg_hash) 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; } diff --git a/drivers/hwtracing/coresight/coresight-syscfg.h b/drivers/hwtracing/coresight/coresight-syscfg.h index e2b2bdab31aa..1da37874f70f 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.h +++ b/drivers/hwtracing/coresight/coresight-syscfg.h @@ -61,6 +61,7 @@ struct cscfg_registered_csdev { /* owner types for loading and unloading of config and feature sets */ enum cscfg_load_owner_type { CSCFG_OWNER_PRELOAD, + CSCFG_OWNER_MODULE, };
/**
On Wed, May 12, 2021 at 10:17:46PM +0100, Mike Leach wrote:
CoreSight configurations and features can be added as kernel loadable modules. This patch updates the load owner API to ensure that the module cannot be unloaded either:-
Please remove the '-'. It is not a big issue but you can be sure the various patch robots will complain. With that:
Reviewed-by: Mathieu Poirier mathieu.poirier@linaro.org
- if the config it supplies is in use
- if the module is not the last in the load order list.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-syscfg.c | 39 ++++++++++++++++++- .../hwtracing/coresight/coresight-syscfg.h | 1 + 2 files changed, 39 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-syscfg.c b/drivers/hwtracing/coresight/coresight-syscfg.c index ab5ec43a9dad..26c1a244c2b1 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.c +++ b/drivers/hwtracing/coresight/coresight-syscfg.c @@ -368,6 +368,26 @@ int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc, return err; } +/*
- 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);
+}
static void cscfg_remove_owned_csdev_configs(struct coresight_device *csdev, void *load_owner) { struct cscfg_config_csdev *config_csdev, *tmp; @@ -497,6 +517,14 @@ int cscfg_load_config_sets(struct cscfg_config_desc **config_descs, /* add the load owner to the load order list */ list_add_tail(&owner_info->item, &cscfg_mgr->load_order_list);
- 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));
if (err) {
cscfg_unload_owned_cfgs_feats(owner_info);
list_del(&owner_info->item);
}
- }
exit_unlock: mutex_unlock(&cscfg_mutex); @@ -547,7 +575,11 @@ int cscfg_unload_config_sets(struct cscfg_load_owner_info *owner_info) cscfg_unload_owned_cfgs_feats(owner_info); /* remove from load order list */
- list_del(&load_list_item->item);
- 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);
exit_unlock: mutex_unlock(&cscfg_mutex); @@ -737,6 +769,10 @@ int cscfg_activate_config(unsigned long cfg_hash) list_for_each_entry(config_desc, &cscfg_mgr->config_desc_list, item) { if ((unsigned long)config_desc->event_ea->var == cfg_hash) {
/* must ensure that config cannot be unloaded in use */
err = cscfg_owner_get(config_desc->load_owner);
if (err)
break; /* * increment the global active count - control changes to * active configurations
@@ -777,6 +813,7 @@ void cscfg_deactivate_config(unsigned long cfg_hash) 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;
diff --git a/drivers/hwtracing/coresight/coresight-syscfg.h b/drivers/hwtracing/coresight/coresight-syscfg.h index e2b2bdab31aa..1da37874f70f 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.h +++ b/drivers/hwtracing/coresight/coresight-syscfg.h @@ -61,6 +61,7 @@ struct cscfg_registered_csdev { /* owner types for loading and unloading of config and feature sets */ enum cscfg_load_owner_type { CSCFG_OWNER_PRELOAD,
- CSCFG_OWNER_MODULE,
}; /** -- 2.17.1
An example of creating a loadable module to add CoreSight configurations into a system.
In the Kernel samples/coresight directory.
Signed-off-by: Mike Leach mike.leach@linaro.org --- MAINTAINERS | 1 + samples/Kconfig | 9 +++ samples/Makefile | 1 + samples/coresight/Makefile | 4 ++ samples/coresight/coresight-cfg-sample.c | 73 ++++++++++++++++++++++++ 5 files changed, 88 insertions(+) create mode 100644 samples/coresight/Makefile create mode 100644 samples/coresight/coresight-cfg-sample.c
diff --git a/MAINTAINERS b/MAINTAINERS index bd7aff0c120f..9ff5f5e7dd06 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1789,6 +1789,7 @@ F: Documentation/trace/coresight/* F: drivers/hwtracing/coresight/* F: include/dt-bindings/arm/coresight-cti-dt.h F: include/linux/coresight* +F: samples/coresight/* F: tools/perf/arch/arm/util/auxtrace.c F: tools/perf/arch/arm/util/cs-etm.c F: tools/perf/arch/arm/util/cs-etm.h diff --git a/samples/Kconfig b/samples/Kconfig index b5a1a7aa7e23..0cd618e15571 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -223,4 +223,13 @@ config SAMPLE_WATCH_QUEUE Build example userspace program to use the new mount_notify(), sb_notify() syscalls and the KEYCTL_WATCH_KEY keyctl() function.
+config SAMPLE_CORESIGHT_SYSCFG + tristate "Build example loadable module for CoreSight config" + depends on CORESIGHT && m + help + Build an example loadable module that adds new CoreSight features + and configuration using the CoreSight system configuration API. + This demonstrates how a user may create their own CoreSight + configurations and easily load them into the system at runtime. + endif # SAMPLES diff --git a/samples/Makefile b/samples/Makefile index 087e0988ccc5..6c96297001a8 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -30,3 +30,4 @@ obj-$(CONFIG_SAMPLE_INTEL_MEI) += mei/ subdir-$(CONFIG_SAMPLE_WATCHDOG) += watchdog subdir-$(CONFIG_SAMPLE_WATCH_QUEUE) += watch_queue obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak/ +obj-$(CONFIG_SAMPLE_CORESIGHT_SYSCFG) += coresight/ diff --git a/samples/coresight/Makefile b/samples/coresight/Makefile new file mode 100644 index 000000000000..09126aabf43d --- /dev/null +++ b/samples/coresight/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_SAMPLE_CORESIGHT_SYSCFG) += coresight-cfg-sample.o +ccflags-y += -I$(src)/../../drivers/hwtracing/coresight diff --git a/samples/coresight/coresight-cfg-sample.c b/samples/coresight/coresight-cfg-sample.c new file mode 100644 index 000000000000..865c188fae34 --- /dev/null +++ b/samples/coresight/coresight-cfg-sample.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(C) 2020 Linaro Limited. All rights reserved. + * Author: Mike Leach mike.leach@linaro.org + */ + +#include "coresight-config.h" +#include "coresight-syscfg.h" + +/* create an alternate autofdo configuration */ + +/* we will provide 4 sets of preset parameter values */ +#define AFDO2_NR_PRESETS 4 +/* the total number of parameters in used features - strobing has 2 */ +#define AFDO2_NR_PARAM_SUM 2 + +static const char *afdo2_ref_names[] = { + "strobing", +}; + +/* + * set of presets leaves strobing window constant while varying period to allow + * experimentation with mark / space ratios for various workloads + */ +static u64 afdo2_presets[AFDO2_NR_PRESETS][AFDO2_NR_PARAM_SUM] = { + { 1000, 100 }, + { 1000, 1000 }, + { 1000, 5000 }, + { 1000, 10000 }, +}; + +struct cscfg_config_desc afdo2 = { + .name = "autofdo2", + .description = "Setup ETMs with strobing for autofdo\n" + "Supplied presets allow experimentation with mark-space ratio for various loads\n", + .nr_feat_refs = ARRAY_SIZE(afdo2_ref_names), + .feat_ref_names = afdo2_ref_names, + .nr_presets = AFDO2_NR_PRESETS, + .nr_total_params = AFDO2_NR_PARAM_SUM, + .presets = &afdo2_presets[0][0], +}; + +static struct cscfg_feature_desc *sample_feats[] = { + 0 +}; + +static struct cscfg_config_desc *sample_cfgs[] = { + &afdo2, + 0 +}; + +static struct cscfg_load_owner_info mod_owner = { + .type = CSCFG_OWNER_MODULE, + .owner_handle = THIS_MODULE, +}; + +/* module init and exit - just load and unload configs */ +static int __init cscfg_sample_init(void) +{ + return cscfg_load_config_sets(sample_cfgs, sample_feats, &mod_owner); +} + +static void __exit cscfg_sample_exit(void) +{ + cscfg_unload_config_sets(&mod_owner); +} + +module_init(cscfg_sample_init); +module_exit(cscfg_sample_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Mike Leach mike.leach@linaro.org"); +MODULE_DESCRIPTION("CoreSight Syscfg Example");
Good day Mike,
On Wed, May 12, 2021 at 10:17:47PM +0100, Mike Leach wrote:
An example of creating a loadable module to add CoreSight configurations into a system.
In the Kernel samples/coresight directory.
Signed-off-by: Mike Leach mike.leach@linaro.org
MAINTAINERS | 1 + samples/Kconfig | 9 +++ samples/Makefile | 1 + samples/coresight/Makefile | 4 ++ samples/coresight/coresight-cfg-sample.c | 73 ++++++++++++++++++++++++ 5 files changed, 88 insertions(+) create mode 100644 samples/coresight/Makefile create mode 100644 samples/coresight/coresight-cfg-sample.c
diff --git a/MAINTAINERS b/MAINTAINERS index bd7aff0c120f..9ff5f5e7dd06 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1789,6 +1789,7 @@ F: Documentation/trace/coresight/* F: drivers/hwtracing/coresight/* F: include/dt-bindings/arm/coresight-cti-dt.h F: include/linux/coresight* +F: samples/coresight/* F: tools/perf/arch/arm/util/auxtrace.c F: tools/perf/arch/arm/util/cs-etm.c F: tools/perf/arch/arm/util/cs-etm.h diff --git a/samples/Kconfig b/samples/Kconfig index b5a1a7aa7e23..0cd618e15571 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -223,4 +223,13 @@ config SAMPLE_WATCH_QUEUE Build example userspace program to use the new mount_notify(), sb_notify() syscalls and the KEYCTL_WATCH_KEY keyctl() function. +config SAMPLE_CORESIGHT_SYSCFG
- tristate "Build example loadable module for CoreSight config"
- depends on CORESIGHT && m
- help
Build an example loadable module that adds new CoreSight features
and configuration using the CoreSight system configuration API.
This demonstrates how a user may create their own CoreSight
configurations and easily load them into the system at runtime.
endif # SAMPLES diff --git a/samples/Makefile b/samples/Makefile index 087e0988ccc5..6c96297001a8 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -30,3 +30,4 @@ obj-$(CONFIG_SAMPLE_INTEL_MEI) += mei/ subdir-$(CONFIG_SAMPLE_WATCHDOG) += watchdog subdir-$(CONFIG_SAMPLE_WATCH_QUEUE) += watch_queue obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak/ +obj-$(CONFIG_SAMPLE_CORESIGHT_SYSCFG) += coresight/ diff --git a/samples/coresight/Makefile b/samples/coresight/Makefile new file mode 100644 index 000000000000..09126aabf43d --- /dev/null +++ b/samples/coresight/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_SAMPLE_CORESIGHT_SYSCFG) += coresight-cfg-sample.o +ccflags-y += -I$(src)/../../drivers/hwtracing/coresight
On my side this really doesn't work and it's baffling. I double checked the path that gets generated with V=1 and it is correct. I ended up replacing $(src) with $(srctree), which gave me a full path rather than a relative path, to get things going. Please see if that works on your side.
I looked at other Makefiles and $(srctree) is predominant. As such I suggest to go with that to avoid further headaches.
diff --git a/samples/coresight/coresight-cfg-sample.c b/samples/coresight/coresight-cfg-sample.c new file mode 100644 index 000000000000..865c188fae34 --- /dev/null +++ b/samples/coresight/coresight-cfg-sample.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright(C) 2020 Linaro Limited. All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include "coresight-config.h" +#include "coresight-syscfg.h"
+/* create an alternate autofdo configuration */
+/* we will provide 4 sets of preset parameter values */ +#define AFDO2_NR_PRESETS 4 +/* the total number of parameters in used features - strobing has 2 */ +#define AFDO2_NR_PARAM_SUM 2
+static const char *afdo2_ref_names[] = {
- "strobing",
+};
+/*
- set of presets leaves strobing window constant while varying period to allow
- experimentation with mark / space ratios for various workloads
- */
+static u64 afdo2_presets[AFDO2_NR_PRESETS][AFDO2_NR_PARAM_SUM] = {
- { 1000, 100 },
- { 1000, 1000 },
- { 1000, 5000 },
- { 1000, 10000 },
+};
+struct cscfg_config_desc afdo2 = {
- .name = "autofdo2",
- .description = "Setup ETMs with strobing for autofdo\n"
- "Supplied presets allow experimentation with mark-space ratio for various loads\n",
- .nr_feat_refs = ARRAY_SIZE(afdo2_ref_names),
- .feat_ref_names = afdo2_ref_names,
- .nr_presets = AFDO2_NR_PRESETS,
- .nr_total_params = AFDO2_NR_PARAM_SUM,
- .presets = &afdo2_presets[0][0],
+};
+static struct cscfg_feature_desc *sample_feats[] = {
- 0
NULL
+};
+static struct cscfg_config_desc *sample_cfgs[] = {
- &afdo2,
- 0
NULL
The end result is the same but it is a matter of time before it gets flagged by a bot.
With the above:
Reviewed-by: Mathieu Poirier mathieu.poirier@linaro.org
+};
+static struct cscfg_load_owner_info mod_owner = {
- .type = CSCFG_OWNER_MODULE,
- .owner_handle = THIS_MODULE,
+};
+/* module init and exit - just load and unload configs */ +static int __init cscfg_sample_init(void) +{
- return cscfg_load_config_sets(sample_cfgs, sample_feats, &mod_owner);
+}
+static void __exit cscfg_sample_exit(void) +{
- cscfg_unload_config_sets(&mod_owner);
+}
+module_init(cscfg_sample_init); +module_exit(cscfg_sample_exit);
+MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Mike Leach mike.leach@linaro.org");
+MODULE_DESCRIPTION("CoreSight Syscfg Example");
2.17.1
Hi Mathieu,
On Tue, 18 May 2021 at 16:52, Mathieu Poirier mathieu.poirier@linaro.org wrote:
Good day Mike,
On Wed, May 12, 2021 at 10:17:47PM +0100, Mike Leach wrote:
An example of creating a loadable module to add CoreSight configurations into a system.
In the Kernel samples/coresight directory.
Signed-off-by: Mike Leach mike.leach@linaro.org
MAINTAINERS | 1 + samples/Kconfig | 9 +++ samples/Makefile | 1 + samples/coresight/Makefile | 4 ++ samples/coresight/coresight-cfg-sample.c | 73 ++++++++++++++++++++++++ 5 files changed, 88 insertions(+) create mode 100644 samples/coresight/Makefile create mode 100644 samples/coresight/coresight-cfg-sample.c
diff --git a/MAINTAINERS b/MAINTAINERS index bd7aff0c120f..9ff5f5e7dd06 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1789,6 +1789,7 @@ F: Documentation/trace/coresight/* F: drivers/hwtracing/coresight/* F: include/dt-bindings/arm/coresight-cti-dt.h F: include/linux/coresight* +F: samples/coresight/* F: tools/perf/arch/arm/util/auxtrace.c F: tools/perf/arch/arm/util/cs-etm.c F: tools/perf/arch/arm/util/cs-etm.h diff --git a/samples/Kconfig b/samples/Kconfig index b5a1a7aa7e23..0cd618e15571 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -223,4 +223,13 @@ config SAMPLE_WATCH_QUEUE Build example userspace program to use the new mount_notify(), sb_notify() syscalls and the KEYCTL_WATCH_KEY keyctl() function.
+config SAMPLE_CORESIGHT_SYSCFG
tristate "Build example loadable module for CoreSight config"
depends on CORESIGHT && m
help
Build an example loadable module that adds new CoreSight features
and configuration using the CoreSight system configuration API.
This demonstrates how a user may create their own CoreSight
configurations and easily load them into the system at runtime.
endif # SAMPLES diff --git a/samples/Makefile b/samples/Makefile index 087e0988ccc5..6c96297001a8 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -30,3 +30,4 @@ obj-$(CONFIG_SAMPLE_INTEL_MEI) += mei/ subdir-$(CONFIG_SAMPLE_WATCHDOG) += watchdog subdir-$(CONFIG_SAMPLE_WATCH_QUEUE) += watch_queue obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak/ +obj-$(CONFIG_SAMPLE_CORESIGHT_SYSCFG) += coresight/ diff --git a/samples/coresight/Makefile b/samples/coresight/Makefile new file mode 100644 index 000000000000..09126aabf43d --- /dev/null +++ b/samples/coresight/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_SAMPLE_CORESIGHT_SYSCFG) += coresight-cfg-sample.o +ccflags-y += -I$(src)/../../drivers/hwtracing/coresight
On my side this really doesn't work and it's baffling. I double checked the path that gets generated with V=1 and it is correct. I ended up replacing $(src) with $(srctree), which gave me a full path rather than a relative path, to get things going. Please see if that works on your side.
I looked at other Makefiles and $(srctree) is predominant. As such I suggest to go with that to avoid further headaches.
If I substitute $(srctree) instead of $(src) I get the header file as not found. Paths generated... using $(src) : -Isamples/coresight/../../drivers/hwtracing/coresight using $(srctree): -I./../../drivers/hwtracing/coresight
However if I drop some of the relative path to make the line ccflags-y += -I$(srctree)/drivers/hwtracing/coresight
then $srctree does work. Can you confirm that is what you did on your system? If so we can go with that.
diff --git a/samples/coresight/coresight-cfg-sample.c b/samples/coresight/coresight-cfg-sample.c new file mode 100644 index 000000000000..865c188fae34 --- /dev/null +++ b/samples/coresight/coresight-cfg-sample.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright(C) 2020 Linaro Limited. All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include "coresight-config.h" +#include "coresight-syscfg.h"
+/* create an alternate autofdo configuration */
+/* we will provide 4 sets of preset parameter values */ +#define AFDO2_NR_PRESETS 4 +/* the total number of parameters in used features - strobing has 2 */ +#define AFDO2_NR_PARAM_SUM 2
+static const char *afdo2_ref_names[] = {
"strobing",
+};
+/*
- set of presets leaves strobing window constant while varying period to allow
- experimentation with mark / space ratios for various workloads
- */
+static u64 afdo2_presets[AFDO2_NR_PRESETS][AFDO2_NR_PARAM_SUM] = {
{ 1000, 100 },
{ 1000, 1000 },
{ 1000, 5000 },
{ 1000, 10000 },
+};
+struct cscfg_config_desc afdo2 = {
.name = "autofdo2",
.description = "Setup ETMs with strobing for autofdo\n"
"Supplied presets allow experimentation with mark-space ratio for various loads\n",
.nr_feat_refs = ARRAY_SIZE(afdo2_ref_names),
.feat_ref_names = afdo2_ref_names,
.nr_presets = AFDO2_NR_PRESETS,
.nr_total_params = AFDO2_NR_PARAM_SUM,
.presets = &afdo2_presets[0][0],
+};
+static struct cscfg_feature_desc *sample_feats[] = {
0
NULL
+};
+static struct cscfg_config_desc *sample_cfgs[] = {
&afdo2,
0
NULL
The end result is the same but it is a matter of time before it gets flagged by a bot.
Agreed - I'll fix the Nulls
Thanks
Mike
With the above:
Reviewed-by: Mathieu Poirier mathieu.poirier@linaro.org
+};
+static struct cscfg_load_owner_info mod_owner = {
.type = CSCFG_OWNER_MODULE,
.owner_handle = THIS_MODULE,
+};
+/* module init and exit - just load and unload configs */ +static int __init cscfg_sample_init(void) +{
return cscfg_load_config_sets(sample_cfgs, sample_feats, &mod_owner);
+}
+static void __exit cscfg_sample_exit(void) +{
cscfg_unload_config_sets(&mod_owner);
+}
+module_init(cscfg_sample_init); +module_exit(cscfg_sample_exit);
+MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Mike Leach mike.leach@linaro.org");
+MODULE_DESCRIPTION("CoreSight Syscfg Example");
2.17.1
On Tue, 18 May 2021 at 10:38, Mike Leach mike.leach@linaro.org wrote:
Hi Mathieu,
On Tue, 18 May 2021 at 16:52, Mathieu Poirier mathieu.poirier@linaro.org wrote:
Good day Mike,
On Wed, May 12, 2021 at 10:17:47PM +0100, Mike Leach wrote:
An example of creating a loadable module to add CoreSight configurations into a system.
In the Kernel samples/coresight directory.
Signed-off-by: Mike Leach mike.leach@linaro.org
MAINTAINERS | 1 + samples/Kconfig | 9 +++ samples/Makefile | 1 + samples/coresight/Makefile | 4 ++ samples/coresight/coresight-cfg-sample.c | 73 ++++++++++++++++++++++++ 5 files changed, 88 insertions(+) create mode 100644 samples/coresight/Makefile create mode 100644 samples/coresight/coresight-cfg-sample.c
diff --git a/MAINTAINERS b/MAINTAINERS index bd7aff0c120f..9ff5f5e7dd06 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1789,6 +1789,7 @@ F: Documentation/trace/coresight/* F: drivers/hwtracing/coresight/* F: include/dt-bindings/arm/coresight-cti-dt.h F: include/linux/coresight* +F: samples/coresight/* F: tools/perf/arch/arm/util/auxtrace.c F: tools/perf/arch/arm/util/cs-etm.c F: tools/perf/arch/arm/util/cs-etm.h diff --git a/samples/Kconfig b/samples/Kconfig index b5a1a7aa7e23..0cd618e15571 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -223,4 +223,13 @@ config SAMPLE_WATCH_QUEUE Build example userspace program to use the new mount_notify(), sb_notify() syscalls and the KEYCTL_WATCH_KEY keyctl() function.
+config SAMPLE_CORESIGHT_SYSCFG
tristate "Build example loadable module for CoreSight config"
depends on CORESIGHT && m
help
Build an example loadable module that adds new CoreSight features
and configuration using the CoreSight system configuration API.
This demonstrates how a user may create their own CoreSight
configurations and easily load them into the system at runtime.
endif # SAMPLES diff --git a/samples/Makefile b/samples/Makefile index 087e0988ccc5..6c96297001a8 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -30,3 +30,4 @@ obj-$(CONFIG_SAMPLE_INTEL_MEI) += mei/ subdir-$(CONFIG_SAMPLE_WATCHDOG) += watchdog subdir-$(CONFIG_SAMPLE_WATCH_QUEUE) += watch_queue obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak/ +obj-$(CONFIG_SAMPLE_CORESIGHT_SYSCFG) += coresight/ diff --git a/samples/coresight/Makefile b/samples/coresight/Makefile new file mode 100644 index 000000000000..09126aabf43d --- /dev/null +++ b/samples/coresight/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_SAMPLE_CORESIGHT_SYSCFG) += coresight-cfg-sample.o +ccflags-y += -I$(src)/../../drivers/hwtracing/coresight
On my side this really doesn't work and it's baffling. I double checked the path that gets generated with V=1 and it is correct. I ended up replacing $(src) with $(srctree), which gave me a full path rather than a relative path, to get things going. Please see if that works on your side.
I looked at other Makefiles and $(srctree) is predominant. As such I suggest to go with that to avoid further headaches.
If I substitute $(srctree) instead of $(src) I get the header file as not found. Paths generated... using $(src) : -Isamples/coresight/../../drivers/hwtracing/coresight using $(srctree): -I./../../drivers/hwtracing/coresight
However if I drop some of the relative path to make the line ccflags-y += -I$(srctree)/drivers/hwtracing/coresight
Yes, that's exactly what I did - I should have been more precise.
then $srctree does work. Can you confirm that is what you did on your system? If so we can go with that.
diff --git a/samples/coresight/coresight-cfg-sample.c b/samples/coresight/coresight-cfg-sample.c new file mode 100644 index 000000000000..865c188fae34 --- /dev/null +++ b/samples/coresight/coresight-cfg-sample.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright(C) 2020 Linaro Limited. All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include "coresight-config.h" +#include "coresight-syscfg.h"
+/* create an alternate autofdo configuration */
+/* we will provide 4 sets of preset parameter values */ +#define AFDO2_NR_PRESETS 4 +/* the total number of parameters in used features - strobing has 2 */ +#define AFDO2_NR_PARAM_SUM 2
+static const char *afdo2_ref_names[] = {
"strobing",
+};
+/*
- set of presets leaves strobing window constant while varying period to allow
- experimentation with mark / space ratios for various workloads
- */
+static u64 afdo2_presets[AFDO2_NR_PRESETS][AFDO2_NR_PARAM_SUM] = {
{ 1000, 100 },
{ 1000, 1000 },
{ 1000, 5000 },
{ 1000, 10000 },
+};
+struct cscfg_config_desc afdo2 = {
.name = "autofdo2",
.description = "Setup ETMs with strobing for autofdo\n"
"Supplied presets allow experimentation with mark-space ratio for various loads\n",
.nr_feat_refs = ARRAY_SIZE(afdo2_ref_names),
.feat_ref_names = afdo2_ref_names,
.nr_presets = AFDO2_NR_PRESETS,
.nr_total_params = AFDO2_NR_PARAM_SUM,
.presets = &afdo2_presets[0][0],
+};
+static struct cscfg_feature_desc *sample_feats[] = {
0
NULL
+};
+static struct cscfg_config_desc *sample_cfgs[] = {
&afdo2,
0
NULL
The end result is the same but it is a matter of time before it gets flagged by a bot.
Agreed - I'll fix the Nulls
Thanks
Mike
With the above:
Reviewed-by: Mathieu Poirier mathieu.poirier@linaro.org
+};
+static struct cscfg_load_owner_info mod_owner = {
.type = CSCFG_OWNER_MODULE,
.owner_handle = THIS_MODULE,
+};
+/* module init and exit - just load and unload configs */ +static int __init cscfg_sample_init(void) +{
return cscfg_load_config_sets(sample_cfgs, sample_feats, &mod_owner);
+}
+static void __exit cscfg_sample_exit(void) +{
cscfg_unload_config_sets(&mod_owner);
+}
+module_init(cscfg_sample_init); +module_exit(cscfg_sample_exit);
+MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Mike Leach mike.leach@linaro.org");
+MODULE_DESCRIPTION("CoreSight Syscfg Example");
2.17.1
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
Adds configfs attributes to allow a configuration to be enabled for use when sysfs is used to control CoreSight.
perf retains independent enabling of configurations.
Signed-off-by: Mike Leach mike.leach@linaro.org --- .../coresight/coresight-etm4x-core.c | 5 + .../coresight/coresight-syscfg-configfs.c | 67 +++++++++ .../coresight/coresight-syscfg-configfs.h | 2 + .../hwtracing/coresight/coresight-syscfg.c | 129 ++++++++++++++---- .../hwtracing/coresight/coresight-syscfg.h | 7 +- 5 files changed, 182 insertions(+), 28 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index b7a4aeaa2bc7..2637096c4621 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -688,6 +688,11 @@ static int etm4_enable_sysfs(struct coresight_device *csdev) struct etm4_enable_arg arg = { }; int ret;
+ /* enable any config activated by configfs */ + ret = cscfg_csdev_enable_active_config(csdev, 0, 0); + if (ret) + return ret; + spin_lock(&drvdata->spinlock);
/* diff --git a/drivers/hwtracing/coresight/coresight-syscfg-configfs.c b/drivers/hwtracing/coresight/coresight-syscfg-configfs.c index 345a62f1b728..ae79ae8b1d7e 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg-configfs.c +++ b/drivers/hwtracing/coresight/coresight-syscfg-configfs.c @@ -6,6 +6,7 @@
#include <linux/configfs.h>
+#include "coresight-config.h" #include "coresight-syscfg-configfs.h"
/* create a default ci_type. */ @@ -87,9 +88,75 @@ static ssize_t cscfg_cfg_values_show(struct config_item *item, char *page) } CONFIGFS_ATTR_RO(cscfg_cfg_, values);
+static ssize_t cscfg_cfg_activate_show(struct config_item *item, char *page) +{ + struct cscfg_fs_config *fs_config = container_of(to_config_group(item), + struct cscfg_fs_config, group); + + return scnprintf(page, PAGE_SIZE, "%d\n", fs_config->active); +} + +static ssize_t cscfg_cfg_activate_store(struct config_item *item, + const char *page, size_t count) +{ + struct cscfg_fs_config *fs_config = container_of(to_config_group(item), + struct cscfg_fs_config, group); + int err; + bool val; + + err = kstrtobool(page, &val); + if (!err) + err = cscfg_config_sysfs_activation(fs_config->config_desc, val); + if (!err) { + fs_config->active = val; + if (val) + cscfg_config_sysfs_preset(fs_config->preset); + } + return err ? err : count; +} +CONFIGFS_ATTR(cscfg_cfg_, activate); + +static ssize_t cscfg_cfg_active_preset_show(struct config_item *item, char *page) +{ + struct cscfg_fs_config *fs_config = container_of(to_config_group(item), + struct cscfg_fs_config, group); + + return scnprintf(page, PAGE_SIZE, "%d\n", fs_config->preset); +} + +static ssize_t cscfg_cfg_active_preset_store(struct config_item *item, + const char *page, size_t count) +{ + struct cscfg_fs_config *fs_config = container_of(to_config_group(item), + struct cscfg_fs_config, group); + int preset, err; + + err = kstrtoint(page, 0, &preset); + if (!err) { + /* + * presets start at 1, and go up to max (15), + * but the config may provide fewer. + */ + if ((preset < 1) || (preset > fs_config->config_desc->nr_presets)) + err = -EINVAL; + } + + if (!err) { + /* set new value */ + fs_config->preset = preset; + /* set on system if active */ + if (fs_config->active) + cscfg_config_sysfs_preset(fs_config->preset); + } + return err ? err : count; +} +CONFIGFS_ATTR(cscfg_cfg_, active_preset); + static struct configfs_attribute *cscfg_config_view_attrs[] = { &cscfg_cfg_attr_description, &cscfg_cfg_attr_feature_refs, + &cscfg_cfg_attr_activate, + &cscfg_cfg_attr_active_preset, NULL, };
diff --git a/drivers/hwtracing/coresight/coresight-syscfg-configfs.h b/drivers/hwtracing/coresight/coresight-syscfg-configfs.h index ea1e54d29f7f..373d84d43268 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg-configfs.h +++ b/drivers/hwtracing/coresight/coresight-syscfg-configfs.h @@ -15,6 +15,8 @@ struct cscfg_fs_config { struct cscfg_config_desc *config_desc; struct config_group group; + bool active; + int preset; };
/* container for feature view */ diff --git a/drivers/hwtracing/coresight/coresight-syscfg.c b/drivers/hwtracing/coresight/coresight-syscfg.c index 26c1a244c2b1..ab74e33b892b 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.c +++ b/drivers/hwtracing/coresight/coresight-syscfg.c @@ -743,32 +743,23 @@ void cscfg_csdev_reset_feats(struct coresight_device *csdev) } EXPORT_SYMBOL_GPL(cscfg_csdev_reset_feats);
-/** - * 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. +/* + * This activate configuration for either perf or sysfs. Perf can have multiple + * active configs, selected per event, sysfs is limited to one. * * Increments the configuration descriptor active count and the global active * count. * * @cfg_hash: Hash value of the selected configuration name. */ -int cscfg_activate_config(unsigned long cfg_hash) +static int _cscfg_activate_config(unsigned long cfg_hash) { struct cscfg_config_desc *config_desc; int err = -EINVAL;
- mutex_lock(&cscfg_mutex); - list_for_each_entry(config_desc, &cscfg_mgr->config_desc_list, item) { if ((unsigned long)config_desc->event_ea->var == cfg_hash) { + /* must ensure that config cannot be unloaded in use */ err = cscfg_owner_get(config_desc->load_owner); if (err) @@ -790,6 +781,88 @@ int cscfg_activate_config(unsigned long cfg_hash) break; } } + 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_activation(struct cscfg_config_desc *config_desc, bool activate) +{ + unsigned long cfg_hash_desc; + int err = 0; + + mutex_lock(&cscfg_mutex); + + cfg_hash_desc = (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_desc); + if (!err) + cscfg_mgr->sysfs_active_config = cfg_hash_desc; + } else { + /* disable if matching current value */ + if (cscfg_mgr->sysfs_active_config == cfg_hash_desc) { + _cscfg_deactivate_config(cfg_hash_desc); + 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_preset(int preset) +{ + mutex_lock(&cscfg_mutex); + cscfg_mgr->sysfs_active_preset = preset; + mutex_unlock(&cscfg_mutex); +} + +/** + * 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); mutex_unlock(&cscfg_mutex);
return err; @@ -805,19 +878,8 @@ EXPORT_SYMBOL_GPL(cscfg_activate_config); */ void cscfg_deactivate_config(unsigned long cfg_hash) { - struct cscfg_config_desc *config_desc; - mutex_lock(&cscfg_mutex); - - 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; - } - } + _cscfg_deactivate_config(cfg_hash); mutex_unlock(&cscfg_mutex); } EXPORT_SYMBOL_GPL(cscfg_deactivate_config); @@ -826,7 +888,8 @@ 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. + * on the device and has been activated. A @cfg_hash value of 0 is used if the device + * is being programmed from sysfs, to select the current sysfs active config. * * If active and supported the CoreSight device @csdev will be programmed with the * configuration, using @preset parameters. @@ -850,6 +913,16 @@ int cscfg_csdev_enable_active_config(struct coresight_device *csdev, return 0;
mutex_lock(&cscfg_csdev_mutex); + + /* sysfs controlled coresight will call with cfg_hash == 0 */ + if (!cfg_hash) { + if (!cscfg_mgr->sysfs_active_config) + goto exit_unlock; + + cfg_hash = cscfg_mgr->sysfs_active_config; + preset = cscfg_mgr->sysfs_active_preset; + } + 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)) && @@ -863,6 +936,8 @@ int cscfg_csdev_enable_active_config(struct coresight_device *csdev, if (!err) csdev->active_cscfg_ctxt = (void *)config_csdev_active; } + +exit_unlock: mutex_unlock(&cscfg_csdev_mutex); return err; } diff --git a/drivers/hwtracing/coresight/coresight-syscfg.h b/drivers/hwtracing/coresight/coresight-syscfg.h index 1da37874f70f..e07e1b872806 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.h +++ b/drivers/hwtracing/coresight/coresight-syscfg.h @@ -28,6 +28,8 @@ * @load_order_list: Ordered list of owners for dynamically loaded configurations. * @sys_active_cnt: Total number of active config descriptor references. * @cfgfs_subsys: configfs subsystem used to manage configurations. + * @sysfs_active_config:Active config hash used if CoreSight controlled from sysfs. + * @sysfs_active_preset:Active preset index used if CoreSight controlled from sysfs. */ struct cscfg_manager { struct device dev; @@ -37,6 +39,8 @@ struct cscfg_manager { struct list_head load_order_list; atomic_t sys_active_cnt; struct configfs_subsystem cfgfs_subsys; + u32 sysfs_active_config; + int sysfs_active_preset; };
/* get reference to dev in cscfg_manager */ @@ -88,7 +92,8 @@ int cscfg_preload(void *owner_handle); const struct cscfg_feature_desc *cscfg_get_named_feat_desc(const char *name); int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc, int param_idx, u64 value); - +int cscfg_config_sysfs_activation(struct cscfg_config_desc *cfg_desc, bool activate); +void cscfg_config_sysfs_preset(int preset);
/* syscfg manager external API */ int cscfg_load_config_sets(struct cscfg_config_desc **cfg_descs,
Please remove the '.' on every patch header.
On Wed, May 12, 2021 at 10:17:48PM +0100, Mike Leach wrote:
Adds configfs attributes to allow a configuration to be enabled for use when sysfs is used to control CoreSight.
perf retains independent enabling of configurations.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../coresight/coresight-etm4x-core.c | 5 + .../coresight/coresight-syscfg-configfs.c | 67 +++++++++ .../coresight/coresight-syscfg-configfs.h | 2 + .../hwtracing/coresight/coresight-syscfg.c | 129 ++++++++++++++---- .../hwtracing/coresight/coresight-syscfg.h | 7 +- 5 files changed, 182 insertions(+), 28 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index b7a4aeaa2bc7..2637096c4621 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -688,6 +688,11 @@ static int etm4_enable_sysfs(struct coresight_device *csdev) struct etm4_enable_arg arg = { }; int ret;
- /* enable any config activated by configfs */
- ret = cscfg_csdev_enable_active_config(csdev, 0, 0);
- if (ret)
return ret;
- spin_lock(&drvdata->spinlock);
/* diff --git a/drivers/hwtracing/coresight/coresight-syscfg-configfs.c b/drivers/hwtracing/coresight/coresight-syscfg-configfs.c index 345a62f1b728..ae79ae8b1d7e 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg-configfs.c +++ b/drivers/hwtracing/coresight/coresight-syscfg-configfs.c @@ -6,6 +6,7 @@ #include <linux/configfs.h> +#include "coresight-config.h" #include "coresight-syscfg-configfs.h" /* create a default ci_type. */ @@ -87,9 +88,75 @@ static ssize_t cscfg_cfg_values_show(struct config_item *item, char *page) } CONFIGFS_ATTR_RO(cscfg_cfg_, values); +static ssize_t cscfg_cfg_activate_show(struct config_item *item, char *page) +{
- struct cscfg_fs_config *fs_config = container_of(to_config_group(item),
struct cscfg_fs_config, group);
- return scnprintf(page, PAGE_SIZE, "%d\n", fs_config->active);
+}
+static ssize_t cscfg_cfg_activate_store(struct config_item *item,
const char *page, size_t count)
+{
- struct cscfg_fs_config *fs_config = container_of(to_config_group(item),
struct cscfg_fs_config, group);
- int err;
- bool val;
- err = kstrtobool(page, &val);
- if (!err)
err = cscfg_config_sysfs_activation(fs_config->config_desc, val);
- if (!err) {
fs_config->active = val;
if (val)
cscfg_config_sysfs_preset(fs_config->preset);
- }
- return err ? err : count;
+} +CONFIGFS_ATTR(cscfg_cfg_, activate);
+static ssize_t cscfg_cfg_active_preset_show(struct config_item *item, char *page) +{
- struct cscfg_fs_config *fs_config = container_of(to_config_group(item),
struct cscfg_fs_config, group);
- return scnprintf(page, PAGE_SIZE, "%d\n", fs_config->preset);
+}
+static ssize_t cscfg_cfg_active_preset_store(struct config_item *item,
const char *page, size_t count)
+{
- struct cscfg_fs_config *fs_config = container_of(to_config_group(item),
struct cscfg_fs_config, group);
- int preset, err;
- err = kstrtoint(page, 0, &preset);
- if (!err) {
/*
* presets start at 1, and go up to max (15),
* but the config may provide fewer.
*/
if ((preset < 1) || (preset > fs_config->config_desc->nr_presets))
err = -EINVAL;
- }
- if (!err) {
/* set new value */
fs_config->preset = preset;
/* set on system if active */
if (fs_config->active)
cscfg_config_sysfs_preset(fs_config->preset);
- }
- return err ? err : count;
+} +CONFIGFS_ATTR(cscfg_cfg_, active_preset);
static struct configfs_attribute *cscfg_config_view_attrs[] = { &cscfg_cfg_attr_description, &cscfg_cfg_attr_feature_refs,
- &cscfg_cfg_attr_activate,
- &cscfg_cfg_attr_active_preset, NULL,
}; diff --git a/drivers/hwtracing/coresight/coresight-syscfg-configfs.h b/drivers/hwtracing/coresight/coresight-syscfg-configfs.h index ea1e54d29f7f..373d84d43268 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg-configfs.h +++ b/drivers/hwtracing/coresight/coresight-syscfg-configfs.h @@ -15,6 +15,8 @@ struct cscfg_fs_config { struct cscfg_config_desc *config_desc; struct config_group group;
- bool active;
- int preset;
}; /* container for feature view */ diff --git a/drivers/hwtracing/coresight/coresight-syscfg.c b/drivers/hwtracing/coresight/coresight-syscfg.c index 26c1a244c2b1..ab74e33b892b 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.c +++ b/drivers/hwtracing/coresight/coresight-syscfg.c @@ -743,32 +743,23 @@ void cscfg_csdev_reset_feats(struct coresight_device *csdev) } EXPORT_SYMBOL_GPL(cscfg_csdev_reset_feats); -/**
- 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.
+/*
- This activate configuration for either perf or sysfs. Perf can have multiple
*/
- active configs, selected per event, sysfs is limited to one.
- Increments the configuration descriptor active count and the global active
- count.
- @cfg_hash: Hash value of the selected configuration name.
-int cscfg_activate_config(unsigned long cfg_hash) +static int _cscfg_activate_config(unsigned long cfg_hash) { struct cscfg_config_desc *config_desc; int err = -EINVAL;
- mutex_lock(&cscfg_mutex);
- list_for_each_entry(config_desc, &cscfg_mgr->config_desc_list, item) { if ((unsigned long)config_desc->event_ea->var == cfg_hash) {
Spurious newline
/* must ensure that config cannot be unloaded in use */ err = cscfg_owner_get(config_desc->load_owner); if (err)
@@ -790,6 +781,88 @@ int cscfg_activate_config(unsigned long cfg_hash) break; } }
- 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_activation(struct cscfg_config_desc *config_desc, bool activate) +{
- unsigned long cfg_hash_desc;
- int err = 0;
- mutex_lock(&cscfg_mutex);
- cfg_hash_desc = (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_desc);
if (!err)
cscfg_mgr->sysfs_active_config = cfg_hash_desc;
- } else {
/* disable if matching current value */
if (cscfg_mgr->sysfs_active_config == cfg_hash_desc) {
_cscfg_deactivate_config(cfg_hash_desc);
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_preset(int preset) +{
- mutex_lock(&cscfg_mutex);
- cscfg_mgr->sysfs_active_preset = preset;
- mutex_unlock(&cscfg_mutex);
+}
+/**
- 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); mutex_unlock(&cscfg_mutex);
return err; @@ -805,19 +878,8 @@ EXPORT_SYMBOL_GPL(cscfg_activate_config); */ void cscfg_deactivate_config(unsigned long cfg_hash) {
- struct cscfg_config_desc *config_desc;
- mutex_lock(&cscfg_mutex);
- 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;
}
- }
- _cscfg_deactivate_config(cfg_hash); mutex_unlock(&cscfg_mutex);
} EXPORT_SYMBOL_GPL(cscfg_deactivate_config); @@ -826,7 +888,8 @@ 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.
- on the device and has been activated. A @cfg_hash value of 0 is used if the device
- is being programmed from sysfs, to select the current sysfs active config.
- If active and supported the CoreSight device @csdev will be programmed with the
- configuration, using @preset parameters.
@@ -850,6 +913,16 @@ int cscfg_csdev_enable_active_config(struct coresight_device *csdev, return 0; mutex_lock(&cscfg_csdev_mutex);
- /* sysfs controlled coresight will call with cfg_hash == 0 */
- if (!cfg_hash) {
if (!cscfg_mgr->sysfs_active_config)
goto exit_unlock;
cfg_hash = cscfg_mgr->sysfs_active_config;
preset = cscfg_mgr->sysfs_active_preset;
- }
I'm worried about this snippet. For the time being it works but the function is now entangled with the "if (attr->config2 ...) of etm4_parse_event_config(). That being said I spent a fair amount of time trying to find a better solution and I can't come up with one. The best option is probably to keep it for now.
- 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)) &&
@@ -863,6 +936,8 @@ int cscfg_csdev_enable_active_config(struct coresight_device *csdev, if (!err) csdev->active_cscfg_ctxt = (void *)config_csdev_active; }
+exit_unlock: mutex_unlock(&cscfg_csdev_mutex); return err; } diff --git a/drivers/hwtracing/coresight/coresight-syscfg.h b/drivers/hwtracing/coresight/coresight-syscfg.h index 1da37874f70f..e07e1b872806 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.h +++ b/drivers/hwtracing/coresight/coresight-syscfg.h @@ -28,6 +28,8 @@
- @load_order_list: Ordered list of owners for dynamically loaded configurations.
- @sys_active_cnt: Total number of active config descriptor references.
- @cfgfs_subsys: configfs subsystem used to manage configurations.
- @sysfs_active_config:Active config hash used if CoreSight controlled from sysfs.
*/
- @sysfs_active_preset:Active preset index used if CoreSight controlled from sysfs.
struct cscfg_manager { struct device dev; @@ -37,6 +39,8 @@ struct cscfg_manager { struct list_head load_order_list; atomic_t sys_active_cnt; struct configfs_subsystem cfgfs_subsys;
- u32 sysfs_active_config;
- int sysfs_active_preset;
}; /* get reference to dev in cscfg_manager */ @@ -88,7 +92,8 @@ int cscfg_preload(void *owner_handle); const struct cscfg_feature_desc *cscfg_get_named_feat_desc(const char *name); int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc, int param_idx, u64 value);
+int cscfg_config_sysfs_activation(struct cscfg_config_desc *cfg_desc, bool activate); +void cscfg_config_sysfs_preset(int preset);
With the above:
Reviewed-by: Mathieu Poirier mathieu.poirier@linaro.org
/* syscfg manager external API */ int cscfg_load_config_sets(struct cscfg_config_desc **cfg_descs, -- 2.17.1
Hi Mathieu,
On Tue, 18 May 2021 at 20:36, Mathieu Poirier mathieu.poirier@linaro.org wrote:
Please remove the '.' on every patch header.
Sorry - will do. Correct punctuation seems to be a habit that is hard to break!
On Wed, May 12, 2021 at 10:17:48PM +0100, Mike Leach wrote:
Adds configfs attributes to allow a configuration to be enabled for use when sysfs is used to control CoreSight.
perf retains independent enabling of configurations.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../coresight/coresight-etm4x-core.c | 5 + .../coresight/coresight-syscfg-configfs.c | 67 +++++++++ .../coresight/coresight-syscfg-configfs.h | 2 + .../hwtracing/coresight/coresight-syscfg.c | 129 ++++++++++++++---- .../hwtracing/coresight/coresight-syscfg.h | 7 +- 5 files changed, 182 insertions(+), 28 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index b7a4aeaa2bc7..2637096c4621 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -688,6 +688,11 @@ static int etm4_enable_sysfs(struct coresight_device *csdev) struct etm4_enable_arg arg = { }; int ret;
/* enable any config activated by configfs */
ret = cscfg_csdev_enable_active_config(csdev, 0, 0);
if (ret)
return ret;
spin_lock(&drvdata->spinlock); /*
diff --git a/drivers/hwtracing/coresight/coresight-syscfg-configfs.c b/drivers/hwtracing/coresight/coresight-syscfg-configfs.c index 345a62f1b728..ae79ae8b1d7e 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg-configfs.c +++ b/drivers/hwtracing/coresight/coresight-syscfg-configfs.c @@ -6,6 +6,7 @@
#include <linux/configfs.h>
+#include "coresight-config.h" #include "coresight-syscfg-configfs.h"
/* create a default ci_type. */ @@ -87,9 +88,75 @@ static ssize_t cscfg_cfg_values_show(struct config_item *item, char *page) } CONFIGFS_ATTR_RO(cscfg_cfg_, values);
+static ssize_t cscfg_cfg_activate_show(struct config_item *item, char *page) +{
struct cscfg_fs_config *fs_config = container_of(to_config_group(item),
struct cscfg_fs_config, group);
return scnprintf(page, PAGE_SIZE, "%d\n", fs_config->active);
+}
+static ssize_t cscfg_cfg_activate_store(struct config_item *item,
const char *page, size_t count)
+{
struct cscfg_fs_config *fs_config = container_of(to_config_group(item),
struct cscfg_fs_config, group);
int err;
bool val;
err = kstrtobool(page, &val);
if (!err)
err = cscfg_config_sysfs_activation(fs_config->config_desc, val);
if (!err) {
fs_config->active = val;
if (val)
cscfg_config_sysfs_preset(fs_config->preset);
}
return err ? err : count;
+} +CONFIGFS_ATTR(cscfg_cfg_, activate);
+static ssize_t cscfg_cfg_active_preset_show(struct config_item *item, char *page) +{
struct cscfg_fs_config *fs_config = container_of(to_config_group(item),
struct cscfg_fs_config, group);
return scnprintf(page, PAGE_SIZE, "%d\n", fs_config->preset);
+}
+static ssize_t cscfg_cfg_active_preset_store(struct config_item *item,
const char *page, size_t count)
+{
struct cscfg_fs_config *fs_config = container_of(to_config_group(item),
struct cscfg_fs_config, group);
int preset, err;
err = kstrtoint(page, 0, &preset);
if (!err) {
/*
* presets start at 1, and go up to max (15),
* but the config may provide fewer.
*/
if ((preset < 1) || (preset > fs_config->config_desc->nr_presets))
err = -EINVAL;
}
if (!err) {
/* set new value */
fs_config->preset = preset;
/* set on system if active */
if (fs_config->active)
cscfg_config_sysfs_preset(fs_config->preset);
}
return err ? err : count;
+} +CONFIGFS_ATTR(cscfg_cfg_, active_preset);
static struct configfs_attribute *cscfg_config_view_attrs[] = { &cscfg_cfg_attr_description, &cscfg_cfg_attr_feature_refs,
&cscfg_cfg_attr_activate,
&cscfg_cfg_attr_active_preset, NULL,
};
diff --git a/drivers/hwtracing/coresight/coresight-syscfg-configfs.h b/drivers/hwtracing/coresight/coresight-syscfg-configfs.h index ea1e54d29f7f..373d84d43268 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg-configfs.h +++ b/drivers/hwtracing/coresight/coresight-syscfg-configfs.h @@ -15,6 +15,8 @@ struct cscfg_fs_config { struct cscfg_config_desc *config_desc; struct config_group group;
bool active;
int preset;
};
/* container for feature view */ diff --git a/drivers/hwtracing/coresight/coresight-syscfg.c b/drivers/hwtracing/coresight/coresight-syscfg.c index 26c1a244c2b1..ab74e33b892b 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.c +++ b/drivers/hwtracing/coresight/coresight-syscfg.c @@ -743,32 +743,23 @@ void cscfg_csdev_reset_feats(struct coresight_device *csdev) } EXPORT_SYMBOL_GPL(cscfg_csdev_reset_feats);
-/**
- 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.
+/*
- This activate configuration for either perf or sysfs. Perf can have multiple
*/
- active configs, selected per event, sysfs is limited to one.
- Increments the configuration descriptor active count and the global active
- count.
- @cfg_hash: Hash value of the selected configuration name.
-int cscfg_activate_config(unsigned long cfg_hash) +static int _cscfg_activate_config(unsigned long cfg_hash) { struct cscfg_config_desc *config_desc; int err = -EINVAL;
mutex_lock(&cscfg_mutex);
list_for_each_entry(config_desc, &cscfg_mgr->config_desc_list, item) { if ((unsigned long)config_desc->event_ea->var == cfg_hash) {
Spurious newline
/* must ensure that config cannot be unloaded in use */ err = cscfg_owner_get(config_desc->load_owner); if (err)
@@ -790,6 +781,88 @@ int cscfg_activate_config(unsigned long cfg_hash) break; } }
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_activation(struct cscfg_config_desc *config_desc, bool activate) +{
unsigned long cfg_hash_desc;
int err = 0;
mutex_lock(&cscfg_mutex);
cfg_hash_desc = (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_desc);
if (!err)
cscfg_mgr->sysfs_active_config = cfg_hash_desc;
} else {
/* disable if matching current value */
if (cscfg_mgr->sysfs_active_config == cfg_hash_desc) {
_cscfg_deactivate_config(cfg_hash_desc);
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_preset(int preset) +{
mutex_lock(&cscfg_mutex);
cscfg_mgr->sysfs_active_preset = preset;
mutex_unlock(&cscfg_mutex);
+}
+/**
- 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); mutex_unlock(&cscfg_mutex); return err;
@@ -805,19 +878,8 @@ EXPORT_SYMBOL_GPL(cscfg_activate_config); */ void cscfg_deactivate_config(unsigned long cfg_hash) {
struct cscfg_config_desc *config_desc;
mutex_lock(&cscfg_mutex);
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;
}
}
_cscfg_deactivate_config(cfg_hash); mutex_unlock(&cscfg_mutex);
} EXPORT_SYMBOL_GPL(cscfg_deactivate_config); @@ -826,7 +888,8 @@ 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.
- on the device and has been activated. A @cfg_hash value of 0 is used if the device
- is being programmed from sysfs, to select the current sysfs active config.
- If active and supported the CoreSight device @csdev will be programmed with the
- configuration, using @preset parameters.
@@ -850,6 +913,16 @@ int cscfg_csdev_enable_active_config(struct coresight_device *csdev, return 0;
mutex_lock(&cscfg_csdev_mutex);
/* sysfs controlled coresight will call with cfg_hash == 0 */
if (!cfg_hash) {
if (!cscfg_mgr->sysfs_active_config)
goto exit_unlock;
cfg_hash = cscfg_mgr->sysfs_active_config;
preset = cscfg_mgr->sysfs_active_preset;
}
I'm worried about this snippet. For the time being it works but the function is now entangled with the "if (attr->config2 ...) of etm4_parse_event_config(). That being said I spent a fair amount of time trying to find a better solution and I can't come up with one. The best option is probably to keep it for now.
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)) &&
@@ -863,6 +936,8 @@ int cscfg_csdev_enable_active_config(struct coresight_device *csdev, if (!err) csdev->active_cscfg_ctxt = (void *)config_csdev_active; }
+exit_unlock: mutex_unlock(&cscfg_csdev_mutex); return err; } diff --git a/drivers/hwtracing/coresight/coresight-syscfg.h b/drivers/hwtracing/coresight/coresight-syscfg.h index 1da37874f70f..e07e1b872806 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.h +++ b/drivers/hwtracing/coresight/coresight-syscfg.h @@ -28,6 +28,8 @@
- @load_order_list: Ordered list of owners for dynamically loaded configurations.
- @sys_active_cnt: Total number of active config descriptor references.
- @cfgfs_subsys: configfs subsystem used to manage configurations.
- @sysfs_active_config:Active config hash used if CoreSight controlled from sysfs.
*/
- @sysfs_active_preset:Active preset index used if CoreSight controlled from sysfs.
struct cscfg_manager { struct device dev; @@ -37,6 +39,8 @@ struct cscfg_manager { struct list_head load_order_list; atomic_t sys_active_cnt; struct configfs_subsystem cfgfs_subsys;
u32 sysfs_active_config;
int sysfs_active_preset;
};
/* get reference to dev in cscfg_manager */ @@ -88,7 +92,8 @@ int cscfg_preload(void *owner_handle); const struct cscfg_feature_desc *cscfg_get_named_feat_desc(const char *name); int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc, int param_idx, u64 value);
+int cscfg_config_sysfs_activation(struct cscfg_config_desc *cfg_desc, bool activate); +void cscfg_config_sysfs_preset(int preset);
With the above:
Reviewed-by: Mathieu Poirier mathieu.poirier@linaro.org
Thanks.
Regards
Mike
/* syscfg manager external API */ int cscfg_load_config_sets(struct cscfg_config_desc **cfg_descs, -- 2.17.1
Adds in API to allow features which define device resources, to check resource availability on load, and allocate on enable.
1) Check on load ensures that the resources required by the feature do not exceed the maximum avialable on the device.
2) Allocate on enable ensures that sufficient unused resources are available to satifsfy the feature on enable. Allocate can also be used to resolve any resource selection requirements - i.e. select an available resource from a pool of resources.
Signed-off-by: Mike Leach mike.leach@linaro.org --- .../hwtracing/coresight/coresight-config.c | 71 +++++++++++++++--- .../hwtracing/coresight/coresight-config.h | 36 +++++++++- .../hwtracing/coresight/coresight-syscfg.c | 72 +++++++++++++++++-- 3 files changed, 163 insertions(+), 16 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-config.c b/drivers/hwtracing/coresight/coresight-config.c index 3c501e027bc0..fdfda1975188 100644 --- a/drivers/hwtracing/coresight/coresight-config.c +++ b/drivers/hwtracing/coresight/coresight-config.c @@ -23,6 +23,10 @@ static void cscfg_set_reg(struct cscfg_regval_csdev *reg_csdev) u32 *p_val32 = (u32 *)reg_csdev->driver_regval; u32 tmp32 = reg_csdev->reg_desc.val32;
+ /* resource mapped registers may have custom handling in the device */ + if (!reg_csdev->driver_regval) + return; + if (reg_csdev->reg_desc.type & CS_CFG_REG_TYPE_VAL_64BIT) { *((u64 *)reg_csdev->driver_regval) = reg_csdev->reg_desc.val64; return; @@ -43,6 +47,8 @@ static void cscfg_save_reg(struct cscfg_regval_csdev *reg_csdev) { if (!(reg_csdev->reg_desc.type & CS_CFG_REG_TYPE_VAL_SAVE)) return; + if (!reg_csdev->driver_regval) + return; if (reg_csdev->reg_desc.type & CS_CFG_REG_TYPE_VAL_64BIT) reg_csdev->reg_desc.val64 = *(u64 *)(reg_csdev->driver_regval); else @@ -73,15 +79,20 @@ static void cscfg_init_reg_param(struct cscfg_feature_csdev *feat_csdev, /* set values into the driver locations referenced in cscfg_reg_csdev */ static int cscfg_set_on_enable(struct cscfg_feature_csdev *feat_csdev) { - int i; + int i, err = 0;
spin_lock(feat_csdev->drv_spinlock); for (i = 0; i < feat_csdev->nr_regs; i++) cscfg_set_reg(&feat_csdev->regs_csdev[i]); spin_unlock(feat_csdev->drv_spinlock); + + if (feat_csdev->feat_ops->set_on_enable) + err = feat_csdev->feat_ops->set_on_enable(feat_csdev); + dev_dbg(&feat_csdev->csdev->dev, "Feature %s: %s", - feat_csdev->feat_desc->name, "set on enable"); - return 0; + feat_csdev->feat_desc->name, + err ? "error on enable" : "set on enable"); + return err; }
/* copy back values from the driver locations referenced in cscfg_reg_csdev */ @@ -89,6 +100,9 @@ static void cscfg_save_on_disable(struct cscfg_feature_csdev *feat_csdev) { int i;
+ if (feat_csdev->feat_ops->clear_on_disable) + feat_csdev->feat_ops->clear_on_disable(feat_csdev); + spin_lock(feat_csdev->drv_spinlock); for (i = 0; i < feat_csdev->nr_regs; i++) cscfg_save_reg(&feat_csdev->regs_csdev[i]); @@ -217,6 +231,37 @@ static int cscfg_update_curr_params(struct cscfg_config_csdev *config_csdev) return 0; }
+static void cscfg_clear_res_config(struct cscfg_config_csdev *config_csdev) +{ + int i; + struct cscfg_feature_csdev *feat_csdev; + + for (i = 0; i < config_csdev->nr_feat; i++) { + feat_csdev = config_csdev->feats_csdev[i]; + if (feat_csdev->feat_ops->clear_feat_res) + feat_csdev->feat_ops->clear_feat_res(feat_csdev); + } +} + +#define FEAT_ALLOC_RES_FMT "coresight-syscfg: Insufficient resource to enable feature %s\n" +static int cscfg_alloc_res_config(struct cscfg_config_csdev *config_csdev) +{ + int err = 0, i; + struct cscfg_feature_csdev *feat_csdev; + + for (i = 0; i < config_csdev->nr_feat; i++) { + feat_csdev = config_csdev->feats_csdev[i]; + if (feat_csdev->feat_ops->alloc_feat_res) + err = feat_csdev->feat_ops->alloc_feat_res(feat_csdev); + if (err) { + pr_warn(FEAT_ALLOC_RES_FMT, feat_csdev->feat_desc->name); + cscfg_clear_res_config(config_csdev); + break; + } + } + return err; +} + /* * Configuration values will be programmed into the driver locations if enabling, or read * from relevant locations on disable. @@ -227,19 +272,29 @@ static int cscfg_prog_config(struct cscfg_config_csdev *config_csdev, bool enabl struct cscfg_feature_csdev *feat_csdev; struct coresight_device *csdev;
+ /* check we have resources to enable this */ + if (enable) { + err = cscfg_alloc_res_config(config_csdev); + if (err) + return err; + } else + cscfg_clear_res_config(config_csdev); + for (i = 0; i < config_csdev->nr_feat; i++) { feat_csdev = config_csdev->feats_csdev[i]; csdev = feat_csdev->csdev; dev_dbg(&csdev->dev, "cfg %s; %s feature:%s", config_csdev->config_desc->name, enable ? "enable" : "disable", feat_csdev->feat_desc->name);
- if (enable) + if (enable) { err = cscfg_set_on_enable(feat_csdev); - else + if (err) { + /* failed to enable - release any resources */ + cscfg_clear_res_config(config_csdev); + break; + } + } else cscfg_save_on_disable(feat_csdev); - - if (err) - break; } return err; } diff --git a/drivers/hwtracing/coresight/coresight-config.h b/drivers/hwtracing/coresight/coresight-config.h index 9bd44b940add..0e46832df993 100644 --- a/drivers/hwtracing/coresight/coresight-config.h +++ b/drivers/hwtracing/coresight/coresight-config.h @@ -199,6 +199,8 @@ struct cscfg_parameter_csdev { * @params_csdev: current parameter values on this device * @nr_regs: number of registers to be programmed. * @regs_csdev: Programming details for the registers + * @res_used: Device specific context for resources used on enable. + * @feat_ops: Feature operations to support alloc/release of resources. */ struct cscfg_feature_csdev { const struct cscfg_feature_desc *feat_desc; @@ -209,6 +211,8 @@ struct cscfg_feature_csdev { struct cscfg_parameter_csdev *params_csdev; int nr_regs; struct cscfg_regval_csdev *regs_csdev; + void *res_used; + struct cscfg_csdev_feat_ops *feat_ops; };
/** @@ -238,14 +242,44 @@ struct cscfg_config_csdev { * Coresight device operations. * * Registered coresight devices provide these operations to manage feature - * instances compatible with the device hardware and drivers + * instances compatible with the device hardware and drivers. + * + * The @check_feat_res function can be used at load time to see if the device has + * sufficient resources to support the feature. This takes into account all resources + * on the device. e.g. if the feature requires three counters, and the device has a + * total of two implemented counters, this will return a -ENODEV error. + * + * The @alloc_feat_res function checks if there are sufficient unallocated resources + * left to enable the feature. This allows for multiple features being loaded that + * may compete for resources and also selects and allocates resources at enable time. + * e.g. if a feature requires two comparators, and there is only one left unallocated, + * then this will return a -ENOSPC error. * * @load_feat: Pass a feature descriptor into the device and create the * loaded feature instance (struct cscfg_feature_csdev). + * @check_feat_res: Check that the coresight device has sufficient resources to + * load the feature. Optional function. Return -ENODEV if this device + * does not have enough resources. Called before @load_feat for feature + * to ensure all feature can be installed on device. + * @alloc_feat_res: Allocate release feature resources when enabling a feature on the device + * . Optional function. Returns -ENOSPC if the device has not enough available + * resources to enable the feature. Called before registers programmed. + * @clear_feat_res: Release allocated resources. Must be implemented if @alloc_feat_res is + * implemented. + * @set_on_enable : Optional programming specific to device. Called immediately after + * generic register programming operation. + * @clear_on_disable: Optional programming specific to device. Called before generic register + * save operation. */ struct cscfg_csdev_feat_ops { int (*load_feat)(struct coresight_device *csdev, struct cscfg_feature_csdev *feat_csdev); + int (*check_feat_res)(struct coresight_device *csdev, + struct cscfg_feature_desc *feat_desc); + int (*alloc_feat_res)(struct cscfg_feature_csdev *feat_csdev); + void (*clear_feat_res)(struct cscfg_feature_csdev *feat_csdev); + int (*set_on_enable)(struct cscfg_feature_csdev *feat_csdev); + void (*clear_on_disable)(struct cscfg_feature_csdev *feat_csdev); };
/* coresight config helper functions*/ diff --git a/drivers/hwtracing/coresight/coresight-syscfg.c b/drivers/hwtracing/coresight/coresight-syscfg.c index ab74e33b892b..984459c8f168 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.c +++ b/drivers/hwtracing/coresight/coresight-syscfg.c @@ -121,7 +121,8 @@ static int cscfg_add_cfg_to_csdevs(struct cscfg_config_desc *config_desc) * 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) +cscfg_alloc_csdev_feat(struct coresight_device *csdev, struct cscfg_feature_desc *feat_desc, + struct cscfg_csdev_feat_ops *ops) { struct cscfg_feature_csdev *feat_csdev = NULL; struct device *dev = csdev->dev.parent; @@ -165,9 +166,16 @@ cscfg_alloc_csdev_feat(struct coresight_device *csdev, struct cscfg_feature_desc if (!feat_csdev->regs_csdev) return NULL;
+ /* copy the static register info */ + for (i = 0; i < feat_desc->nr_regs; i++) { + memcpy(&feat_csdev->regs_csdev[i].reg_desc, + &feat_desc->regs_desc[i], sizeof(struct cscfg_regval_desc)); + } + /* load the feature default values */ feat_csdev->feat_desc = feat_desc; feat_csdev->csdev = csdev; + feat_csdev->feat_ops = ops;
return feat_csdev; } @@ -180,10 +188,7 @@ static int cscfg_load_feat_csdev(struct coresight_device *csdev, struct cscfg_feature_csdev *feat_csdev; int err;
- if (!ops->load_feat) - return -EINVAL; - - feat_csdev = cscfg_alloc_csdev_feat(csdev, feat_desc); + feat_csdev = cscfg_alloc_csdev_feat(csdev, feat_desc, ops); if (!feat_csdev) return -ENOMEM;
@@ -201,6 +206,57 @@ static int cscfg_load_feat_csdev(struct coresight_device *csdev, return 0; }
+#define WARN_RES_FMT "coresight-syscfg: Insufficient resources to load feature %s in device %s\n" +#define PARAM_ERR_FMT "coresight-syscfg: Cannot load feature %s. Invalid parameter index\n" + +/* + * Check if device supports resource check function, and check for sufficient resources + * to load feature onto device. Validate parameter references. + * + * Load feature if sufficient resources & valid parameter references. + */ +static int cscfg_check_feat_res_load(struct coresight_device *csdev, + struct cscfg_feature_desc *feat_desc, + struct cscfg_csdev_feat_ops *ops) +{ + int err = 0, i; + + /* if we cannot load - fail early */ + if (!ops->load_feat) + return -EINVAL; + + /* ensure matched resource ops */ + if ((ops->alloc_feat_res && !ops->clear_feat_res) || + (!ops->alloc_feat_res && ops->clear_feat_res)) + return -EINVAL; + + /* ensure that the coresight device can support the feature */ + if (ops->check_feat_res) { + err = ops->check_feat_res(csdev, feat_desc); + if (err == -ENODEV) { + /* treat as mismatch, warn and continue */ + pr_warn(WARN_RES_FMT, feat_desc->name, + dev_name(&csdev->dev)); + return 0; + } + } + + /* check parameter indexes are valid */ + for (i = 0; i < feat_desc->nr_regs; i++) { + if (feat_desc->regs_desc[i].type & CS_CFG_REG_TYPE_VAL_PARAM) { + if (feat_desc->regs_desc[i].param_idx >= feat_desc->nr_params) { + pr_err(PARAM_ERR_FMT, feat_desc->name); + return -EINVAL; + } + } + } + + /* sufficient resources - load if no other error */ + if (!err) + err = cscfg_load_feat_csdev(csdev, feat_desc, ops); + return err; +} + /* * Add feature to any matching devices - call with mutex locked. * Iterates through devices - any device that matches the feature will be @@ -213,7 +269,9 @@ static int cscfg_add_feat_to_csdevs(struct cscfg_feature_desc *feat_desc)
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); + /* check sufficient resources and load feature */ + err = cscfg_check_feat_res_load(csdev_item->csdev, feat_desc, + &csdev_item->ops); if (err) return err; } @@ -616,7 +674,7 @@ static int cscfg_add_feats_csdev(struct coresight_device *csdev,
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); + err = cscfg_check_feat_res_load(csdev, feat_desc, ops); if (err) break; }
Hi Mike,
On Wed, May 12, 2021 at 10:17:49PM +0100, Mike Leach wrote:
Adds in API to allow features which define device resources, to check resource availability on load, and allocate on enable.
- Check on load ensures that the resources required by the feature do
not exceed the maximum avialable on the device.
- Allocate on enable ensures that sufficient unused resources are
available to satifsfy the feature on enable. Allocate can also be used to resolve any resource selection requirements - i.e. select an available resource from a pool of resources.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-config.c | 71 +++++++++++++++--- .../hwtracing/coresight/coresight-config.h | 36 +++++++++- .../hwtracing/coresight/coresight-syscfg.c | 72 +++++++++++++++++-- 3 files changed, 163 insertions(+), 16 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-config.c b/drivers/hwtracing/coresight/coresight-config.c index 3c501e027bc0..fdfda1975188 100644 --- a/drivers/hwtracing/coresight/coresight-config.c +++ b/drivers/hwtracing/coresight/coresight-config.c @@ -23,6 +23,10 @@ static void cscfg_set_reg(struct cscfg_regval_csdev *reg_csdev) u32 *p_val32 = (u32 *)reg_csdev->driver_regval; u32 tmp32 = reg_csdev->reg_desc.val32;
- /* resource mapped registers may have custom handling in the device */
- if (!reg_csdev->driver_regval)
return;
I am curious as to why it is needed now and wasn't in the patchset that introduced the complex configuration feature.
if (reg_csdev->reg_desc.type & CS_CFG_REG_TYPE_VAL_64BIT) { *((u64 *)reg_csdev->driver_regval) = reg_csdev->reg_desc.val64; return; @@ -43,6 +47,8 @@ static void cscfg_save_reg(struct cscfg_regval_csdev *reg_csdev) { if (!(reg_csdev->reg_desc.type & CS_CFG_REG_TYPE_VAL_SAVE)) return;
- if (!reg_csdev->driver_regval)
if (reg_csdev->reg_desc.type & CS_CFG_REG_TYPE_VAL_64BIT) reg_csdev->reg_desc.val64 = *(u64 *)(reg_csdev->driver_regval); elsereturn;
@@ -73,15 +79,20 @@ static void cscfg_init_reg_param(struct cscfg_feature_csdev *feat_csdev, /* set values into the driver locations referenced in cscfg_reg_csdev */ static int cscfg_set_on_enable(struct cscfg_feature_csdev *feat_csdev) {
- int i;
- int i, err = 0;
spin_lock(feat_csdev->drv_spinlock); for (i = 0; i < feat_csdev->nr_regs; i++) cscfg_set_reg(&feat_csdev->regs_csdev[i]); spin_unlock(feat_csdev->drv_spinlock);
- if (feat_csdev->feat_ops->set_on_enable)
err = feat_csdev->feat_ops->set_on_enable(feat_csdev);
- dev_dbg(&feat_csdev->csdev->dev, "Feature %s: %s",
feat_csdev->feat_desc->name, "set on enable");
- return 0;
feat_csdev->feat_desc->name,
err ? "error on enable" : "set on enable");
- return err;
} /* copy back values from the driver locations referenced in cscfg_reg_csdev */ @@ -89,6 +100,9 @@ static void cscfg_save_on_disable(struct cscfg_feature_csdev *feat_csdev) { int i;
- if (feat_csdev->feat_ops->clear_on_disable)
feat_csdev->feat_ops->clear_on_disable(feat_csdev);
- spin_lock(feat_csdev->drv_spinlock); for (i = 0; i < feat_csdev->nr_regs; i++) cscfg_save_reg(&feat_csdev->regs_csdev[i]);
@@ -217,6 +231,37 @@ static int cscfg_update_curr_params(struct cscfg_config_csdev *config_csdev) return 0; } +static void cscfg_clear_res_config(struct cscfg_config_csdev *config_csdev) +{
- int i;
- struct cscfg_feature_csdev *feat_csdev;
- for (i = 0; i < config_csdev->nr_feat; i++) {
feat_csdev = config_csdev->feats_csdev[i];
if (feat_csdev->feat_ops->clear_feat_res)
feat_csdev->feat_ops->clear_feat_res(feat_csdev);
- }
+}
+#define FEAT_ALLOC_RES_FMT "coresight-syscfg: Insufficient resource to enable feature %s\n" +static int cscfg_alloc_res_config(struct cscfg_config_csdev *config_csdev) +{
- int err = 0, i;
- struct cscfg_feature_csdev *feat_csdev;
- for (i = 0; i < config_csdev->nr_feat; i++) {
feat_csdev = config_csdev->feats_csdev[i];
if (feat_csdev->feat_ops->alloc_feat_res)
err = feat_csdev->feat_ops->alloc_feat_res(feat_csdev);
if (err) {
pr_warn(FEAT_ALLOC_RES_FMT, feat_csdev->feat_desc->name);
cscfg_clear_res_config(config_csdev);
break;
}
- }
- return err;
+}
/*
- Configuration values will be programmed into the driver locations if enabling, or read
- from relevant locations on disable.
@@ -227,19 +272,29 @@ static int cscfg_prog_config(struct cscfg_config_csdev *config_csdev, bool enabl struct cscfg_feature_csdev *feat_csdev; struct coresight_device *csdev;
- /* check we have resources to enable this */
- if (enable) {
err = cscfg_alloc_res_config(config_csdev);
if (err)
return err;
- } else
cscfg_clear_res_config(config_csdev);
- for (i = 0; i < config_csdev->nr_feat; i++) { feat_csdev = config_csdev->feats_csdev[i]; csdev = feat_csdev->csdev; dev_dbg(&csdev->dev, "cfg %s; %s feature:%s", config_csdev->config_desc->name, enable ? "enable" : "disable", feat_csdev->feat_desc->name);
if (enable)
if (enable) { err = cscfg_set_on_enable(feat_csdev);
else
if (err) {
/* failed to enable - release any resources */
cscfg_clear_res_config(config_csdev);
break;
}
} else cscfg_save_on_disable(feat_csdev);
if (err)
break;
Is all of the above related to what this patchset is providing or an enhancement of the original feature?
} return err; } diff --git a/drivers/hwtracing/coresight/coresight-config.h b/drivers/hwtracing/coresight/coresight-config.h index 9bd44b940add..0e46832df993 100644 --- a/drivers/hwtracing/coresight/coresight-config.h +++ b/drivers/hwtracing/coresight/coresight-config.h @@ -199,6 +199,8 @@ struct cscfg_parameter_csdev {
- @params_csdev: current parameter values on this device
- @nr_regs: number of registers to be programmed.
- @regs_csdev: Programming details for the registers
- @res_used: Device specific context for resources used on enable.
*/
- @feat_ops: Feature operations to support alloc/release of resources.
struct cscfg_feature_csdev { const struct cscfg_feature_desc *feat_desc; @@ -209,6 +211,8 @@ struct cscfg_feature_csdev { struct cscfg_parameter_csdev *params_csdev; int nr_regs; struct cscfg_regval_csdev *regs_csdev;
- void *res_used;
This isn't used in this patch. Please move it to the patch that will do so.
- struct cscfg_csdev_feat_ops *feat_ops;
}; /** @@ -238,14 +242,44 @@ struct cscfg_config_csdev {
- Coresight device operations.
- Registered coresight devices provide these operations to manage feature
- instances compatible with the device hardware and drivers
- instances compatible with the device hardware and drivers.
- The @check_feat_res function can be used at load time to see if the device has
- sufficient resources to support the feature. This takes into account all resources
- on the device. e.g. if the feature requires three counters, and the device has a
s/device./device,
- total of two implemented counters, this will return a -ENODEV error.
- The @alloc_feat_res function checks if there are sufficient unallocated resources
- left to enable the feature. This allows for multiple features being loaded that
- may compete for resources and also selects and allocates resources at enable time.
This is the second time so I'm wondering if this is an accepted way of writing things in english.
- e.g. if a feature requires two comparators, and there is only one left unallocated,
- then this will return a -ENOSPC error.
- @load_feat: Pass a feature descriptor into the device and create the
loaded feature instance (struct cscfg_feature_csdev).
- @check_feat_res: Check that the coresight device has sufficient resources to
load the feature. Optional function. Return -ENODEV if this device
does not have enough resources. Called before @load_feat for feature
to ensure all feature can be installed on device.
- @alloc_feat_res: Allocate release feature resources when enabling a feature on the device
. Optional function. Returns -ENOSPC if the device has not enough available
The '.' at the beginning of the line looks wierd.
resources to enable the feature. Called before registers programmed.
- @clear_feat_res: Release allocated resources. Must be implemented if @alloc_feat_res is
implemented.
- @set_on_enable : Optional programming specific to device. Called immediately after
generic register programming operation.
- @clear_on_disable: Optional programming specific to device. Called before generic register
*/
save operation.
struct cscfg_csdev_feat_ops { int (*load_feat)(struct coresight_device *csdev, struct cscfg_feature_csdev *feat_csdev);
- int (*check_feat_res)(struct coresight_device *csdev,
struct cscfg_feature_desc *feat_desc);
- int (*alloc_feat_res)(struct cscfg_feature_csdev *feat_csdev);
- void (*clear_feat_res)(struct cscfg_feature_csdev *feat_csdev);
- int (*set_on_enable)(struct cscfg_feature_csdev *feat_csdev);
- void (*clear_on_disable)(struct cscfg_feature_csdev *feat_csdev);
}; /* coresight config helper functions*/ diff --git a/drivers/hwtracing/coresight/coresight-syscfg.c b/drivers/hwtracing/coresight/coresight-syscfg.c index ab74e33b892b..984459c8f168 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.c +++ b/drivers/hwtracing/coresight/coresight-syscfg.c @@ -121,7 +121,8 @@ static int cscfg_add_cfg_to_csdevs(struct cscfg_config_desc *config_desc)
- 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) +cscfg_alloc_csdev_feat(struct coresight_device *csdev, struct cscfg_feature_desc *feat_desc,
struct cscfg_csdev_feat_ops *ops)
{ struct cscfg_feature_csdev *feat_csdev = NULL; struct device *dev = csdev->dev.parent; @@ -165,9 +166,16 @@ cscfg_alloc_csdev_feat(struct coresight_device *csdev, struct cscfg_feature_desc if (!feat_csdev->regs_csdev) return NULL;
- /* copy the static register info */
- for (i = 0; i < feat_desc->nr_regs; i++) {
memcpy(&feat_csdev->regs_csdev[i].reg_desc,
&feat_desc->regs_desc[i], sizeof(struct cscfg_regval_desc));
- }
I really spend a lot of time on this snippet, especially since it duplicates some of the work done in cscfg_reset_feat() and that we now have two places where things get initialised.
/* load the feature default values */ feat_csdev->feat_desc = feat_desc; feat_csdev->csdev = csdev;
- feat_csdev->feat_ops = ops;
return feat_csdev; } @@ -180,10 +188,7 @@ static int cscfg_load_feat_csdev(struct coresight_device *csdev, struct cscfg_feature_csdev *feat_csdev; int err;
- if (!ops->load_feat)
return -EINVAL;
- feat_csdev = cscfg_alloc_csdev_feat(csdev, feat_desc);
- feat_csdev = cscfg_alloc_csdev_feat(csdev, feat_desc, ops); if (!feat_csdev) return -ENOMEM;
@@ -201,6 +206,57 @@ static int cscfg_load_feat_csdev(struct coresight_device *csdev, return 0; } +#define WARN_RES_FMT "coresight-syscfg: Insufficient resources to load feature %s in device %s\n" +#define PARAM_ERR_FMT "coresight-syscfg: Cannot load feature %s. Invalid parameter index\n"
+/*
- Check if device supports resource check function, and check for sufficient resources
- to load feature onto device. Validate parameter references.
- Load feature if sufficient resources & valid parameter references.
- */
+static int cscfg_check_feat_res_load(struct coresight_device *csdev,
struct cscfg_feature_desc *feat_desc,
struct cscfg_csdev_feat_ops *ops)
+{
- int err = 0, i;
- /* if we cannot load - fail early */
- if (!ops->load_feat)
return -EINVAL;
- /* ensure matched resource ops */
- if ((ops->alloc_feat_res && !ops->clear_feat_res) ||
(!ops->alloc_feat_res && ops->clear_feat_res))
return -EINVAL;
- /* ensure that the coresight device can support the feature */
- if (ops->check_feat_res) {
err = ops->check_feat_res(csdev, feat_desc);
if (err == -ENODEV) {
/* treat as mismatch, warn and continue */
pr_warn(WARN_RES_FMT, feat_desc->name,
dev_name(&csdev->dev));
return 0;
}
- }
- /* check parameter indexes are valid */
- for (i = 0; i < feat_desc->nr_regs; i++) {
if (feat_desc->regs_desc[i].type & CS_CFG_REG_TYPE_VAL_PARAM) {
if (feat_desc->regs_desc[i].param_idx >= feat_desc->nr_params) {
pr_err(PARAM_ERR_FMT, feat_desc->name);
return -EINVAL;
}
}
- }
- /* sufficient resources - load if no other error */
- if (!err)
err = cscfg_load_feat_csdev(csdev, feat_desc, ops);
- return err;
+}
/*
- Add feature to any matching devices - call with mutex locked.
- Iterates through devices - any device that matches the feature will be
@@ -213,7 +269,9 @@ static int cscfg_add_feat_to_csdevs(struct cscfg_feature_desc *feat_desc) 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);
/* check sufficient resources and load feature */
err = cscfg_check_feat_res_load(csdev_item->csdev, feat_desc,
&csdev_item->ops);
Checking the resources and loading them are two different operations and as such we should keep them separate. It will also make it easier to understand and review this set.
I will come back to all the operations introduced by struct cscfg_csdev_feat_ops once I have a better understanding of what they do.
More comments to come next week.
Mathieu
if (err) return err; }
@@ -616,7 +674,7 @@ static int cscfg_add_feats_csdev(struct coresight_device *csdev, 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);
}err = cscfg_check_feat_res_load(csdev, feat_desc, ops); if (err) break;
-- 2.17.1
Hi Mathieu,
On Fri, 21 May 2021 at 18:56, Mathieu Poirier mathieu.poirier@linaro.org wrote:
Hi Mike,
On Wed, May 12, 2021 at 10:17:49PM +0100, Mike Leach wrote:
Adds in API to allow features which define device resources, to check resource availability on load, and allocate on enable.
- Check on load ensures that the resources required by the feature do
not exceed the maximum avialable on the device.
- Allocate on enable ensures that sufficient unused resources are
available to satifsfy the feature on enable. Allocate can also be used to resolve any resource selection requirements - i.e. select an available resource from a pool of resources.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-config.c | 71 +++++++++++++++--- .../hwtracing/coresight/coresight-config.h | 36 +++++++++- .../hwtracing/coresight/coresight-syscfg.c | 72 +++++++++++++++++-- 3 files changed, 163 insertions(+), 16 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-config.c b/drivers/hwtracing/coresight/coresight-config.c index 3c501e027bc0..fdfda1975188 100644 --- a/drivers/hwtracing/coresight/coresight-config.c +++ b/drivers/hwtracing/coresight/coresight-config.c @@ -23,6 +23,10 @@ static void cscfg_set_reg(struct cscfg_regval_csdev *reg_csdev) u32 *p_val32 = (u32 *)reg_csdev->driver_regval; u32 tmp32 = reg_csdev->reg_desc.val32;
/* resource mapped registers may have custom handling in the device */
if (!reg_csdev->driver_regval)
return;
I am curious as to why it is needed now and wasn't in the patchset that introduced the complex configuration feature.
This is here now because we are adding in resource managemenrt with this set. It was omitted from the earlier set, simply to reduce the size and complexity of that set.
Resource mapped registers all a user to specify a resource to be used (e.g. comparator) without specifying the particular comparator to use. The enable routines will then find the first available resource and use that. This sort of thing happens already in the ETMv4 driver - but this set expands and enhances this functionality.
if (reg_csdev->reg_desc.type & CS_CFG_REG_TYPE_VAL_64BIT) { *((u64 *)reg_csdev->driver_regval) = reg_csdev->reg_desc.val64; return;
@@ -43,6 +47,8 @@ static void cscfg_save_reg(struct cscfg_regval_csdev *reg_csdev) { if (!(reg_csdev->reg_desc.type & CS_CFG_REG_TYPE_VAL_SAVE)) return;
if (!reg_csdev->driver_regval)
return; if (reg_csdev->reg_desc.type & CS_CFG_REG_TYPE_VAL_64BIT) reg_csdev->reg_desc.val64 = *(u64 *)(reg_csdev->driver_regval); else
@@ -73,15 +79,20 @@ static void cscfg_init_reg_param(struct cscfg_feature_csdev *feat_csdev, /* set values into the driver locations referenced in cscfg_reg_csdev */ static int cscfg_set_on_enable(struct cscfg_feature_csdev *feat_csdev) {
int i;
int i, err = 0; spin_lock(feat_csdev->drv_spinlock); for (i = 0; i < feat_csdev->nr_regs; i++) cscfg_set_reg(&feat_csdev->regs_csdev[i]); spin_unlock(feat_csdev->drv_spinlock);
if (feat_csdev->feat_ops->set_on_enable)
err = feat_csdev->feat_ops->set_on_enable(feat_csdev);
dev_dbg(&feat_csdev->csdev->dev, "Feature %s: %s",
feat_csdev->feat_desc->name, "set on enable");
return 0;
feat_csdev->feat_desc->name,
err ? "error on enable" : "set on enable");
return err;
}
/* copy back values from the driver locations referenced in cscfg_reg_csdev */ @@ -89,6 +100,9 @@ static void cscfg_save_on_disable(struct cscfg_feature_csdev *feat_csdev) { int i;
if (feat_csdev->feat_ops->clear_on_disable)
feat_csdev->feat_ops->clear_on_disable(feat_csdev);
spin_lock(feat_csdev->drv_spinlock); for (i = 0; i < feat_csdev->nr_regs; i++) cscfg_save_reg(&feat_csdev->regs_csdev[i]);
@@ -217,6 +231,37 @@ static int cscfg_update_curr_params(struct cscfg_config_csdev *config_csdev) return 0; }
+static void cscfg_clear_res_config(struct cscfg_config_csdev *config_csdev) +{
int i;
struct cscfg_feature_csdev *feat_csdev;
for (i = 0; i < config_csdev->nr_feat; i++) {
feat_csdev = config_csdev->feats_csdev[i];
if (feat_csdev->feat_ops->clear_feat_res)
feat_csdev->feat_ops->clear_feat_res(feat_csdev);
}
+}
+#define FEAT_ALLOC_RES_FMT "coresight-syscfg: Insufficient resource to enable feature %s\n" +static int cscfg_alloc_res_config(struct cscfg_config_csdev *config_csdev) +{
int err = 0, i;
struct cscfg_feature_csdev *feat_csdev;
for (i = 0; i < config_csdev->nr_feat; i++) {
feat_csdev = config_csdev->feats_csdev[i];
if (feat_csdev->feat_ops->alloc_feat_res)
err = feat_csdev->feat_ops->alloc_feat_res(feat_csdev);
if (err) {
pr_warn(FEAT_ALLOC_RES_FMT, feat_csdev->feat_desc->name);
cscfg_clear_res_config(config_csdev);
break;
}
}
return err;
+}
/*
- Configuration values will be programmed into the driver locations if enabling, or read
- from relevant locations on disable.
@@ -227,19 +272,29 @@ static int cscfg_prog_config(struct cscfg_config_csdev *config_csdev, bool enabl struct cscfg_feature_csdev *feat_csdev; struct coresight_device *csdev;
/* check we have resources to enable this */
if (enable) {
err = cscfg_alloc_res_config(config_csdev);
if (err)
return err;
} else
cscfg_clear_res_config(config_csdev);
for (i = 0; i < config_csdev->nr_feat; i++) { feat_csdev = config_csdev->feats_csdev[i]; csdev = feat_csdev->csdev; dev_dbg(&csdev->dev, "cfg %s; %s feature:%s", config_csdev->config_desc->name, enable ? "enable" : "disable", feat_csdev->feat_desc->name);
if (enable)
if (enable) { err = cscfg_set_on_enable(feat_csdev);
else
if (err) {
/* failed to enable - release any resources */
cscfg_clear_res_config(config_csdev);
break;
}
} else cscfg_save_on_disable(feat_csdev);
if (err)
break;
Is all of the above related to what this patchset is providing or an enhancement of the original feature?
Again - an enhancment to allow for resource management within the coresight devices
} return err;
} diff --git a/drivers/hwtracing/coresight/coresight-config.h b/drivers/hwtracing/coresight/coresight-config.h index 9bd44b940add..0e46832df993 100644 --- a/drivers/hwtracing/coresight/coresight-config.h +++ b/drivers/hwtracing/coresight/coresight-config.h @@ -199,6 +199,8 @@ struct cscfg_parameter_csdev {
- @params_csdev: current parameter values on this device
- @nr_regs: number of registers to be programmed.
- @regs_csdev: Programming details for the registers
- @res_used: Device specific context for resources used on enable.
*/
- @feat_ops: Feature operations to support alloc/release of resources.
struct cscfg_feature_csdev { const struct cscfg_feature_desc *feat_desc; @@ -209,6 +211,8 @@ struct cscfg_feature_csdev { struct cscfg_parameter_csdev *params_csdev; int nr_regs; struct cscfg_regval_csdev *regs_csdev;
void *res_used;
This isn't used in this patch. Please move it to the patch that will do so.
OK
struct cscfg_csdev_feat_ops *feat_ops;
};
/** @@ -238,14 +242,44 @@ struct cscfg_config_csdev {
- Coresight device operations.
- Registered coresight devices provide these operations to manage feature
- instances compatible with the device hardware and drivers
- instances compatible with the device hardware and drivers.
- The @check_feat_res function can be used at load time to see if the device has
- sufficient resources to support the feature. This takes into account all resources
- on the device. e.g. if the feature requires three counters, and the device has a
s/device./device,
- total of two implemented counters, this will return a -ENODEV error.
- The @alloc_feat_res function checks if there are sufficient unallocated resources
- left to enable the feature. This allows for multiple features being loaded that
- may compete for resources and also selects and allocates resources at enable time.
This is the second time so I'm wondering if this is an accepted way of writing things in english.
Not sure there are any hard and fast rules here - I am reading it as...
....elects and allocates resources at enable time. For example, if a feature requires....
Perhaps it would be better if I used the expanded version of 'e.g.' here.
- e.g. if a feature requires two comparators, and there is only one left unallocated,
- then this will return a -ENOSPC error.
- @load_feat: Pass a feature descriptor into the device and create the
loaded feature instance (struct cscfg_feature_csdev).
- @check_feat_res: Check that the coresight device has sufficient resources to
load the feature. Optional function. Return -ENODEV if this device
does not have enough resources. Called before @load_feat for feature
to ensure all feature can be installed on device.
- @alloc_feat_res: Allocate release feature resources when enabling a feature on the device
. Optional function. Returns -ENOSPC if the device has not enough available
The '.' at the beginning of the line looks wierd.
OK
resources to enable the feature. Called before registers programmed.
- @clear_feat_res: Release allocated resources. Must be implemented if @alloc_feat_res is
implemented.
- @set_on_enable : Optional programming specific to device. Called immediately after
generic register programming operation.
- @clear_on_disable: Optional programming specific to device. Called before generic register
*/
save operation.
struct cscfg_csdev_feat_ops { int (*load_feat)(struct coresight_device *csdev, struct cscfg_feature_csdev *feat_csdev);
int (*check_feat_res)(struct coresight_device *csdev,
struct cscfg_feature_desc *feat_desc);
int (*alloc_feat_res)(struct cscfg_feature_csdev *feat_csdev);
void (*clear_feat_res)(struct cscfg_feature_csdev *feat_csdev);
int (*set_on_enable)(struct cscfg_feature_csdev *feat_csdev);
void (*clear_on_disable)(struct cscfg_feature_csdev *feat_csdev);
};
/* coresight config helper functions*/ diff --git a/drivers/hwtracing/coresight/coresight-syscfg.c b/drivers/hwtracing/coresight/coresight-syscfg.c index ab74e33b892b..984459c8f168 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.c +++ b/drivers/hwtracing/coresight/coresight-syscfg.c @@ -121,7 +121,8 @@ static int cscfg_add_cfg_to_csdevs(struct cscfg_config_desc *config_desc)
- 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) +cscfg_alloc_csdev_feat(struct coresight_device *csdev, struct cscfg_feature_desc *feat_desc,
struct cscfg_csdev_feat_ops *ops)
{ struct cscfg_feature_csdev *feat_csdev = NULL; struct device *dev = csdev->dev.parent; @@ -165,9 +166,16 @@ cscfg_alloc_csdev_feat(struct coresight_device *csdev, struct cscfg_feature_desc if (!feat_csdev->regs_csdev) return NULL;
/* copy the static register info */
for (i = 0; i < feat_desc->nr_regs; i++) {
memcpy(&feat_csdev->regs_csdev[i].reg_desc,
&feat_desc->regs_desc[i], sizeof(struct cscfg_regval_desc));
}
I really spend a lot of time on this snippet, especially since it duplicates some of the work done in cscfg_reset_feat() and that we now have two places where things get initialised.
OK - I'll revisit this - not sure quite why I thought this was needed here - other than that it moves more of the registered typing and usage information than is moved by the reset_feat(), which is important for resource management.
This resource info is essentially static - so there is benefit in only moving it once - and rather than move only that it is simple at load time to move everything. reset_feat resets that register _values_ and parameterised values, that might have been altered by reading back from the hardware (counters) or set by configfs.
If there is something that needs to be here and not in reset_feats then I'll expand the comment .
/* load the feature default values */ feat_csdev->feat_desc = feat_desc; feat_csdev->csdev = csdev;
feat_csdev->feat_ops = ops; return feat_csdev;
} @@ -180,10 +188,7 @@ static int cscfg_load_feat_csdev(struct coresight_device *csdev, struct cscfg_feature_csdev *feat_csdev; int err;
if (!ops->load_feat)
return -EINVAL;
feat_csdev = cscfg_alloc_csdev_feat(csdev, feat_desc);
feat_csdev = cscfg_alloc_csdev_feat(csdev, feat_desc, ops); if (!feat_csdev) return -ENOMEM;
@@ -201,6 +206,57 @@ static int cscfg_load_feat_csdev(struct coresight_device *csdev, return 0; }
+#define WARN_RES_FMT "coresight-syscfg: Insufficient resources to load feature %s in device %s\n" +#define PARAM_ERR_FMT "coresight-syscfg: Cannot load feature %s. Invalid parameter index\n"
+/*
- Check if device supports resource check function, and check for sufficient resources
- to load feature onto device. Validate parameter references.
- Load feature if sufficient resources & valid parameter references.
- */
+static int cscfg_check_feat_res_load(struct coresight_device *csdev,
struct cscfg_feature_desc *feat_desc,
struct cscfg_csdev_feat_ops *ops)
+{
int err = 0, i;
/* if we cannot load - fail early */
if (!ops->load_feat)
return -EINVAL;
/* ensure matched resource ops */
if ((ops->alloc_feat_res && !ops->clear_feat_res) ||
(!ops->alloc_feat_res && ops->clear_feat_res))
return -EINVAL;
/* ensure that the coresight device can support the feature */
if (ops->check_feat_res) {
err = ops->check_feat_res(csdev, feat_desc);
if (err == -ENODEV) {
/* treat as mismatch, warn and continue */
pr_warn(WARN_RES_FMT, feat_desc->name,
dev_name(&csdev->dev));
return 0;
}
}
/* check parameter indexes are valid */
for (i = 0; i < feat_desc->nr_regs; i++) {
if (feat_desc->regs_desc[i].type & CS_CFG_REG_TYPE_VAL_PARAM) {
if (feat_desc->regs_desc[i].param_idx >= feat_desc->nr_params) {
pr_err(PARAM_ERR_FMT, feat_desc->name);
return -EINVAL;
}
}
}
/* sufficient resources - load if no other error */
if (!err)
err = cscfg_load_feat_csdev(csdev, feat_desc, ops);
return err;
+}
/*
- Add feature to any matching devices - call with mutex locked.
- Iterates through devices - any device that matches the feature will be
@@ -213,7 +269,9 @@ static int cscfg_add_feat_to_csdevs(struct cscfg_feature_desc *feat_desc)
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);
/* check sufficient resources and load feature */
err = cscfg_check_feat_res_load(csdev_item->csdev, feat_desc,
&csdev_item->ops);
Checking the resources and loading them are two different operations and as such we should keep them separate. It will also make it easier to understand and review this set.
Will revisit this.
Regards
Mike
I will come back to all the operations introduced by struct cscfg_csdev_feat_ops once I have a better understanding of what they do.
More comments to come next week.
Mathieu
if (err) return err; }
@@ -616,7 +674,7 @@ static int cscfg_add_feats_csdev(struct coresight_device *csdev,
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);
err = cscfg_check_feat_res_load(csdev, feat_desc, ops); if (err) break; }
-- 2.17.1
Adds resource management to configuration and feature handling for ETM4 using the system configuration resource management API.
Allows specification of ETM4 resources when creating configurations and features.
Adds in checking and validation of resources used by features to prevent over allocation when multiple features used in a configuration.
Signed-off-by: Mike Leach mike.leach@linaro.org --- .../hwtracing/coresight/coresight-etm4x-cfg.c | 533 ++++++++++++++++++ .../hwtracing/coresight/coresight-etm4x-cfg.h | 196 ++++++- include/linux/coresight.h | 2 + 3 files changed, 724 insertions(+), 7 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c index d2ea903231b2..ba6d20b58a9a 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c @@ -28,6 +28,11 @@ } \ }
+/* check for a match to ts rate */ +static bool etm4_cfg_feat_is_ts_rate(const struct cscfg_feature_desc *feat_desc); + +#define TS_RATE_REG_VAL_IDX 0 + /** * etm4_cfg_map_reg_offset - validate and map the register offset into a * location in the driver config struct. @@ -128,6 +133,66 @@ static int etm4_cfg_map_reg_offset(struct etmv4_drvdata *drvdata, return err; }
+static void etm4_cfg_dump_res_mask(const char *name, struct etm4_cfg_resources *res) +{ + pr_debug("Mask %s\n", name); + pr_debug("selectors %08x; addr_cmp %04x\n", res->selectors, res->addr_cmp); + pr_debug("cid_cmp %02x; vmid_cmp %02x; counters %02x\n", res->cid_cmp, + res->vmid_cmp, res->counters); + pr_debug("misc bits %08x\n", res->misc); +} + +/* + * generate an address offset from a resource type and index + * Bit selected resources will return a ETM4_RES_OFFSET_SKIP value + * as these require special handling on enable / disable. + */ +static u32 etm4_cfg_get_res_offset(u16 res_type, u32 res_idx) +{ + u32 offset = ETM4_RES_OFFSET_ERR; + + switch (res_type & ETM4_CFG_RES_MASK) { + case ETM4_CFG_RES_CTR: + if (res_type & ETM4_CTR_VAL) + offset = TRCCNTVRn(res_idx); + else if (res_type & ETM4_CTR_RLD) + offset = TRCCNTRLDVRn(res_idx); + else if (res_type & ETM4_CTR_CTRL) + offset = TRCCNTCTLRn(res_idx); + break; + case ETM4_CFG_RES_CMP: + if (res_type & ETM4_CMP_VAL) + offset = TRCACVRn(res_idx); + else if (res_type & ETM4_CMP_CTL) + offset = TRCACATRn(res_idx); + break; + case ETM4_CFG_RES_SEL: + offset = TRCRSCTLRn(res_idx); + break; + + case ETM4_CFG_RES_SEQ: + if (res_type & ETM4_SEQ_STATE_R) + offset = TRCSEQEVRn(res_idx); + else if (res_type & ETM4_SEQ_RESET_R) + offset = TRCSEQRSTEVR; + break; + case ETM4_CFG_RES_CID_CMP: + offset = TRCCIDCVRn(res_idx); + break; + + case ETM4_CFG_RES_VID_CMP: + offset = TRCVMIDCVRn(res_idx); + break; + + /* these two have dedicated enable functions, no address needed */ + case ETM4_CFG_RES_BITCTRL: + case ETM4_CFG_RES_TS: + offset = ETM4_RES_OFFSET_SKIP; + break; + } + return offset; +} + /** * etm4_cfg_load_feature - load a feature into a device instance. * @@ -163,11 +228,349 @@ static int etm4_cfg_load_feature(struct coresight_device *csdev, /* process the register descriptions */ for (i = 0; i < feat_csdev->nr_regs && !err; i++) { offset = feat_desc->regs_desc[i].offset; + + /* resource needs conversion to a register access value */ + if (feat_desc->regs_desc[i].type & CS_CFG_REG_TYPE_RESOURCE) { + offset = etm4_cfg_get_res_offset(feat_desc->regs_desc[i].hw_info, + offset); + if (offset == ETM4_RES_OFFSET_ERR) { + err = -ENODEV; + break; + } else if (offset == ETM4_RES_OFFSET_SKIP) + continue; + } err = etm4_cfg_map_reg_offset(drvdata, &feat_csdev->regs_csdev[i], offset); } return err; }
+/* + * ts rate - set a counter to emit timestamp requests at a set interval. + * if we have sufficient resources then we use a counter and resource + * selector to achieve this. + * + * However, if not then do the best possible - which prevents the perf + * event timestamp request from failing if any configuration selection + * is using resources. e.g. when profiling, timestamps do not really matter. + */ +void etm4_cfg_set_ts_rate(struct coresight_device *csdev, u32 ts_rate_val) +{ + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + struct etmv4_config *drvcfg = &drvdata->config; + struct cscfg_res_impl_used *res_impl_used; + int counter_idx, res_sel_idx; + u32 tsctlr_val = 0; + + res_impl_used = (struct cscfg_res_impl_used *)csdev->cscfg_res_mask; + + /* look for resources */ + counter_idx = etm4_res_find_counter(res_impl_used); + res_sel_idx = etm4_res_find_selector(res_impl_used); + if (counter_idx >= 0 && res_sel_idx >= 0) { + /* counter and selector - can set up ts rate normally */ + /* + * counter @ 1 and reload @ rate supplied - + * immediate timestamp then every rate + */ + drvcfg->cntr_val[counter_idx] = 1; + drvcfg->cntrldvr[counter_idx] = ts_rate_val; + /* + * counter ctrl - bit 16: 1 for reload self, + * bit 7: 0 single event, + * bit 6:0 res sel 1 - true + */ + drvcfg->cntr_ctrl[counter_idx] = 0x1 << 16 | 0x1; + + /* + * set up resource selector for the counter. + * bits 19:16 - group 0b0010 counter + * bits 15:0 - bit select for counter idx + */ + drvcfg->res_ctrl[res_sel_idx] = (0x2 << 16) | (0x1 << counter_idx); + + /* single selector bit 7 == 0, bit 6:0 - selector index */ + tsctlr_val = res_sel_idx; + + } else if (ts_rate_val == 1) { + /* + * perf always tries to use a min value - + * emulate by setting the ts event to true + */ + /* single selector bit 7 == 0, bit 6:0 - selector 1 - always true */ + tsctlr_val = 0x1; + } + + /* set the configr reg to enable TS, and the ts control reg */ + drvcfg->ts_ctrl = tsctlr_val; + drvcfg->cfg |= BIT(11); +} + +/* + * on enable a feature - called after generic routine has programmed other registers. + * handle bit selects and custom elements + */ +static int etm4_cfg_on_enable_feat(struct cscfg_feature_csdev *feat_csdev) +{ + int err = 0; + struct etm4_cfg_resources *res_feat; + struct device *dev = feat_csdev->csdev->dev.parent; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev); + struct etmv4_config *drvcfg = &drvdata->config; + u32 ts_rate_val; + + /* + * look for the bit selected resources in this feature and set driver + * values to be programmed when enabling hardware. + */ + res_feat = (struct etm4_cfg_resources *)feat_csdev->res_used; + + /* if none of the bit selected resources in use, exit early */ + if (!res_feat->misc) + return 0; + + /* otherwise check each and set as required */ + if (res_feat->ctxt_id) + drvcfg->cfg |= BIT(6); + + if (res_feat->vm_id) + drvcfg->cfg |= BIT(7); + + /* return stack is bit 12 in config register */ + if (res_feat->return_stack) + drvcfg->cfg |= BIT(12); + + /* branch broadcast - feature using this must program the bbctlr */ + if (res_feat->branch_broadcast) + drvcfg->cfg |= BIT(3); + + /* cycle count */ + if (res_feat->cycle_cnt) { + drvcfg->cfg |= BIT(4); + /* TRM: Must program this for cycacc to work - ensure mun permitted */ + if (drvcfg->ccctlr < drvdata->ccitmin) + drvcfg->ccctlr = drvdata->ccitmin; + } + + /* + * timestamps - if not ts-rate just set to on, otherwise + * set using reload counter according to requested rate + */ + if (res_feat->timestamp) { + /* the current feature is the ts-rate feature */ + if (res_feat->ts_rate) { + ts_rate_val = feat_csdev->regs_csdev[TS_RATE_REG_VAL_IDX].reg_desc.val32; + etm4_cfg_set_ts_rate(feat_csdev->csdev, ts_rate_val); + } else + drvcfg->cfg |= BIT(11); + } + return err; +} + +/* set the overall available resource masks for the device */ +static int etm4_cfg_set_res_mask(struct coresight_device *csdev) +{ + struct device *dev = csdev->dev.parent; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev); + struct etm4_cfg_resources *res; + struct cscfg_res_impl_used *res_impl_used; + + res_impl_used = devm_kzalloc(dev, sizeof(*res_impl_used), GFP_KERNEL); + if (!res_impl_used) + return -ENOMEM; + res = &res_impl_used->impl; + + /* selectors */ + if (drvdata->nr_resource) + res->selectors = GENMASK((drvdata->nr_resource * 2) - 1, 0); + + /* comparators */ + if (drvdata->nr_addr_cmp) + res->addr_cmp = GENMASK(drvdata->nr_addr_cmp - 1, 0); + + if (drvdata->numvmidc) + res->vmid_cmp = GENMASK(drvdata->numvmidc - 1, 0); + + if (drvdata->numcidc) + res->cid_cmp = GENMASK(drvdata->numcidc - 1, 0); + + /* misc resources */ + if (drvdata->nr_cntr) + res->counters = GENMASK(drvdata->nr_cntr - 1, 0); + + if (drvdata->trccci) + res->cycle_cnt = 1; + + if (drvdata->trcbb) + res->branch_broadcast = 1; + + if (drvdata->ctxid_size) + res->ctxt_id = 1; + + if (drvdata->vmid_size) + res->vm_id = 1; + + if (drvdata->nrseqstate) + res->sequencer = 1; + + if (drvdata->retstack) + res->return_stack = 1; + + if (drvdata->ts_size) { + res->timestamp = 1; + if (drvdata->nr_cntr && drvdata->nr_resource) + res->ts_rate = 1; + } + etm4_cfg_dump_res_mask("device impl resources", &res_impl_used->impl); + csdev->cscfg_res_mask = res_impl_used; + return 0; +} + +/* + * reads a descriptor and updates the resource mask structure + * checks resource indexes are valid. + */ +static int etm4_cfg_update_res_from_desc(const struct cscfg_feature_desc *feat_desc, + struct etm4_cfg_resources *res) +{ + struct cscfg_regval_desc *regs_desc = &feat_desc->regs_desc[0]; + u32 res_idx, hw_info; + int i; + + for (i = 0; i < feat_desc->nr_regs; i++) { + if (regs_desc[i].type & CS_CFG_REG_TYPE_RESOURCE) { + res_idx = regs_desc[i].offset; + hw_info = regs_desc[i].hw_info; + switch (hw_info & ETM4_CFG_RES_MASK) { + case ETM4_CFG_RES_CTR: + if (res_idx >= ETMv4_MAX_CNTR) + goto invalid_resource_err; + res->counters |= BIT(res_idx); + break; + + case ETM4_CFG_RES_CMP: + if (res_idx >= ETM_MAX_SINGLE_ADDR_CMP) + goto invalid_resource_err; + res->addr_cmp |= BIT(res_idx); + break; + + case ETM4_CFG_RES_SEL: + if (res_idx >= ETM_MAX_RES_SEL) + goto invalid_resource_err; + res->selectors |= BIT(res_idx); + break; + + case ETM4_CFG_RES_SEQ: + res->sequencer = 1; + break; + + case ETM4_CFG_RES_TS: + res->timestamp = 1; + if (etm4_cfg_feat_is_ts_rate(feat_desc)) + res->ts_rate = 1; + break; + + case ETM4_CFG_RES_BITCTRL: + if (hw_info & ETM4_BITCTRL_BRANCH_BROADCAST) + res->branch_broadcast = 1; + if (hw_info & ETM4_BITCTRL_CYCLE_COUNT) + res->cycle_cnt = 1; + if (hw_info & ETM4_BITCTRL_CTXTID) + res->ctxt_id = 1; + if (hw_info & ETM4_BITCTRL_VMID) + res->vm_id = 1; + if (hw_info & ETM4_BITCTRL_RETSTACK) + res->return_stack = 1; + break; + + case ETM4_CFG_RES_CID_CMP: + if (res_idx >= ETMv4_MAX_CTXID_CMP) + goto invalid_resource_err; + res->cid_cmp |= BIT(res_idx); + break; + + case ETM4_CFG_RES_VID_CMP: + if (res_idx >= ETM_MAX_VMID_CMP) + goto invalid_resource_err; + res->vmid_cmp |= BIT(res_idx); + break; + } + } + } + return 0; + +invalid_resource_err: + pr_err("Error: Invalid resource values in feature %s\n", feat_desc->name); + return -EINVAL; +} +/* + * Check that the device contains the minimum resources required to support the + * described @feat_desc. Return -ENODEV if missing required resources. + */ +static int etm4_cfg_check_feat_res(struct coresight_device *csdev, + struct cscfg_feature_desc *feat_desc) +{ + struct etm4_cfg_resources req_res; + struct cscfg_res_impl_used *dev_res; + int err; + + /* create a resource mask from descriptor and validate */ + memset(&req_res, 0, sizeof(req_res)); + err = etm4_cfg_update_res_from_desc(feat_desc, &req_res); + etm4_cfg_dump_res_mask("check_feat_res", &req_res); + if (!err) { + dev_res = (struct cscfg_res_impl_used *)csdev->cscfg_res_mask; + if (!etm4_cfg_check_impl(&dev_res->impl, &req_res)) + return -ENODEV; + } + return err; +} + +/* + * Allocate resource requirements for the feature before + * it is programmed into the system. Ensures that two or more features in a + * configuration do not try to use the same resources on the device. + * + * At this point we use the absolute programmed resources - we do not attempt + * to find alternate available resources. (e.g. if 2 features use selector 3, + * fail the 2nd feature - do not look for an alternative free selector). + */ +static int etm4_cfg_alloc_feat_res(struct cscfg_feature_csdev *feat_csdev) +{ + struct coresight_device *csdev = feat_csdev->csdev; + struct device *dev = csdev->dev.parent; + struct etm4_cfg_resources *res_feat, *res_inuse; + int err = 0; + + /* one off initialisation of resources required for this feature */ + if (!feat_csdev->res_used) { + res_feat = devm_kzalloc(dev, sizeof(*res_feat), GFP_KERNEL); + if (!res_feat) + return -ENOMEM; + err = etm4_cfg_update_res_from_desc(feat_csdev->feat_desc, res_feat); + if (err) + return err; + feat_csdev->res_used = res_feat; + } else + res_feat = (struct etm4_cfg_resources *)feat_csdev->res_used; + + /* check that the device resources reqiured are not in use */ + res_inuse = &((struct cscfg_res_impl_used *)csdev->cscfg_res_mask)->used; + if (!etm4_cfg_check_set_inuse(res_inuse, res_feat)) + err = -ENOSPC; + + return err; +} + +static void etm4_cfg_clear_feat_res(struct cscfg_feature_csdev *feat_csdev) +{ + struct coresight_device *csdev = feat_csdev->csdev; + struct etm4_cfg_resources *res_feat, *res_inuse; + + res_feat = (struct etm4_cfg_resources *)feat_csdev->res_used; + res_inuse = &((struct cscfg_res_impl_used *)csdev->cscfg_res_mask)->used; + etm4_cfg_clear_inuse(res_inuse, res_feat); +} + /* match information when loading configurations */ #define CS_CFG_ETM4_MATCH_FLAGS (CS_CFG_MATCH_CLASS_SRC_ALL | \ CS_CFG_MATCH_CLASS_SRC_ETM4) @@ -175,8 +578,138 @@ static int etm4_cfg_load_feature(struct coresight_device *csdev, int etm4_cscfg_register(struct coresight_device *csdev) { struct cscfg_csdev_feat_ops ops; + int err = 0; + + err = etm4_cfg_set_res_mask(csdev); + if (err) + return err;
ops.load_feat = &etm4_cfg_load_feature; + ops.check_feat_res = &etm4_cfg_check_feat_res; + ops.alloc_feat_res = &etm4_cfg_alloc_feat_res; + ops.clear_feat_res = &etm4_cfg_clear_feat_res; + ops.set_on_enable = &etm4_cfg_on_enable_feat; + ops.clear_on_disable = 0;
return cscfg_register_csdev(csdev, CS_CFG_ETM4_MATCH_FLAGS, &ops); } + +/* + * find first available bit in implemented mask @impl, that is not set in @used mask. + * set bit in @used and return. Return -ENOSPC if no available bits. + */ +int etm4_cfg_find_unused_idx(unsigned long *impl, unsigned long *used, int size) +{ + unsigned long end_idx, unused_idx; + + end_idx = find_first_zero_bit(impl, size); + unused_idx = find_first_zero_bit(used, size); + if (unused_idx < end_idx) { + *used |= BIT(unused_idx); + return (int)unused_idx; + } + return -ENOSPC; +} + +/* + * find first available pair of bits in implemented mask @impl, that are not set in + * @used mask. First bit of pair will always be an even index. + * Set bits in @used and return. Return -ENOSPC if no available bits. + */ +int etm4_cfg_find_unused_idx_pair(unsigned long *impl, unsigned long *used, int size) +{ + unsigned long end_idx, first_unused_idx, next_unused_idx; + + end_idx = find_first_zero_bit(impl, size); + first_unused_idx = find_first_zero_bit(used, size); + + /* + * even indexes are the 1st in a pair, look through the comparators + * till a pair found or we are at the end of the list. + */ + while (first_unused_idx < end_idx) { + /* first is an even number, if the next is free we have a pair */ + if (!(first_unused_idx % 2)) { + next_unused_idx = find_next_zero_bit(used, size, first_unused_idx); + if (next_unused_idx == (first_unused_idx + 1)) { + *used |= BIT(first_unused_idx); + *used |= BIT(next_unused_idx); + return (int)first_unused_idx; + } + first_unused_idx = next_unused_idx; + } else + first_unused_idx = find_next_zero_bit(used, size, first_unused_idx); + } + return -ENOSPC; +} + + +/* built in timestamp rate for etm4x */ +static struct cscfg_parameter_desc ts_rate_param[] = { + { + .name = "ts_rate_cycles", + .value = 100. + }, +}; + +static struct cscfg_regval_desc ts_rate_regs[] = { + { + .type = CS_CFG_REG_TYPE_RESOURCE | CS_CFG_REG_TYPE_VAL_PARAM, + .offset = 0, + .hw_info = ETM4_CFG_RES_TS, + .param_idx = 0, + }, +}; + +static struct cscfg_feature_desc ts_rate_etm4x = { + .name = "timestamp-rate", + .description = "Enable timestamps and set rate they appear in the trace.\n" + "Rate value is number of cycles between timestamp requests. Min value 1.\n", + .match_flags = CS_CFG_MATCH_CLASS_SRC_ETM4, + .nr_params = ARRAY_SIZE(ts_rate_param), + .params_desc = ts_rate_param, + .nr_regs = ARRAY_SIZE(ts_rate_regs), + .regs_desc = ts_rate_regs, +}; + +static struct cscfg_feature_desc *etm4x_feats[] = { + &ts_rate_etm4x, + NULL, +}; + +static struct cscfg_config_desc *etm4x_cfgs[] = { + NULL, +}; + +static struct cscfg_load_owner_info etm4x_mod_owner = { + .type = CSCFG_OWNER_MODULE, + .owner_handle = THIS_MODULE, +}; + +/* + * check if incoming feature is ts-rate + */ +static bool etm4_cfg_feat_is_ts_rate(const struct cscfg_feature_desc *feat_desc) +{ + if (!strcmp(feat_desc->name, ts_rate_etm4x.name)) + return true; + return false; +} + +/* load the etm4 builtin ts_rate feature into the system */ +int etm4_cscfg_load_builtin_cfg(void) +{ + int err; + + err = cscfg_load_config_sets(etm4x_cfgs, etm4x_feats, &etm4x_mod_owner); + + /* if currently loaded matching devs ts_rate, still allow to load */ + if (err == -ENODEV) + err = 0; + return err; +} + +void etm4_cscfg_unload_builtin_cfg(void) +{ + cscfg_unload_config_sets(&etm4x_mod_owner); +} diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.h b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h index 32dab34c1dac..dd69a8ef522d 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-cfg.h +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h @@ -13,18 +13,200 @@
/* resource IDs */
+/* + * 12 bit resource ID: + * 3:0 = resource type in use. + * 11:4 = additional resource specific information. + */ #define ETM4_CFG_RES_CTR 0x001 #define ETM4_CFG_RES_CMP 0x002 -#define ETM4_CFG_RES_CMP_PAIR0 0x003 -#define ETM4_CFG_RES_CMP_PAIR1 0x004 -#define ETM4_CFG_RES_SEL 0x005 -#define ETM4_CFG_RES_SEL_PAIR0 0x006 -#define ETM4_CFG_RES_SEL_PAIR1 0x007 -#define ETM4_CFG_RES_SEQ 0x008 -#define ETM4_CFG_RES_TS 0x009 +#define ETM4_CFG_RES_SEL 0x003 +#define ETM4_CFG_RES_SEQ 0x004 +#define ETM4_CFG_RES_TS 0x005 +#define ETM4_CFG_RES_BITCTRL 0x006 +#define ETM4_CFG_RES_CID_CMP 0x007 +#define ETM4_CFG_RES_VID_CMP 0x008 #define ETM4_CFG_RES_MASK 0x00F
+/* additional bits to supplement _CFG_RES_CTR */ +#define ETM4_CTR_VAL 0x010 +#define ETM4_CTR_RLD 0x020 +#define ETM4_CTR_CTRL 0x040 + +/* additional bits for address comparators _CFG_RES_CMP */ +#define ETM4_CMP_PAIR0 0x010 +#define ETM4_CMP_PAIR1 0x020 +#define ETM4_CMP_VAL 0x040 +#define ETM4_CMP_CTL 0x080 + +/* additional bits for resource selectors _CFG_RES_SEL */ +#define ETM4_SEL_PAIR0 0x010 +#define ETM4_SEL_PAIR1 0x020 + +/* addtional bits for sequencer _CFG_RES_SEQ */ +#define ETM4_SEQ_STATE_R 0x010 +#define ETM4_SEQ_RESET_R 0x020 + +/* additional bits to supplement _CFG_RES_BITCTRL */ +#define ETM4_BITCTRL_BRANCH_BROADCAST 0x010 +#define ETM4_BITCTRL_CYCLE_COUNT 0x020 +#define ETM4_BITCTRL_CTXTID 0x040 +#define ETM4_BITCTRL_VMID 0x080 +#define ETM4_BITCTRL_RETSTACK 0x100 + +/* error value when calculating resource register offset (max offset = 0xFFC) */ +#define ETM4_RES_OFFSET_ERR 0xFFF + +/* skip value if a bit control that is resolved later */ +#define ETM4_RES_OFFSET_SKIP 0xFFE + +/** + * Masks to indicate resource usage. + * @selectors: The resource selector regs - max 32 off + * @comparators: Comparators - address (16 max), context ID (8 max), VMID (8 max). + * @misc:- bitselected features, sequencer etc. + */ +struct etm4_cfg_resources { + u32 selectors; + u16 addr_cmp; + u8 cid_cmp; + u8 vmid_cmp; + u8 counters; + union { + u32 misc; + struct { + u32 cycle_cnt:1; + u32 branch_broadcast:1; + u32 ctxt_id:1; + u32 vm_id:1; + u32 sequencer:1; + u32 return_stack:1; + u32 timestamp:1; + u32 ts_rate:1; + }; + }; +}; + +/* structure to hold implemented & used resources for the coresight device */ +struct cscfg_res_impl_used { + struct etm4_cfg_resources impl; + struct etm4_cfg_resources used; +}; + +/* resource mask tests */ +/* check implmented - ensure that all bits in @req exist in @impl */ +static inline bool etm4_cfg_check_impl(struct etm4_cfg_resources *impl, + struct etm4_cfg_resources *req) +{ + /* invert impl then and req - anything set is outside impl mask */ + if ((~impl->selectors & req->selectors) || + (~impl->addr_cmp & req->addr_cmp) || + (~impl->cid_cmp & req->cid_cmp) || + (~impl->vmid_cmp & req->vmid_cmp) || + (~impl->counters & req->counters) || + (~impl->misc & req->misc)) + return false; + return true; +} + +/* check @req not @inuse, & set @inuse if free (assumes @req passed the impl check) */ +static inline bool etm4_cfg_check_set_inuse(struct etm4_cfg_resources *inuse, + struct etm4_cfg_resources *req) +{ + /* first check for hits between inuse and requested bits */ + if ((inuse->selectors & req->selectors) || + (inuse->addr_cmp & req->addr_cmp) || + (inuse->cid_cmp & req->cid_cmp) || + (inuse->vmid_cmp & req->vmid_cmp) || + (inuse->counters & req->counters) || + (inuse->misc & req->misc)) + return false; + + /* set all requested bits as inuse */ + inuse->selectors |= req->selectors; + inuse->addr_cmp |= req->addr_cmp; + inuse->cid_cmp |= req->cid_cmp; + inuse->vmid_cmp |= req->vmid_cmp; + inuse->counters |= req->counters; + inuse->misc |= req->misc; + return true; +} + +static inline void etm4_cfg_clear_inuse(struct etm4_cfg_resources *inuse, + struct etm4_cfg_resources *req) +{ + /* clear requested bits from inuse */ + inuse->selectors &= ~req->selectors; + inuse->addr_cmp &= ~req->addr_cmp; + inuse->cid_cmp &= ~req->cid_cmp; + inuse->vmid_cmp &= ~req->vmid_cmp; + inuse->counters &= ~req->counters; + inuse->misc &= ~req->misc; +} + /* ETMv4 specific config functions */ int etm4_cscfg_register(struct coresight_device *csdev); +int etm4_cfg_find_unused_idx(unsigned long *impl, unsigned long *used, int size); +int etm4_cfg_find_unused_idx_pair(unsigned long *impl, unsigned long *used, int size); +void etm4_cfg_set_ts_rate(struct coresight_device *csdev, u32 ts_rate_val); + +/* register etm4x builtins with cscfg on module load */ +int etm4_cscfg_load_builtin_cfg(void); +void etm4_cscfg_unload_builtin_cfg(void); + +/* + * Set of functions to find an available resource from @res->impl, not already marked as used + * in @res->used. + * return index and mark as used in @res->used. return -ENOSPC if nothing available. + */ + +static inline int etm4_res_find_selector(struct cscfg_res_impl_used *res) +{ + unsigned long *impl, *used; + + if (!res->impl.selectors) + return -ENOSPC; + + impl = (unsigned long *)&res->impl.selectors; + used = (unsigned long *)&res->used.selectors; + return etm4_cfg_find_unused_idx(impl, used, ETM_MAX_RES_SEL); +} + +static inline int etm4_res_find_counter(struct cscfg_res_impl_used *res) +{ + unsigned long *impl, *used; + + if (!res->impl.counters) + return -ENOSPC; + + impl = (unsigned long *)&res->impl.counters; + used = (unsigned long *)&res->used.counters; + return etm4_cfg_find_unused_idx(impl, used, ETMv4_MAX_CNTR); +} + +static inline int etm4_res_find_addr_comparator(struct cscfg_res_impl_used *res) +{ + unsigned long *impl, *used; + + if (!res->impl.addr_cmp) + return -ENOSPC; + + impl = (unsigned long *)&res->impl.addr_cmp; + used = (unsigned long *)&res->used.addr_cmp; + return etm4_cfg_find_unused_idx(impl, used, ETM_MAX_SINGLE_ADDR_CMP); +} + + +static inline int etm4_res_find_addr_comp_pair(struct cscfg_res_impl_used *res) +{ + unsigned long *impl, *used; + + if (!res->impl.addr_cmp) + return -ENOSPC; + + impl = (unsigned long *)&res->impl.addr_cmp; + used = (unsigned long *)&res->used.addr_cmp; + return etm4_cfg_find_unused_idx_pair(impl, used, ETM_MAX_SINGLE_ADDR_CMP); +}
#endif /* CORESIGHT_ETM4X_CFG_H */ diff --git a/include/linux/coresight.h b/include/linux/coresight.h index a348049ee08b..b513964b9305 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -223,6 +223,7 @@ struct coresight_sysfs_link { * @feature_csdev_list: List of complex feature programming added to the device. * @config_csdev_list: List of system configurations added to the device. * @active_cscfg_ctxt: Context information for current active system configuration. + * @cscfg_res_mask: Available device specific resources usable in features. */ struct coresight_device { struct coresight_platform_data *pdata; @@ -248,6 +249,7 @@ struct coresight_device { struct list_head feature_csdev_list; struct list_head config_csdev_list; void *active_cscfg_ctxt; + void *cscfg_res_mask; };
/*
Good day,
Comments for this patch will come over multiple days...
On Wed, May 12, 2021 at 10:17:50PM +0100, Mike Leach wrote:
Adds resource management to configuration and feature handling for ETM4 using the system configuration resource management API.
Allows specification of ETM4 resources when creating configurations and features.
Adds in checking and validation of resources used by features to prevent over allocation when multiple features used in a configuration.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-etm4x-cfg.c | 533 ++++++++++++++++++ .../hwtracing/coresight/coresight-etm4x-cfg.h | 196 ++++++- include/linux/coresight.h | 2 + 3 files changed, 724 insertions(+), 7 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c index d2ea903231b2..ba6d20b58a9a 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c @@ -28,6 +28,11 @@ } \ } +/* check for a match to ts rate */ +static bool etm4_cfg_feat_is_ts_rate(const struct cscfg_feature_desc *feat_desc);
+#define TS_RATE_REG_VAL_IDX 0
/**
- etm4_cfg_map_reg_offset - validate and map the register offset into a
location in the driver config struct.
@@ -128,6 +133,66 @@ static int etm4_cfg_map_reg_offset(struct etmv4_drvdata *drvdata, return err; } +static void etm4_cfg_dump_res_mask(const char *name, struct etm4_cfg_resources *res) +{
- pr_debug("Mask %s\n", name);
- pr_debug("selectors %08x; addr_cmp %04x\n", res->selectors, res->addr_cmp);
- pr_debug("cid_cmp %02x; vmid_cmp %02x; counters %02x\n", res->cid_cmp,
res->vmid_cmp, res->counters);
- pr_debug("misc bits %08x\n", res->misc);
Even at a pr_debug() log level this is probably too much output. I also think this is a purely debugging mechanism and only used when developing new features. I would enclose the core of the function with an #if DEBUG and add #define DEBUG 0 at the top of the file.
+}
+/*
- generate an address offset from a resource type and index
- Bit selected resources will return a ETM4_RES_OFFSET_SKIP value
- as these require special handling on enable / disable.
- */
+static u32 etm4_cfg_get_res_offset(u16 res_type, u32 res_idx) +{
- u32 offset = ETM4_RES_OFFSET_ERR;
- switch (res_type & ETM4_CFG_RES_MASK) {
- case ETM4_CFG_RES_CTR:
if (res_type & ETM4_CTR_VAL)
offset = TRCCNTVRn(res_idx);
else if (res_type & ETM4_CTR_RLD)
offset = TRCCNTRLDVRn(res_idx);
else if (res_type & ETM4_CTR_CTRL)
offset = TRCCNTCTLRn(res_idx);
break;
- case ETM4_CFG_RES_CMP:
if (res_type & ETM4_CMP_VAL)
offset = TRCACVRn(res_idx);
else if (res_type & ETM4_CMP_CTL)
offset = TRCACATRn(res_idx);
break;
- case ETM4_CFG_RES_SEL:
offset = TRCRSCTLRn(res_idx);
break;
The above case statements don't have a space and the ones below do. The same applies to function etm4_cfg_update_res_from_des(). I don't really mind which one is used (though I'd prefer no space) but pick a scheme and stick with it.
- case ETM4_CFG_RES_SEQ:
if (res_type & ETM4_SEQ_STATE_R)
offset = TRCSEQEVRn(res_idx);
else if (res_type & ETM4_SEQ_RESET_R)
offset = TRCSEQRSTEVR;
break;
- case ETM4_CFG_RES_CID_CMP:
offset = TRCCIDCVRn(res_idx);
break;
- case ETM4_CFG_RES_VID_CMP:
offset = TRCVMIDCVRn(res_idx);
break;
/* these two have dedicated enable functions, no address needed */
- case ETM4_CFG_RES_BITCTRL:
- case ETM4_CFG_RES_TS:
offset = ETM4_RES_OFFSET_SKIP;
break;
- }
- return offset;
+}
/**
- etm4_cfg_load_feature - load a feature into a device instance.
@@ -163,11 +228,349 @@ static int etm4_cfg_load_feature(struct coresight_device *csdev, /* process the register descriptions */ for (i = 0; i < feat_csdev->nr_regs && !err; i++) { offset = feat_desc->regs_desc[i].offset;
/* resource needs conversion to a register access value */
if (feat_desc->regs_desc[i].type & CS_CFG_REG_TYPE_RESOURCE) {
offset = etm4_cfg_get_res_offset(feat_desc->regs_desc[i].hw_info,
offset);
if (offset == ETM4_RES_OFFSET_ERR) {
err = -ENODEV;
break;
} else if (offset == ETM4_RES_OFFSET_SKIP)
continue;
err = etm4_cfg_map_reg_offset(drvdata, &feat_csdev->regs_csdev[i], offset); } return err;}
} +/*
- ts rate - set a counter to emit timestamp requests at a set interval.
- if we have sufficient resources then we use a counter and resource
- selector to achieve this.
- However, if not then do the best possible - which prevents the perf
- event timestamp request from failing if any configuration selection
- is using resources. e.g. when profiling, timestamps do not really matter.
- */
+void etm4_cfg_set_ts_rate(struct coresight_device *csdev, u32 ts_rate_val) +{
- struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
- struct etmv4_config *drvcfg = &drvdata->config;
- struct cscfg_res_impl_used *res_impl_used;
- int counter_idx, res_sel_idx;
- u32 tsctlr_val = 0;
- res_impl_used = (struct cscfg_res_impl_used *)csdev->cscfg_res_mask;
- /* look for resources */
- counter_idx = etm4_res_find_counter(res_impl_used);
- res_sel_idx = etm4_res_find_selector(res_impl_used);
- if (counter_idx >= 0 && res_sel_idx >= 0) {
/* counter and selector - can set up ts rate normally */
/*
* counter @ 1 and reload @ rate supplied -
* immediate timestamp then every rate
*/
drvcfg->cntr_val[counter_idx] = 1;
drvcfg->cntrldvr[counter_idx] = ts_rate_val;
/*
* counter ctrl - bit 16: 1 for reload self,
* bit 7: 0 single event,
* bit 6:0 res sel 1 - true
*/
drvcfg->cntr_ctrl[counter_idx] = 0x1 << 16 | 0x1;
/*
* set up resource selector for the counter.
* bits 19:16 - group 0b0010 counter
* bits 15:0 - bit select for counter idx
*/
drvcfg->res_ctrl[res_sel_idx] = (0x2 << 16) | (0x1 << counter_idx);
/* single selector bit 7 == 0, bit 6:0 - selector index */
tsctlr_val = res_sel_idx;
- } else if (ts_rate_val == 1) {
/*
* perf always tries to use a min value -
* emulate by setting the ts event to true
*/
/* single selector bit 7 == 0, bit 6:0 - selector 1 - always true */
tsctlr_val = 0x1;
- }
- /* set the configr reg to enable TS, and the ts control reg */
- drvcfg->ts_ctrl = tsctlr_val;
- drvcfg->cfg |= BIT(11);
+}
+/*
- on enable a feature - called after generic routine has programmed other registers.
- handle bit selects and custom elements
- */
+static int etm4_cfg_on_enable_feat(struct cscfg_feature_csdev *feat_csdev) +{
- int err = 0;
- struct etm4_cfg_resources *res_feat;
- struct device *dev = feat_csdev->csdev->dev.parent;
- struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
- struct etmv4_config *drvcfg = &drvdata->config;
- u32 ts_rate_val;
- /*
* look for the bit selected resources in this feature and set driver
* values to be programmed when enabling hardware.
*/
- res_feat = (struct etm4_cfg_resources *)feat_csdev->res_used;
- /* if none of the bit selected resources in use, exit early */
- if (!res_feat->misc)
return 0;
- /* otherwise check each and set as required */
- if (res_feat->ctxt_id)
drvcfg->cfg |= BIT(6);
- if (res_feat->vm_id)
drvcfg->cfg |= BIT(7);
- /* return stack is bit 12 in config register */
- if (res_feat->return_stack)
drvcfg->cfg |= BIT(12);
- /* branch broadcast - feature using this must program the bbctlr */
- if (res_feat->branch_broadcast)
drvcfg->cfg |= BIT(3);
- /* cycle count */
- if (res_feat->cycle_cnt) {
drvcfg->cfg |= BIT(4);
/* TRM: Must program this for cycacc to work - ensure mun permitted */
if (drvcfg->ccctlr < drvdata->ccitmin)
drvcfg->ccctlr = drvdata->ccitmin;
- }
- /*
* timestamps - if not ts-rate just set to on, otherwise
* set using reload counter according to requested rate
*/
- if (res_feat->timestamp) {
/* the current feature is the ts-rate feature */
if (res_feat->ts_rate) {
ts_rate_val = feat_csdev->regs_csdev[TS_RATE_REG_VAL_IDX].reg_desc.val32;
etm4_cfg_set_ts_rate(feat_csdev->csdev, ts_rate_val);
} else
drvcfg->cfg |= BIT(11);
- }
- return err;
+}
+/* set the overall available resource masks for the device */ +static int etm4_cfg_set_res_mask(struct coresight_device *csdev) +{
- struct device *dev = csdev->dev.parent;
- struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
- struct etm4_cfg_resources *res;
- struct cscfg_res_impl_used *res_impl_used;
- res_impl_used = devm_kzalloc(dev, sizeof(*res_impl_used), GFP_KERNEL);
- if (!res_impl_used)
return -ENOMEM;
- res = &res_impl_used->impl;
- /* selectors */
- if (drvdata->nr_resource)
res->selectors = GENMASK((drvdata->nr_resource * 2) - 1, 0);
- /* comparators */
- if (drvdata->nr_addr_cmp)
res->addr_cmp = GENMASK(drvdata->nr_addr_cmp - 1, 0);
- if (drvdata->numvmidc)
res->vmid_cmp = GENMASK(drvdata->numvmidc - 1, 0);
- if (drvdata->numcidc)
res->cid_cmp = GENMASK(drvdata->numcidc - 1, 0);
- /* misc resources */
- if (drvdata->nr_cntr)
res->counters = GENMASK(drvdata->nr_cntr - 1, 0);
- if (drvdata->trccci)
res->cycle_cnt = 1;
- if (drvdata->trcbb)
res->branch_broadcast = 1;
- if (drvdata->ctxid_size)
res->ctxt_id = 1;
- if (drvdata->vmid_size)
res->vm_id = 1;
- if (drvdata->nrseqstate)
res->sequencer = 1;
- if (drvdata->retstack)
res->return_stack = 1;
- if (drvdata->ts_size) {
res->timestamp = 1;
if (drvdata->nr_cntr && drvdata->nr_resource)
res->ts_rate = 1;
- }
- etm4_cfg_dump_res_mask("device impl resources", &res_impl_used->impl);
- csdev->cscfg_res_mask = res_impl_used;
- return 0;
+}
+/*
- reads a descriptor and updates the resource mask structure
- checks resource indexes are valid.
- */
+static int etm4_cfg_update_res_from_desc(const struct cscfg_feature_desc *feat_desc,
struct etm4_cfg_resources *res)
+{
- struct cscfg_regval_desc *regs_desc = &feat_desc->regs_desc[0];
- u32 res_idx, hw_info;
- int i;
- for (i = 0; i < feat_desc->nr_regs; i++) {
if (regs_desc[i].type & CS_CFG_REG_TYPE_RESOURCE) {
res_idx = regs_desc[i].offset;
hw_info = regs_desc[i].hw_info;
switch (hw_info & ETM4_CFG_RES_MASK) {
case ETM4_CFG_RES_CTR:
if (res_idx >= ETMv4_MAX_CNTR)
goto invalid_resource_err;
res->counters |= BIT(res_idx);
break;
case ETM4_CFG_RES_CMP:
if (res_idx >= ETM_MAX_SINGLE_ADDR_CMP)
goto invalid_resource_err;
res->addr_cmp |= BIT(res_idx);
break;
case ETM4_CFG_RES_SEL:
if (res_idx >= ETM_MAX_RES_SEL)
goto invalid_resource_err;
res->selectors |= BIT(res_idx);
break;
case ETM4_CFG_RES_SEQ:
res->sequencer = 1;
break;
case ETM4_CFG_RES_TS:
res->timestamp = 1;
if (etm4_cfg_feat_is_ts_rate(feat_desc))
res->ts_rate = 1;
break;
case ETM4_CFG_RES_BITCTRL:
if (hw_info & ETM4_BITCTRL_BRANCH_BROADCAST)
res->branch_broadcast = 1;
if (hw_info & ETM4_BITCTRL_CYCLE_COUNT)
res->cycle_cnt = 1;
if (hw_info & ETM4_BITCTRL_CTXTID)
res->ctxt_id = 1;
if (hw_info & ETM4_BITCTRL_VMID)
res->vm_id = 1;
if (hw_info & ETM4_BITCTRL_RETSTACK)
res->return_stack = 1;
break;
case ETM4_CFG_RES_CID_CMP:
if (res_idx >= ETMv4_MAX_CTXID_CMP)
goto invalid_resource_err;
res->cid_cmp |= BIT(res_idx);
break;
case ETM4_CFG_RES_VID_CMP:
if (res_idx >= ETM_MAX_VMID_CMP)
goto invalid_resource_err;
res->vmid_cmp |= BIT(res_idx);
break;
}
}
- }
- return 0;
+invalid_resource_err:
- pr_err("Error: Invalid resource values in feature %s\n", feat_desc->name);
- return -EINVAL;
+} +/*
- Check that the device contains the minimum resources required to support the
- described @feat_desc. Return -ENODEV if missing required resources.
- */
+static int etm4_cfg_check_feat_res(struct coresight_device *csdev,
struct cscfg_feature_desc *feat_desc)
+{
- struct etm4_cfg_resources req_res;
- struct cscfg_res_impl_used *dev_res;
- int err;
- /* create a resource mask from descriptor and validate */
- memset(&req_res, 0, sizeof(req_res));
- err = etm4_cfg_update_res_from_desc(feat_desc, &req_res);
- etm4_cfg_dump_res_mask("check_feat_res", &req_res);
- if (!err) {
dev_res = (struct cscfg_res_impl_used *)csdev->cscfg_res_mask;
if (!etm4_cfg_check_impl(&dev_res->impl, &req_res))
return -ENODEV;
- }
- return err;
+}
+/*
- Allocate resource requirements for the feature before
- it is programmed into the system. Ensures that two or more features in a
- configuration do not try to use the same resources on the device.
- At this point we use the absolute programmed resources - we do not attempt
- to find alternate available resources. (e.g. if 2 features use selector 3,
- fail the 2nd feature - do not look for an alternative free selector).
- */
+static int etm4_cfg_alloc_feat_res(struct cscfg_feature_csdev *feat_csdev) +{
- struct coresight_device *csdev = feat_csdev->csdev;
- struct device *dev = csdev->dev.parent;
- struct etm4_cfg_resources *res_feat, *res_inuse;
- int err = 0;
- /* one off initialisation of resources required for this feature */
- if (!feat_csdev->res_used) {
res_feat = devm_kzalloc(dev, sizeof(*res_feat), GFP_KERNEL);
if (!res_feat)
return -ENOMEM;
err = etm4_cfg_update_res_from_desc(feat_csdev->feat_desc, res_feat);
if (err)
return err;
feat_csdev->res_used = res_feat;
- } else
res_feat = (struct etm4_cfg_resources *)feat_csdev->res_used;
- /* check that the device resources reqiured are not in use */
- res_inuse = &((struct cscfg_res_impl_used *)csdev->cscfg_res_mask)->used;
- if (!etm4_cfg_check_set_inuse(res_inuse, res_feat))
err = -ENOSPC;
- return err;
+}
+static void etm4_cfg_clear_feat_res(struct cscfg_feature_csdev *feat_csdev) +{
- struct coresight_device *csdev = feat_csdev->csdev;
- struct etm4_cfg_resources *res_feat, *res_inuse;
- res_feat = (struct etm4_cfg_resources *)feat_csdev->res_used;
- res_inuse = &((struct cscfg_res_impl_used *)csdev->cscfg_res_mask)->used;
- etm4_cfg_clear_inuse(res_inuse, res_feat);
+}
/* match information when loading configurations */ #define CS_CFG_ETM4_MATCH_FLAGS (CS_CFG_MATCH_CLASS_SRC_ALL | \ CS_CFG_MATCH_CLASS_SRC_ETM4) @@ -175,8 +578,138 @@ static int etm4_cfg_load_feature(struct coresight_device *csdev, int etm4_cscfg_register(struct coresight_device *csdev) { struct cscfg_csdev_feat_ops ops;
- int err = 0;
- err = etm4_cfg_set_res_mask(csdev);
- if (err)
return err;
ops.load_feat = &etm4_cfg_load_feature;
- ops.check_feat_res = &etm4_cfg_check_feat_res;
- ops.alloc_feat_res = &etm4_cfg_alloc_feat_res;
- ops.clear_feat_res = &etm4_cfg_clear_feat_res;
- ops.set_on_enable = &etm4_cfg_on_enable_feat;
- ops.clear_on_disable = 0;
s/0/NULL
return cscfg_register_csdev(csdev, CS_CFG_ETM4_MATCH_FLAGS, &ops); }
+/*
- find first available bit in implemented mask @impl, that is not set in @used mask.
- set bit in @used and return. Return -ENOSPC if no available bits.
- */
+int etm4_cfg_find_unused_idx(unsigned long *impl, unsigned long *used, int size) +{
- unsigned long end_idx, unused_idx;
- end_idx = find_first_zero_bit(impl, size);
- unused_idx = find_first_zero_bit(used, size);
- if (unused_idx < end_idx) {
*used |= BIT(unused_idx);
return (int)unused_idx;
- }
- return -ENOSPC;
+}
+/*
- find first available pair of bits in implemented mask @impl, that are not set in
- @used mask. First bit of pair will always be an even index.
- Set bits in @used and return. Return -ENOSPC if no available bits.
- */
+int etm4_cfg_find_unused_idx_pair(unsigned long *impl, unsigned long *used, int size) +{
- unsigned long end_idx, first_unused_idx, next_unused_idx;
- end_idx = find_first_zero_bit(impl, size);
- first_unused_idx = find_first_zero_bit(used, size);
- /*
* even indexes are the 1st in a pair, look through the comparators
* till a pair found or we are at the end of the list.
*/
- while (first_unused_idx < end_idx) {
/* first is an even number, if the next is free we have a pair */
if (!(first_unused_idx % 2)) {
next_unused_idx = find_next_zero_bit(used, size, first_unused_idx);
if (next_unused_idx == (first_unused_idx + 1)) {
*used |= BIT(first_unused_idx);
*used |= BIT(next_unused_idx);
return (int)first_unused_idx;
}
first_unused_idx = next_unused_idx;
} else
first_unused_idx = find_next_zero_bit(used, size, first_unused_idx);
- }
- return -ENOSPC;
+}
+/* built in timestamp rate for etm4x */ +static struct cscfg_parameter_desc ts_rate_param[] = {
- {
.name = "ts_rate_cycles",
.value = 100.
- },
+};
+static struct cscfg_regval_desc ts_rate_regs[] = {
- {
.type = CS_CFG_REG_TYPE_RESOURCE | CS_CFG_REG_TYPE_VAL_PARAM,
.offset = 0,
.hw_info = ETM4_CFG_RES_TS,
.param_idx = 0,
- },
+};
+static struct cscfg_feature_desc ts_rate_etm4x = {
- .name = "timestamp-rate",
- .description = "Enable timestamps and set rate they appear in the trace.\n"
- "Rate value is number of cycles between timestamp requests. Min value 1.\n",
- .match_flags = CS_CFG_MATCH_CLASS_SRC_ETM4,
- .nr_params = ARRAY_SIZE(ts_rate_param),
- .params_desc = ts_rate_param,
- .nr_regs = ARRAY_SIZE(ts_rate_regs),
- .regs_desc = ts_rate_regs,
+};
+static struct cscfg_feature_desc *etm4x_feats[] = {
- &ts_rate_etm4x,
- NULL,
+};
+static struct cscfg_config_desc *etm4x_cfgs[] = {
- NULL,
+};
+static struct cscfg_load_owner_info etm4x_mod_owner = {
- .type = CSCFG_OWNER_MODULE,
- .owner_handle = THIS_MODULE,
+};
+/*
- check if incoming feature is ts-rate
- */
+static bool etm4_cfg_feat_is_ts_rate(const struct cscfg_feature_desc *feat_desc) +{
- if (!strcmp(feat_desc->name, ts_rate_etm4x.name))
return true;
- return false;
+}
+/* load the etm4 builtin ts_rate feature into the system */ +int etm4_cscfg_load_builtin_cfg(void) +{
- int err;
- err = cscfg_load_config_sets(etm4x_cfgs, etm4x_feats, &etm4x_mod_owner);
- /* if currently loaded matching devs ts_rate, still allow to load */
- if (err == -ENODEV)
err = 0;
- return err;
+}
+void etm4_cscfg_unload_builtin_cfg(void) +{
- cscfg_unload_config_sets(&etm4x_mod_owner);
+} diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.h b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h index 32dab34c1dac..dd69a8ef522d 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-cfg.h +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h @@ -13,18 +13,200 @@ /* resource IDs */ +/*
- 12 bit resource ID:
- 3:0 = resource type in use.
- 11:4 = additional resource specific information.
- */
#define ETM4_CFG_RES_CTR 0x001 #define ETM4_CFG_RES_CMP 0x002 -#define ETM4_CFG_RES_CMP_PAIR0 0x003 -#define ETM4_CFG_RES_CMP_PAIR1 0x004 -#define ETM4_CFG_RES_SEL 0x005 -#define ETM4_CFG_RES_SEL_PAIR0 0x006 -#define ETM4_CFG_RES_SEL_PAIR1 0x007 -#define ETM4_CFG_RES_SEQ 0x008 -#define ETM4_CFG_RES_TS 0x009 +#define ETM4_CFG_RES_SEL 0x003 +#define ETM4_CFG_RES_SEQ 0x004 +#define ETM4_CFG_RES_TS 0x005 +#define ETM4_CFG_RES_BITCTRL 0x006 +#define ETM4_CFG_RES_CID_CMP 0x007 +#define ETM4_CFG_RES_VID_CMP 0x008 #define ETM4_CFG_RES_MASK 0x00F +/* additional bits to supplement _CFG_RES_CTR */ +#define ETM4_CTR_VAL 0x010 +#define ETM4_CTR_RLD 0x020 +#define ETM4_CTR_CTRL 0x040
+/* additional bits for address comparators _CFG_RES_CMP */ +#define ETM4_CMP_PAIR0 0x010 +#define ETM4_CMP_PAIR1 0x020 +#define ETM4_CMP_VAL 0x040 +#define ETM4_CMP_CTL 0x080
+/* additional bits for resource selectors _CFG_RES_SEL */ +#define ETM4_SEL_PAIR0 0x010 +#define ETM4_SEL_PAIR1 0x020
+/* addtional bits for sequencer _CFG_RES_SEQ */ +#define ETM4_SEQ_STATE_R 0x010 +#define ETM4_SEQ_RESET_R 0x020
+/* additional bits to supplement _CFG_RES_BITCTRL */ +#define ETM4_BITCTRL_BRANCH_BROADCAST 0x010 +#define ETM4_BITCTRL_CYCLE_COUNT 0x020 +#define ETM4_BITCTRL_CTXTID 0x040 +#define ETM4_BITCTRL_VMID 0x080 +#define ETM4_BITCTRL_RETSTACK 0x100
+/* error value when calculating resource register offset (max offset = 0xFFC) */ +#define ETM4_RES_OFFSET_ERR 0xFFF
+/* skip value if a bit control that is resolved later */ +#define ETM4_RES_OFFSET_SKIP 0xFFE
+/**
- Masks to indicate resource usage.
- @selectors: The resource selector regs - max 32 off
- @comparators: Comparators - address (16 max), context ID (8 max), VMID (8 max).
- @misc:- bitselected features, sequencer etc.
There is a discrepancy between the above and the structure itself.
- */
+struct etm4_cfg_resources {
I would name this etm4_cfg_resources_mask.
- u32 selectors;
- u16 addr_cmp;
- u8 cid_cmp;
- u8 vmid_cmp;
- u8 counters;
- union {
u32 misc;
struct {
u32 cycle_cnt:1;
u32 branch_broadcast:1;
u32 ctxt_id:1;
u32 vm_id:1;
u32 sequencer:1;
u32 return_stack:1;
u32 timestamp:1;
u32 ts_rate:1;
};
- };
+};
+/* structure to hold implemented & used resources for the coresight device */ +struct cscfg_res_impl_used {
And this etm4_cfg_resources.
Also, I find it quite confusing that some structure and functions start with etm4_cfg_ while other with etm4_cscfg_. Having a uniform naming convention would be greatly appreciated.
More comments tomorrow...
- struct etm4_cfg_resources impl;
- struct etm4_cfg_resources used;
+};
+/* resource mask tests */ +/* check implmented - ensure that all bits in @req exist in @impl */ +static inline bool etm4_cfg_check_impl(struct etm4_cfg_resources *impl,
struct etm4_cfg_resources *req)
+{
- /* invert impl then and req - anything set is outside impl mask */
- if ((~impl->selectors & req->selectors) ||
(~impl->addr_cmp & req->addr_cmp) ||
(~impl->cid_cmp & req->cid_cmp) ||
(~impl->vmid_cmp & req->vmid_cmp) ||
(~impl->counters & req->counters) ||
(~impl->misc & req->misc))
return false;
- return true;
+}
+/* check @req not @inuse, & set @inuse if free (assumes @req passed the impl check) */ +static inline bool etm4_cfg_check_set_inuse(struct etm4_cfg_resources *inuse,
struct etm4_cfg_resources *req)
+{
- /* first check for hits between inuse and requested bits */
- if ((inuse->selectors & req->selectors) ||
(inuse->addr_cmp & req->addr_cmp) ||
(inuse->cid_cmp & req->cid_cmp) ||
(inuse->vmid_cmp & req->vmid_cmp) ||
(inuse->counters & req->counters) ||
(inuse->misc & req->misc))
return false;
- /* set all requested bits as inuse */
- inuse->selectors |= req->selectors;
- inuse->addr_cmp |= req->addr_cmp;
- inuse->cid_cmp |= req->cid_cmp;
- inuse->vmid_cmp |= req->vmid_cmp;
- inuse->counters |= req->counters;
- inuse->misc |= req->misc;
- return true;
+}
+static inline void etm4_cfg_clear_inuse(struct etm4_cfg_resources *inuse,
struct etm4_cfg_resources *req)
+{
- /* clear requested bits from inuse */
- inuse->selectors &= ~req->selectors;
- inuse->addr_cmp &= ~req->addr_cmp;
- inuse->cid_cmp &= ~req->cid_cmp;
- inuse->vmid_cmp &= ~req->vmid_cmp;
- inuse->counters &= ~req->counters;
- inuse->misc &= ~req->misc;
+}
/* ETMv4 specific config functions */ int etm4_cscfg_register(struct coresight_device *csdev); +int etm4_cfg_find_unused_idx(unsigned long *impl, unsigned long *used, int size); +int etm4_cfg_find_unused_idx_pair(unsigned long *impl, unsigned long *used, int size); +void etm4_cfg_set_ts_rate(struct coresight_device *csdev, u32 ts_rate_val);
+/* register etm4x builtins with cscfg on module load */ +int etm4_cscfg_load_builtin_cfg(void); +void etm4_cscfg_unload_builtin_cfg(void);
+/*
- Set of functions to find an available resource from @res->impl, not already marked as used
- in @res->used.
- return index and mark as used in @res->used. return -ENOSPC if nothing available.
- */
+static inline int etm4_res_find_selector(struct cscfg_res_impl_used *res) +{
- unsigned long *impl, *used;
- if (!res->impl.selectors)
return -ENOSPC;
- impl = (unsigned long *)&res->impl.selectors;
- used = (unsigned long *)&res->used.selectors;
- return etm4_cfg_find_unused_idx(impl, used, ETM_MAX_RES_SEL);
+}
+static inline int etm4_res_find_counter(struct cscfg_res_impl_used *res) +{
- unsigned long *impl, *used;
- if (!res->impl.counters)
return -ENOSPC;
- impl = (unsigned long *)&res->impl.counters;
- used = (unsigned long *)&res->used.counters;
- return etm4_cfg_find_unused_idx(impl, used, ETMv4_MAX_CNTR);
+}
+static inline int etm4_res_find_addr_comparator(struct cscfg_res_impl_used *res) +{
- unsigned long *impl, *used;
- if (!res->impl.addr_cmp)
return -ENOSPC;
- impl = (unsigned long *)&res->impl.addr_cmp;
- used = (unsigned long *)&res->used.addr_cmp;
- return etm4_cfg_find_unused_idx(impl, used, ETM_MAX_SINGLE_ADDR_CMP);
+}
+static inline int etm4_res_find_addr_comp_pair(struct cscfg_res_impl_used *res) +{
- unsigned long *impl, *used;
- if (!res->impl.addr_cmp)
return -ENOSPC;
- impl = (unsigned long *)&res->impl.addr_cmp;
- used = (unsigned long *)&res->used.addr_cmp;
- return etm4_cfg_find_unused_idx_pair(impl, used, ETM_MAX_SINGLE_ADDR_CMP);
+} #endif /* CORESIGHT_ETM4X_CFG_H */ diff --git a/include/linux/coresight.h b/include/linux/coresight.h index a348049ee08b..b513964b9305 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -223,6 +223,7 @@ struct coresight_sysfs_link {
- @feature_csdev_list: List of complex feature programming added to the device.
- @config_csdev_list: List of system configurations added to the device.
- @active_cscfg_ctxt: Context information for current active system configuration.
*/
- @cscfg_res_mask: Available device specific resources usable in features.
struct coresight_device { struct coresight_platform_data *pdata; @@ -248,6 +249,7 @@ struct coresight_device { struct list_head feature_csdev_list; struct list_head config_csdev_list; void *active_cscfg_ctxt;
- void *cscfg_res_mask;
}; /* -- 2.17.1
On Wed, May 12, 2021 at 10:17:50PM +0100, Mike Leach wrote:
Adds resource management to configuration and feature handling for ETM4 using the system configuration resource management API.
Allows specification of ETM4 resources when creating configurations and features.
Adds in checking and validation of resources used by features to prevent over allocation when multiple features used in a configuration.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-etm4x-cfg.c | 533 ++++++++++++++++++ .../hwtracing/coresight/coresight-etm4x-cfg.h | 196 ++++++- include/linux/coresight.h | 2 + 3 files changed, 724 insertions(+), 7 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c index d2ea903231b2..ba6d20b58a9a 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c @@ -28,6 +28,11 @@ } \ } +/* check for a match to ts rate */ +static bool etm4_cfg_feat_is_ts_rate(const struct cscfg_feature_desc *feat_desc);
+#define TS_RATE_REG_VAL_IDX 0
/**
- etm4_cfg_map_reg_offset - validate and map the register offset into a
location in the driver config struct.
@@ -128,6 +133,66 @@ static int etm4_cfg_map_reg_offset(struct etmv4_drvdata *drvdata, return err; } +static void etm4_cfg_dump_res_mask(const char *name, struct etm4_cfg_resources *res) +{
- pr_debug("Mask %s\n", name);
- pr_debug("selectors %08x; addr_cmp %04x\n", res->selectors, res->addr_cmp);
- pr_debug("cid_cmp %02x; vmid_cmp %02x; counters %02x\n", res->cid_cmp,
res->vmid_cmp, res->counters);
- pr_debug("misc bits %08x\n", res->misc);
+}
+/*
- generate an address offset from a resource type and index
- Bit selected resources will return a ETM4_RES_OFFSET_SKIP value
- as these require special handling on enable / disable.
- */
+static u32 etm4_cfg_get_res_offset(u16 res_type, u32 res_idx) +{
- u32 offset = ETM4_RES_OFFSET_ERR;
- switch (res_type & ETM4_CFG_RES_MASK) {
- case ETM4_CFG_RES_CTR:
if (res_type & ETM4_CTR_VAL)
offset = TRCCNTVRn(res_idx);
else if (res_type & ETM4_CTR_RLD)
offset = TRCCNTRLDVRn(res_idx);
else if (res_type & ETM4_CTR_CTRL)
offset = TRCCNTCTLRn(res_idx);
break;
- case ETM4_CFG_RES_CMP:
if (res_type & ETM4_CMP_VAL)
offset = TRCACVRn(res_idx);
else if (res_type & ETM4_CMP_CTL)
offset = TRCACATRn(res_idx);
break;
- case ETM4_CFG_RES_SEL:
offset = TRCRSCTLRn(res_idx);
break;
- case ETM4_CFG_RES_SEQ:
if (res_type & ETM4_SEQ_STATE_R)
offset = TRCSEQEVRn(res_idx);
else if (res_type & ETM4_SEQ_RESET_R)
offset = TRCSEQRSTEVR;
break;
- case ETM4_CFG_RES_CID_CMP:
offset = TRCCIDCVRn(res_idx);
break;
- case ETM4_CFG_RES_VID_CMP:
offset = TRCVMIDCVRn(res_idx);
break;
/* these two have dedicated enable functions, no address needed */
- case ETM4_CFG_RES_BITCTRL:
- case ETM4_CFG_RES_TS:
offset = ETM4_RES_OFFSET_SKIP;
break;
- }
- return offset;
+}
/**
- etm4_cfg_load_feature - load a feature into a device instance.
@@ -163,11 +228,349 @@ static int etm4_cfg_load_feature(struct coresight_device *csdev, /* process the register descriptions */ for (i = 0; i < feat_csdev->nr_regs && !err; i++) { offset = feat_desc->regs_desc[i].offset;
/* resource needs conversion to a register access value */
if (feat_desc->regs_desc[i].type & CS_CFG_REG_TYPE_RESOURCE) {
offset = etm4_cfg_get_res_offset(feat_desc->regs_desc[i].hw_info,
offset);
if (offset == ETM4_RES_OFFSET_ERR) {
err = -ENODEV;
break;
} else if (offset == ETM4_RES_OFFSET_SKIP)
continue;
err = etm4_cfg_map_reg_offset(drvdata, &feat_csdev->regs_csdev[i], offset); } return err;}
} +/*
- ts rate - set a counter to emit timestamp requests at a set interval.
- if we have sufficient resources then we use a counter and resource
- selector to achieve this.
- However, if not then do the best possible - which prevents the perf
- event timestamp request from failing if any configuration selection
- is using resources. e.g. when profiling, timestamps do not really matter.
- */
+void etm4_cfg_set_ts_rate(struct coresight_device *csdev, u32 ts_rate_val) +{
- struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
- struct etmv4_config *drvcfg = &drvdata->config;
- struct cscfg_res_impl_used *res_impl_used;
- int counter_idx, res_sel_idx;
- u32 tsctlr_val = 0;
- res_impl_used = (struct cscfg_res_impl_used *)csdev->cscfg_res_mask;
- /* look for resources */
- counter_idx = etm4_res_find_counter(res_impl_used);
- res_sel_idx = etm4_res_find_selector(res_impl_used);
- if (counter_idx >= 0 && res_sel_idx >= 0) {
/* counter and selector - can set up ts rate normally */
/*
* counter @ 1 and reload @ rate supplied -
* immediate timestamp then every rate
*/
drvcfg->cntr_val[counter_idx] = 1;
drvcfg->cntrldvr[counter_idx] = ts_rate_val;
/*
* counter ctrl - bit 16: 1 for reload self,
* bit 7: 0 single event,
* bit 6:0 res sel 1 - true
*/
drvcfg->cntr_ctrl[counter_idx] = 0x1 << 16 | 0x1;
/*
* set up resource selector for the counter.
* bits 19:16 - group 0b0010 counter
* bits 15:0 - bit select for counter idx
*/
drvcfg->res_ctrl[res_sel_idx] = (0x2 << 16) | (0x1 << counter_idx);
/* single selector bit 7 == 0, bit 6:0 - selector index */
tsctlr_val = res_sel_idx;
- } else if (ts_rate_val == 1) {
/*
* perf always tries to use a min value -
* emulate by setting the ts event to true
*/
/* single selector bit 7 == 0, bit 6:0 - selector 1 - always true */
tsctlr_val = 0x1;
- }
- /* set the configr reg to enable TS, and the ts control reg */
- drvcfg->ts_ctrl = tsctlr_val;
- drvcfg->cfg |= BIT(11);
+}
+/*
- on enable a feature - called after generic routine has programmed other registers.
- handle bit selects and custom elements
- */
+static int etm4_cfg_on_enable_feat(struct cscfg_feature_csdev *feat_csdev) +{
- int err = 0;
- struct etm4_cfg_resources *res_feat;
- struct device *dev = feat_csdev->csdev->dev.parent;
- struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
- struct etmv4_config *drvcfg = &drvdata->config;
struct etmv4_config *config = ...
That way we are conformant with that is already done in coresight-etm4x-core.c
- u32 ts_rate_val;
- /*
* look for the bit selected resources in this feature and set driver
* values to be programmed when enabling hardware.
*/
- res_feat = (struct etm4_cfg_resources *)feat_csdev->res_used;
- /* if none of the bit selected resources in use, exit early */
- if (!res_feat->misc)
return 0;
- /* otherwise check each and set as required */
- if (res_feat->ctxt_id)
drvcfg->cfg |= BIT(6);
- if (res_feat->vm_id)
drvcfg->cfg |= BIT(7);
- /* return stack is bit 12 in config register */
- if (res_feat->return_stack)
drvcfg->cfg |= BIT(12);
- /* branch broadcast - feature using this must program the bbctlr */
- if (res_feat->branch_broadcast)
drvcfg->cfg |= BIT(3);
- /* cycle count */
- if (res_feat->cycle_cnt) {
drvcfg->cfg |= BIT(4);
/* TRM: Must program this for cycacc to work - ensure mun permitted */
s/mun/min
if (drvcfg->ccctlr < drvdata->ccitmin)
drvcfg->ccctlr = drvdata->ccitmin;
- }
We have defines for those in coresight-pmu.h, please reuse them. Only the branch broadcast option hasn't been define so just go ahead and define one.
- /*
* timestamps - if not ts-rate just set to on, otherwise
* set using reload counter according to requested rate
*/
- if (res_feat->timestamp) {
/* the current feature is the ts-rate feature */
if (res_feat->ts_rate) {
ts_rate_val = feat_csdev->regs_csdev[TS_RATE_REG_VAL_IDX].reg_desc.val32;
Here we are weaving a feature specific configuration into generic code, something that won't be possible to maintain on the long run. I will comment further on the ts-rate feature later when I have reviewed the rest of this set.
etm4_cfg_set_ts_rate(feat_csdev->csdev, ts_rate_val);
} else
drvcfg->cfg |= BIT(11);
- }
- return err;
+}
+/* set the overall available resource masks for the device */ +static int etm4_cfg_set_res_mask(struct coresight_device *csdev) +{
- struct device *dev = csdev->dev.parent;
- struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
- struct etm4_cfg_resources *res;
- struct cscfg_res_impl_used *res_impl_used;
- res_impl_used = devm_kzalloc(dev, sizeof(*res_impl_used), GFP_KERNEL);
- if (!res_impl_used)
return -ENOMEM;
- res = &res_impl_used->impl;
- /* selectors */
- if (drvdata->nr_resource)
res->selectors = GENMASK((drvdata->nr_resource * 2) - 1, 0);
- /* comparators */
- if (drvdata->nr_addr_cmp)
res->addr_cmp = GENMASK(drvdata->nr_addr_cmp - 1, 0);
- if (drvdata->numvmidc)
res->vmid_cmp = GENMASK(drvdata->numvmidc - 1, 0);
- if (drvdata->numcidc)
res->cid_cmp = GENMASK(drvdata->numcidc - 1, 0);
- /* misc resources */
- if (drvdata->nr_cntr)
res->counters = GENMASK(drvdata->nr_cntr - 1, 0);
- if (drvdata->trccci)
res->cycle_cnt = 1;
- if (drvdata->trcbb)
res->branch_broadcast = 1;
- if (drvdata->ctxid_size)
res->ctxt_id = 1;
- if (drvdata->vmid_size)
res->vm_id = 1;
- if (drvdata->nrseqstate)
res->sequencer = 1;
- if (drvdata->retstack)
res->return_stack = 1;
- if (drvdata->ts_size) {
res->timestamp = 1;
if (drvdata->nr_cntr && drvdata->nr_resource)
res->ts_rate = 1;
- }
- etm4_cfg_dump_res_mask("device impl resources", &res_impl_used->impl);
- csdev->cscfg_res_mask = res_impl_used;
- return 0;
+}
+/*
- reads a descriptor and updates the resource mask structure
- checks resource indexes are valid.
- */
+static int etm4_cfg_update_res_from_desc(const struct cscfg_feature_desc *feat_desc,
struct etm4_cfg_resources *res)
+{
- struct cscfg_regval_desc *regs_desc = &feat_desc->regs_desc[0];
- u32 res_idx, hw_info;
- int i;
- for (i = 0; i < feat_desc->nr_regs; i++) {
if (regs_desc[i].type & CS_CFG_REG_TYPE_RESOURCE) {
res_idx = regs_desc[i].offset;
hw_info = regs_desc[i].hw_info;
switch (hw_info & ETM4_CFG_RES_MASK) {
case ETM4_CFG_RES_CTR:
if (res_idx >= ETMv4_MAX_CNTR)
goto invalid_resource_err;
res->counters |= BIT(res_idx);
break;
case ETM4_CFG_RES_CMP:
if (res_idx >= ETM_MAX_SINGLE_ADDR_CMP)
goto invalid_resource_err;
res->addr_cmp |= BIT(res_idx);
break;
case ETM4_CFG_RES_SEL:
if (res_idx >= ETM_MAX_RES_SEL)
goto invalid_resource_err;
res->selectors |= BIT(res_idx);
break;
case ETM4_CFG_RES_SEQ:
res->sequencer = 1;
break;
case ETM4_CFG_RES_TS:
res->timestamp = 1;
if (etm4_cfg_feat_is_ts_rate(feat_desc))
res->ts_rate = 1;
break;
case ETM4_CFG_RES_BITCTRL:
if (hw_info & ETM4_BITCTRL_BRANCH_BROADCAST)
res->branch_broadcast = 1;
if (hw_info & ETM4_BITCTRL_CYCLE_COUNT)
res->cycle_cnt = 1;
if (hw_info & ETM4_BITCTRL_CTXTID)
res->ctxt_id = 1;
if (hw_info & ETM4_BITCTRL_VMID)
res->vm_id = 1;
if (hw_info & ETM4_BITCTRL_RETSTACK)
res->return_stack = 1;
break;
case ETM4_CFG_RES_CID_CMP:
if (res_idx >= ETMv4_MAX_CTXID_CMP)
goto invalid_resource_err;
res->cid_cmp |= BIT(res_idx);
break;
case ETM4_CFG_RES_VID_CMP:
if (res_idx >= ETM_MAX_VMID_CMP)
goto invalid_resource_err;
res->vmid_cmp |= BIT(res_idx);
break;
}
}
- }
- return 0;
+invalid_resource_err:
- pr_err("Error: Invalid resource values in feature %s\n", feat_desc->name);
- return -EINVAL;
+} +/*
- Check that the device contains the minimum resources required to support the
- described @feat_desc. Return -ENODEV if missing required resources.
- */
+static int etm4_cfg_check_feat_res(struct coresight_device *csdev,
struct cscfg_feature_desc *feat_desc)
+{
- struct etm4_cfg_resources req_res;
- struct cscfg_res_impl_used *dev_res;
- int err;
- /* create a resource mask from descriptor and validate */
- memset(&req_res, 0, sizeof(req_res));
- err = etm4_cfg_update_res_from_desc(feat_desc, &req_res);
- etm4_cfg_dump_res_mask("check_feat_res", &req_res);
- if (!err) {
dev_res = (struct cscfg_res_impl_used *)csdev->cscfg_res_mask;
if (!etm4_cfg_check_impl(&dev_res->impl, &req_res))
return -ENODEV;
- }
- return err;
+}
+/*
- Allocate resource requirements for the feature before
- it is programmed into the system. Ensures that two or more features in a
- configuration do not try to use the same resources on the device.
- At this point we use the absolute programmed resources - we do not attempt
- to find alternate available resources. (e.g. if 2 features use selector 3,
- fail the 2nd feature - do not look for an alternative free selector).
- */
+static int etm4_cfg_alloc_feat_res(struct cscfg_feature_csdev *feat_csdev) +{
- struct coresight_device *csdev = feat_csdev->csdev;
- struct device *dev = csdev->dev.parent;
- struct etm4_cfg_resources *res_feat, *res_inuse;
- int err = 0;
- /* one off initialisation of resources required for this feature */
- if (!feat_csdev->res_used) {
res_feat = devm_kzalloc(dev, sizeof(*res_feat), GFP_KERNEL);
if (!res_feat)
return -ENOMEM;
err = etm4_cfg_update_res_from_desc(feat_csdev->feat_desc, res_feat);
if (err)
return err;
feat_csdev->res_used = res_feat;
- } else
res_feat = (struct etm4_cfg_resources *)feat_csdev->res_used;
Where would this have been allocated? Since this his the allocation function is it very confusing to find a valid resource structure.
- /* check that the device resources reqiured are not in use */
- res_inuse = &((struct cscfg_res_impl_used *)csdev->cscfg_res_mask)->used;
- if (!etm4_cfg_check_set_inuse(res_inuse, res_feat))
err = -ENOSPC;
- return err;
+}
+static void etm4_cfg_clear_feat_res(struct cscfg_feature_csdev *feat_csdev) +{
- struct coresight_device *csdev = feat_csdev->csdev;
- struct etm4_cfg_resources *res_feat, *res_inuse;
- res_feat = (struct etm4_cfg_resources *)feat_csdev->res_used;
- res_inuse = &((struct cscfg_res_impl_used *)csdev->cscfg_res_mask)->used;
- etm4_cfg_clear_inuse(res_inuse, res_feat);
+}
/* match information when loading configurations */ #define CS_CFG_ETM4_MATCH_FLAGS (CS_CFG_MATCH_CLASS_SRC_ALL | \ CS_CFG_MATCH_CLASS_SRC_ETM4) @@ -175,8 +578,138 @@ static int etm4_cfg_load_feature(struct coresight_device *csdev, int etm4_cscfg_register(struct coresight_device *csdev) { struct cscfg_csdev_feat_ops ops;
- int err = 0;
- err = etm4_cfg_set_res_mask(csdev);
- if (err)
return err;
ops.load_feat = &etm4_cfg_load_feature;
- ops.check_feat_res = &etm4_cfg_check_feat_res;
- ops.alloc_feat_res = &etm4_cfg_alloc_feat_res;
- ops.clear_feat_res = &etm4_cfg_clear_feat_res;
- ops.set_on_enable = &etm4_cfg_on_enable_feat;
- ops.clear_on_disable = 0;
This hunk and the function pointers it references should be in a patch on its own (still part of this series).
return cscfg_register_csdev(csdev, CS_CFG_ETM4_MATCH_FLAGS, &ops); }
+/*
- find first available bit in implemented mask @impl, that is not set in @used mask.
- set bit in @used and return. Return -ENOSPC if no available bits.
- */
+int etm4_cfg_find_unused_idx(unsigned long *impl, unsigned long *used, int size) +{
- unsigned long end_idx, unused_idx;
- end_idx = find_first_zero_bit(impl, size);
- unused_idx = find_first_zero_bit(used, size);
- if (unused_idx < end_idx) {
*used |= BIT(unused_idx);
return (int)unused_idx;
- }
- return -ENOSPC;
+}
+/*
- find first available pair of bits in implemented mask @impl, that are not set in
- @used mask. First bit of pair will always be an even index.
- Set bits in @used and return. Return -ENOSPC if no available bits.
- */
+int etm4_cfg_find_unused_idx_pair(unsigned long *impl, unsigned long *used, int size) +{
- unsigned long end_idx, first_unused_idx, next_unused_idx;
- end_idx = find_first_zero_bit(impl, size);
- first_unused_idx = find_first_zero_bit(used, size);
- /*
* even indexes are the 1st in a pair, look through the comparators
* till a pair found or we are at the end of the list.
*/
- while (first_unused_idx < end_idx) {
/* first is an even number, if the next is free we have a pair */
if (!(first_unused_idx % 2)) {
next_unused_idx = find_next_zero_bit(used, size, first_unused_idx);
if (next_unused_idx == (first_unused_idx + 1)) {
*used |= BIT(first_unused_idx);
*used |= BIT(next_unused_idx);
return (int)first_unused_idx;
}
first_unused_idx = next_unused_idx;
} else
first_unused_idx = find_next_zero_bit(used, size, first_unused_idx);
- }
- return -ENOSPC;
+}
+/* built in timestamp rate for etm4x */ +static struct cscfg_parameter_desc ts_rate_param[] = {
- {
.name = "ts_rate_cycles",
.value = 100.
- },
+};
+static struct cscfg_regval_desc ts_rate_regs[] = {
- {
.type = CS_CFG_REG_TYPE_RESOURCE | CS_CFG_REG_TYPE_VAL_PARAM,
.offset = 0,
.hw_info = ETM4_CFG_RES_TS,
.param_idx = 0,
- },
+};
+static struct cscfg_feature_desc ts_rate_etm4x = {
- .name = "timestamp-rate",
- .description = "Enable timestamps and set rate they appear in the trace.\n"
- "Rate value is number of cycles between timestamp requests. Min value 1.\n",
- .match_flags = CS_CFG_MATCH_CLASS_SRC_ETM4,
- .nr_params = ARRAY_SIZE(ts_rate_param),
- .params_desc = ts_rate_param,
- .nr_regs = ARRAY_SIZE(ts_rate_regs),
- .regs_desc = ts_rate_regs,
+};
+static struct cscfg_feature_desc *etm4x_feats[] = {
- &ts_rate_etm4x,
- NULL,
+};
+static struct cscfg_config_desc *etm4x_cfgs[] = {
- NULL,
+};
+static struct cscfg_load_owner_info etm4x_mod_owner = {
- .type = CSCFG_OWNER_MODULE,
- .owner_handle = THIS_MODULE,
+};
+/*
- check if incoming feature is ts-rate
- */
+static bool etm4_cfg_feat_is_ts_rate(const struct cscfg_feature_desc *feat_desc) +{
- if (!strcmp(feat_desc->name, ts_rate_etm4x.name))
return true;
- return false;
+}
+/* load the etm4 builtin ts_rate feature into the system */ +int etm4_cscfg_load_builtin_cfg(void) +{
- int err;
- err = cscfg_load_config_sets(etm4x_cfgs, etm4x_feats, &etm4x_mod_owner);
- /* if currently loaded matching devs ts_rate, still allow to load */
- if (err == -ENODEV)
err = 0;
- return err;
+}
+void etm4_cscfg_unload_builtin_cfg(void) +{
- cscfg_unload_config_sets(&etm4x_mod_owner);
+} diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.h b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h index 32dab34c1dac..dd69a8ef522d 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-cfg.h +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h @@ -13,18 +13,200 @@ /* resource IDs */ +/*
- 12 bit resource ID:
- 3:0 = resource type in use.
- 11:4 = additional resource specific information.
- */
#define ETM4_CFG_RES_CTR 0x001 #define ETM4_CFG_RES_CMP 0x002 -#define ETM4_CFG_RES_CMP_PAIR0 0x003 -#define ETM4_CFG_RES_CMP_PAIR1 0x004 -#define ETM4_CFG_RES_SEL 0x005 -#define ETM4_CFG_RES_SEL_PAIR0 0x006 -#define ETM4_CFG_RES_SEL_PAIR1 0x007 -#define ETM4_CFG_RES_SEQ 0x008 -#define ETM4_CFG_RES_TS 0x009 +#define ETM4_CFG_RES_SEL 0x003 +#define ETM4_CFG_RES_SEQ 0x004 +#define ETM4_CFG_RES_TS 0x005 +#define ETM4_CFG_RES_BITCTRL 0x006 +#define ETM4_CFG_RES_CID_CMP 0x007 +#define ETM4_CFG_RES_VID_CMP 0x008 #define ETM4_CFG_RES_MASK 0x00F +/* additional bits to supplement _CFG_RES_CTR */ +#define ETM4_CTR_VAL 0x010 +#define ETM4_CTR_RLD 0x020 +#define ETM4_CTR_CTRL 0x040
+/* additional bits for address comparators _CFG_RES_CMP */ +#define ETM4_CMP_PAIR0 0x010 +#define ETM4_CMP_PAIR1 0x020 +#define ETM4_CMP_VAL 0x040 +#define ETM4_CMP_CTL 0x080
+/* additional bits for resource selectors _CFG_RES_SEL */ +#define ETM4_SEL_PAIR0 0x010 +#define ETM4_SEL_PAIR1 0x020
+/* addtional bits for sequencer _CFG_RES_SEQ */ +#define ETM4_SEQ_STATE_R 0x010 +#define ETM4_SEQ_RESET_R 0x020
+/* additional bits to supplement _CFG_RES_BITCTRL */ +#define ETM4_BITCTRL_BRANCH_BROADCAST 0x010 +#define ETM4_BITCTRL_CYCLE_COUNT 0x020 +#define ETM4_BITCTRL_CTXTID 0x040 +#define ETM4_BITCTRL_VMID 0x080 +#define ETM4_BITCTRL_RETSTACK 0x100
+/* error value when calculating resource register offset (max offset = 0xFFC) */ +#define ETM4_RES_OFFSET_ERR 0xFFF
+/* skip value if a bit control that is resolved later */ +#define ETM4_RES_OFFSET_SKIP 0xFFE
+/**
- Masks to indicate resource usage.
- @selectors: The resource selector regs - max 32 off
- @comparators: Comparators - address (16 max), context ID (8 max), VMID (8 max).
- @misc:- bitselected features, sequencer etc.
- */
+struct etm4_cfg_resources {
- u32 selectors;
- u16 addr_cmp;
- u8 cid_cmp;
- u8 vmid_cmp;
- u8 counters;
- union {
u32 misc;
struct {
u32 cycle_cnt:1;
u32 branch_broadcast:1;
u32 ctxt_id:1;
u32 vm_id:1;
u32 sequencer:1;
u32 return_stack:1;
u32 timestamp:1;
u32 ts_rate:1;
};
- };
+};
+/* structure to hold implemented & used resources for the coresight device */ +struct cscfg_res_impl_used {
- struct etm4_cfg_resources impl;
- struct etm4_cfg_resources used;
+};
+/* resource mask tests */ +/* check implmented - ensure that all bits in @req exist in @impl */ +static inline bool etm4_cfg_check_impl(struct etm4_cfg_resources *impl,
struct etm4_cfg_resources *req)
+{
- /* invert impl then and req - anything set is outside impl mask */
- if ((~impl->selectors & req->selectors) ||
(~impl->addr_cmp & req->addr_cmp) ||
(~impl->cid_cmp & req->cid_cmp) ||
(~impl->vmid_cmp & req->vmid_cmp) ||
(~impl->counters & req->counters) ||
(~impl->misc & req->misc))
return false;
- return true;
+}
+/* check @req not @inuse, & set @inuse if free (assumes @req passed the impl check) */ +static inline bool etm4_cfg_check_set_inuse(struct etm4_cfg_resources *inuse,
struct etm4_cfg_resources *req)
+{
- /* first check for hits between inuse and requested bits */
- if ((inuse->selectors & req->selectors) ||
(inuse->addr_cmp & req->addr_cmp) ||
(inuse->cid_cmp & req->cid_cmp) ||
(inuse->vmid_cmp & req->vmid_cmp) ||
(inuse->counters & req->counters) ||
(inuse->misc & req->misc))
return false;
- /* set all requested bits as inuse */
- inuse->selectors |= req->selectors;
- inuse->addr_cmp |= req->addr_cmp;
- inuse->cid_cmp |= req->cid_cmp;
- inuse->vmid_cmp |= req->vmid_cmp;
- inuse->counters |= req->counters;
- inuse->misc |= req->misc;
- return true;
+}
+static inline void etm4_cfg_clear_inuse(struct etm4_cfg_resources *inuse,
struct etm4_cfg_resources *req)
+{
- /* clear requested bits from inuse */
- inuse->selectors &= ~req->selectors;
- inuse->addr_cmp &= ~req->addr_cmp;
- inuse->cid_cmp &= ~req->cid_cmp;
- inuse->vmid_cmp &= ~req->vmid_cmp;
- inuse->counters &= ~req->counters;
- inuse->misc &= ~req->misc;
+}
/* ETMv4 specific config functions */ int etm4_cscfg_register(struct coresight_device *csdev); +int etm4_cfg_find_unused_idx(unsigned long *impl, unsigned long *used, int size); +int etm4_cfg_find_unused_idx_pair(unsigned long *impl, unsigned long *used, int size); +void etm4_cfg_set_ts_rate(struct coresight_device *csdev, u32 ts_rate_val);
+/* register etm4x builtins with cscfg on module load */ +int etm4_cscfg_load_builtin_cfg(void); +void etm4_cscfg_unload_builtin_cfg(void);
+/*
- Set of functions to find an available resource from @res->impl, not already marked as used
- in @res->used.
- return index and mark as used in @res->used. return -ENOSPC if nothing available.
- */
+static inline int etm4_res_find_selector(struct cscfg_res_impl_used *res) +{
- unsigned long *impl, *used;
- if (!res->impl.selectors)
return -ENOSPC;
- impl = (unsigned long *)&res->impl.selectors;
- used = (unsigned long *)&res->used.selectors;
- return etm4_cfg_find_unused_idx(impl, used, ETM_MAX_RES_SEL);
+}
+static inline int etm4_res_find_counter(struct cscfg_res_impl_used *res) +{
- unsigned long *impl, *used;
- if (!res->impl.counters)
return -ENOSPC;
- impl = (unsigned long *)&res->impl.counters;
- used = (unsigned long *)&res->used.counters;
- return etm4_cfg_find_unused_idx(impl, used, ETMv4_MAX_CNTR);
+}
+static inline int etm4_res_find_addr_comparator(struct cscfg_res_impl_used *res) +{
- unsigned long *impl, *used;
- if (!res->impl.addr_cmp)
return -ENOSPC;
- impl = (unsigned long *)&res->impl.addr_cmp;
- used = (unsigned long *)&res->used.addr_cmp;
- return etm4_cfg_find_unused_idx(impl, used, ETM_MAX_SINGLE_ADDR_CMP);
+}
+static inline int etm4_res_find_addr_comp_pair(struct cscfg_res_impl_used *res) +{
- unsigned long *impl, *used;
- if (!res->impl.addr_cmp)
return -ENOSPC;
- impl = (unsigned long *)&res->impl.addr_cmp;
- used = (unsigned long *)&res->used.addr_cmp;
- return etm4_cfg_find_unused_idx_pair(impl, used, ETM_MAX_SINGLE_ADDR_CMP);
+} #endif /* CORESIGHT_ETM4X_CFG_H */ diff --git a/include/linux/coresight.h b/include/linux/coresight.h index a348049ee08b..b513964b9305 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -223,6 +223,7 @@ struct coresight_sysfs_link {
- @feature_csdev_list: List of complex feature programming added to the device.
- @config_csdev_list: List of system configurations added to the device.
- @active_cscfg_ctxt: Context information for current active system configuration.
*/
- @cscfg_res_mask: Available device specific resources usable in features.
struct coresight_device { struct coresight_platform_data *pdata; @@ -248,6 +249,7 @@ struct coresight_device { struct list_head feature_csdev_list; struct list_head config_csdev_list; void *active_cscfg_ctxt;
- void *cscfg_res_mask;
}; /* -- 2.17.1
On Wed, May 12, 2021 at 10:17:50PM +0100, Mike Leach wrote:
Adds resource management to configuration and feature handling for ETM4 using the system configuration resource management API.
Allows specification of ETM4 resources when creating configurations and features.
Adds in checking and validation of resources used by features to prevent over allocation when multiple features used in a configuration.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-etm4x-cfg.c | 533 ++++++++++++++++++ .../hwtracing/coresight/coresight-etm4x-cfg.h | 196 ++++++- include/linux/coresight.h | 2 + 3 files changed, 724 insertions(+), 7 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c index d2ea903231b2..ba6d20b58a9a 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c @@ -28,6 +28,11 @@ } \ } +/* check for a match to ts rate */ +static bool etm4_cfg_feat_is_ts_rate(const struct cscfg_feature_desc *feat_desc);
+#define TS_RATE_REG_VAL_IDX 0
/**
- etm4_cfg_map_reg_offset - validate and map the register offset into a
location in the driver config struct.
@@ -128,6 +133,66 @@ static int etm4_cfg_map_reg_offset(struct etmv4_drvdata *drvdata, return err; } +static void etm4_cfg_dump_res_mask(const char *name, struct etm4_cfg_resources *res) +{
- pr_debug("Mask %s\n", name);
- pr_debug("selectors %08x; addr_cmp %04x\n", res->selectors, res->addr_cmp);
- pr_debug("cid_cmp %02x; vmid_cmp %02x; counters %02x\n", res->cid_cmp,
res->vmid_cmp, res->counters);
- pr_debug("misc bits %08x\n", res->misc);
+}
+/*
- generate an address offset from a resource type and index
- Bit selected resources will return a ETM4_RES_OFFSET_SKIP value
- as these require special handling on enable / disable.
- */
+static u32 etm4_cfg_get_res_offset(u16 res_type, u32 res_idx) +{
- u32 offset = ETM4_RES_OFFSET_ERR;
- switch (res_type & ETM4_CFG_RES_MASK) {
- case ETM4_CFG_RES_CTR:
if (res_type & ETM4_CTR_VAL)
offset = TRCCNTVRn(res_idx);
else if (res_type & ETM4_CTR_RLD)
offset = TRCCNTRLDVRn(res_idx);
else if (res_type & ETM4_CTR_CTRL)
offset = TRCCNTCTLRn(res_idx);
break;
- case ETM4_CFG_RES_CMP:
if (res_type & ETM4_CMP_VAL)
offset = TRCACVRn(res_idx);
else if (res_type & ETM4_CMP_CTL)
offset = TRCACATRn(res_idx);
break;
- case ETM4_CFG_RES_SEL:
offset = TRCRSCTLRn(res_idx);
break;
- case ETM4_CFG_RES_SEQ:
if (res_type & ETM4_SEQ_STATE_R)
offset = TRCSEQEVRn(res_idx);
else if (res_type & ETM4_SEQ_RESET_R)
offset = TRCSEQRSTEVR;
break;
- case ETM4_CFG_RES_CID_CMP:
offset = TRCCIDCVRn(res_idx);
break;
- case ETM4_CFG_RES_VID_CMP:
offset = TRCVMIDCVRn(res_idx);
break;
/* these two have dedicated enable functions, no address needed */
- case ETM4_CFG_RES_BITCTRL:
fallthrough;
- case ETM4_CFG_RES_TS:
offset = ETM4_RES_OFFSET_SKIP;
break;
- }
- return offset;
+}
/**
- etm4_cfg_load_feature - load a feature into a device instance.
@@ -163,11 +228,349 @@ static int etm4_cfg_load_feature(struct coresight_device *csdev, /* process the register descriptions */ for (i = 0; i < feat_csdev->nr_regs && !err; i++) { offset = feat_desc->regs_desc[i].offset;
/* resource needs conversion to a register access value */
if (feat_desc->regs_desc[i].type & CS_CFG_REG_TYPE_RESOURCE) {
offset = etm4_cfg_get_res_offset(feat_desc->regs_desc[i].hw_info,
offset);
if (offset == ETM4_RES_OFFSET_ERR) {
err = -ENODEV;
break;
} else if (offset == ETM4_RES_OFFSET_SKIP)
continue;
err = etm4_cfg_map_reg_offset(drvdata, &feat_csdev->regs_csdev[i], offset); } return err;}
} +/*
- ts rate - set a counter to emit timestamp requests at a set interval.
- if we have sufficient resources then we use a counter and resource
- selector to achieve this.
- However, if not then do the best possible - which prevents the perf
- event timestamp request from failing if any configuration selection
- is using resources. e.g. when profiling, timestamps do not really matter.
To me it seems like we are adding too much intelligence, but we can have this conversation at a later time.
- */
+void etm4_cfg_set_ts_rate(struct coresight_device *csdev, u32 ts_rate_val) +{
- struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
- struct etmv4_config *drvcfg = &drvdata->config;
- struct cscfg_res_impl_used *res_impl_used;
- int counter_idx, res_sel_idx;
- u32 tsctlr_val = 0;
- res_impl_used = (struct cscfg_res_impl_used *)csdev->cscfg_res_mask;
- /* look for resources */
- counter_idx = etm4_res_find_counter(res_impl_used);
- res_sel_idx = etm4_res_find_selector(res_impl_used);
- if (counter_idx >= 0 && res_sel_idx >= 0) {
/* counter and selector - can set up ts rate normally */
/*
* counter @ 1 and reload @ rate supplied -
* immediate timestamp then every rate
*/
drvcfg->cntr_val[counter_idx] = 1;
drvcfg->cntrldvr[counter_idx] = ts_rate_val;
/*
* counter ctrl - bit 16: 1 for reload self,
* bit 7: 0 single event,
* bit 6:0 res sel 1 - true
*/
drvcfg->cntr_ctrl[counter_idx] = 0x1 << 16 | 0x1;
/*
* set up resource selector for the counter.
* bits 19:16 - group 0b0010 counter
* bits 15:0 - bit select for counter idx
*/
drvcfg->res_ctrl[res_sel_idx] = (0x2 << 16) | (0x1 << counter_idx);
/* single selector bit 7 == 0, bit 6:0 - selector index */
tsctlr_val = res_sel_idx;
- } else if (ts_rate_val == 1) {
/*
* perf always tries to use a min value -
* emulate by setting the ts event to true
*/
/* single selector bit 7 == 0, bit 6:0 - selector 1 - always true */
tsctlr_val = 0x1;
- }
- /* set the configr reg to enable TS, and the ts control reg */
- drvcfg->ts_ctrl = tsctlr_val;
- drvcfg->cfg |= BIT(11);
+}
+/*
- on enable a feature - called after generic routine has programmed other registers.
- handle bit selects and custom elements
- */
+static int etm4_cfg_on_enable_feat(struct cscfg_feature_csdev *feat_csdev) +{
- int err = 0;
- struct etm4_cfg_resources *res_feat;
- struct device *dev = feat_csdev->csdev->dev.parent;
- struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
- struct etmv4_config *drvcfg = &drvdata->config;
- u32 ts_rate_val;
- /*
* look for the bit selected resources in this feature and set driver
* values to be programmed when enabling hardware.
*/
- res_feat = (struct etm4_cfg_resources *)feat_csdev->res_used;
- /* if none of the bit selected resources in use, exit early */
- if (!res_feat->misc)
return 0;
- /* otherwise check each and set as required */
- if (res_feat->ctxt_id)
drvcfg->cfg |= BIT(6);
- if (res_feat->vm_id)
drvcfg->cfg |= BIT(7);
- /* return stack is bit 12 in config register */
- if (res_feat->return_stack)
drvcfg->cfg |= BIT(12);
- /* branch broadcast - feature using this must program the bbctlr */
- if (res_feat->branch_broadcast)
drvcfg->cfg |= BIT(3);
- /* cycle count */
- if (res_feat->cycle_cnt) {
drvcfg->cfg |= BIT(4);
/* TRM: Must program this for cycacc to work - ensure mun permitted */
if (drvcfg->ccctlr < drvdata->ccitmin)
drvcfg->ccctlr = drvdata->ccitmin;
- }
- /*
* timestamps - if not ts-rate just set to on, otherwise
* set using reload counter according to requested rate
*/
- if (res_feat->timestamp) {
/* the current feature is the ts-rate feature */
if (res_feat->ts_rate) {
ts_rate_val = feat_csdev->regs_csdev[TS_RATE_REG_VAL_IDX].reg_desc.val32;
etm4_cfg_set_ts_rate(feat_csdev->csdev, ts_rate_val);
I would like to see a better way to do this. Setting the timestamp rate should be treated like any other feature. If the current framework doesn't allow for it then it should be extended.
} else
drvcfg->cfg |= BIT(11);
- }
- return err;
+}
+/* set the overall available resource masks for the device */ +static int etm4_cfg_set_res_mask(struct coresight_device *csdev) +{
- struct device *dev = csdev->dev.parent;
- struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
- struct etm4_cfg_resources *res;
- struct cscfg_res_impl_used *res_impl_used;
- res_impl_used = devm_kzalloc(dev, sizeof(*res_impl_used), GFP_KERNEL);
- if (!res_impl_used)
return -ENOMEM;
- res = &res_impl_used->impl;
- /* selectors */
- if (drvdata->nr_resource)
res->selectors = GENMASK((drvdata->nr_resource * 2) - 1, 0);
- /* comparators */
- if (drvdata->nr_addr_cmp)
res->addr_cmp = GENMASK(drvdata->nr_addr_cmp - 1, 0);
- if (drvdata->numvmidc)
res->vmid_cmp = GENMASK(drvdata->numvmidc - 1, 0);
- if (drvdata->numcidc)
res->cid_cmp = GENMASK(drvdata->numcidc - 1, 0);
- /* misc resources */
- if (drvdata->nr_cntr)
res->counters = GENMASK(drvdata->nr_cntr - 1, 0);
- if (drvdata->trccci)
res->cycle_cnt = 1;
- if (drvdata->trcbb)
res->branch_broadcast = 1;
- if (drvdata->ctxid_size)
res->ctxt_id = 1;
- if (drvdata->vmid_size)
res->vm_id = 1;
- if (drvdata->nrseqstate)
res->sequencer = 1;
- if (drvdata->retstack)
res->return_stack = 1;
- if (drvdata->ts_size) {
res->timestamp = 1;
if (drvdata->nr_cntr && drvdata->nr_resource)
res->ts_rate = 1;
- }
- etm4_cfg_dump_res_mask("device impl resources", &res_impl_used->impl);
- csdev->cscfg_res_mask = res_impl_used;
- return 0;
+}
+/*
- reads a descriptor and updates the resource mask structure
- checks resource indexes are valid.
- */
+static int etm4_cfg_update_res_from_desc(const struct cscfg_feature_desc *feat_desc,
struct etm4_cfg_resources *res)
+{
- struct cscfg_regval_desc *regs_desc = &feat_desc->regs_desc[0];
- u32 res_idx, hw_info;
- int i;
- for (i = 0; i < feat_desc->nr_regs; i++) {
if (regs_desc[i].type & CS_CFG_REG_TYPE_RESOURCE) {
res_idx = regs_desc[i].offset;
hw_info = regs_desc[i].hw_info;
switch (hw_info & ETM4_CFG_RES_MASK) {
case ETM4_CFG_RES_CTR:
if (res_idx >= ETMv4_MAX_CNTR)
goto invalid_resource_err;
res->counters |= BIT(res_idx);
break;
case ETM4_CFG_RES_CMP:
if (res_idx >= ETM_MAX_SINGLE_ADDR_CMP)
goto invalid_resource_err;
res->addr_cmp |= BIT(res_idx);
break;
case ETM4_CFG_RES_SEL:
if (res_idx >= ETM_MAX_RES_SEL)
goto invalid_resource_err;
res->selectors |= BIT(res_idx);
break;
case ETM4_CFG_RES_SEQ:
res->sequencer = 1;
break;
case ETM4_CFG_RES_TS:
res->timestamp = 1;
if (etm4_cfg_feat_is_ts_rate(feat_desc))
res->ts_rate = 1;
break;
case ETM4_CFG_RES_BITCTRL:
if (hw_info & ETM4_BITCTRL_BRANCH_BROADCAST)
res->branch_broadcast = 1;
if (hw_info & ETM4_BITCTRL_CYCLE_COUNT)
res->cycle_cnt = 1;
if (hw_info & ETM4_BITCTRL_CTXTID)
res->ctxt_id = 1;
if (hw_info & ETM4_BITCTRL_VMID)
res->vm_id = 1;
if (hw_info & ETM4_BITCTRL_RETSTACK)
res->return_stack = 1;
break;
case ETM4_CFG_RES_CID_CMP:
if (res_idx >= ETMv4_MAX_CTXID_CMP)
goto invalid_resource_err;
res->cid_cmp |= BIT(res_idx);
break;
case ETM4_CFG_RES_VID_CMP:
if (res_idx >= ETM_MAX_VMID_CMP)
goto invalid_resource_err;
res->vmid_cmp |= BIT(res_idx);
break;
}
}
- }
- return 0;
+invalid_resource_err:
- pr_err("Error: Invalid resource values in feature %s\n", feat_desc->name);
- return -EINVAL;
+} +/*
- Check that the device contains the minimum resources required to support the
- described @feat_desc. Return -ENODEV if missing required resources.
- */
+static int etm4_cfg_check_feat_res(struct coresight_device *csdev,
struct cscfg_feature_desc *feat_desc)
+{
- struct etm4_cfg_resources req_res;
- struct cscfg_res_impl_used *dev_res;
- int err;
- /* create a resource mask from descriptor and validate */
- memset(&req_res, 0, sizeof(req_res));
- err = etm4_cfg_update_res_from_desc(feat_desc, &req_res);
- etm4_cfg_dump_res_mask("check_feat_res", &req_res);
- if (!err) {
dev_res = (struct cscfg_res_impl_used *)csdev->cscfg_res_mask;
if (!etm4_cfg_check_impl(&dev_res->impl, &req_res))
return -ENODEV;
- }
- return err;
+}
+/*
- Allocate resource requirements for the feature before
- it is programmed into the system. Ensures that two or more features in a
- configuration do not try to use the same resources on the device.
- At this point we use the absolute programmed resources - we do not attempt
- to find alternate available resources. (e.g. if 2 features use selector 3,
- fail the 2nd feature - do not look for an alternative free selector).
- */
+static int etm4_cfg_alloc_feat_res(struct cscfg_feature_csdev *feat_csdev) +{
- struct coresight_device *csdev = feat_csdev->csdev;
- struct device *dev = csdev->dev.parent;
- struct etm4_cfg_resources *res_feat, *res_inuse;
- int err = 0;
- /* one off initialisation of resources required for this feature */
- if (!feat_csdev->res_used) {
res_feat = devm_kzalloc(dev, sizeof(*res_feat), GFP_KERNEL);
if (!res_feat)
return -ENOMEM;
err = etm4_cfg_update_res_from_desc(feat_csdev->feat_desc, res_feat);
if (err)
return err;
feat_csdev->res_used = res_feat;
- } else
res_feat = (struct etm4_cfg_resources *)feat_csdev->res_used;
- /* check that the device resources reqiured are not in use */
- res_inuse = &((struct cscfg_res_impl_used *)csdev->cscfg_res_mask)->used;
- if (!etm4_cfg_check_set_inuse(res_inuse, res_feat))
err = -ENOSPC;
- return err;
+}
+static void etm4_cfg_clear_feat_res(struct cscfg_feature_csdev *feat_csdev) +{
- struct coresight_device *csdev = feat_csdev->csdev;
- struct etm4_cfg_resources *res_feat, *res_inuse;
- res_feat = (struct etm4_cfg_resources *)feat_csdev->res_used;
- res_inuse = &((struct cscfg_res_impl_used *)csdev->cscfg_res_mask)->used;
- etm4_cfg_clear_inuse(res_inuse, res_feat);
+}
/* match information when loading configurations */ #define CS_CFG_ETM4_MATCH_FLAGS (CS_CFG_MATCH_CLASS_SRC_ALL | \ CS_CFG_MATCH_CLASS_SRC_ETM4) @@ -175,8 +578,138 @@ static int etm4_cfg_load_feature(struct coresight_device *csdev, int etm4_cscfg_register(struct coresight_device *csdev) { struct cscfg_csdev_feat_ops ops;
- int err = 0;
- err = etm4_cfg_set_res_mask(csdev);
- if (err)
return err;
ops.load_feat = &etm4_cfg_load_feature;
- ops.check_feat_res = &etm4_cfg_check_feat_res;
- ops.alloc_feat_res = &etm4_cfg_alloc_feat_res;
- ops.clear_feat_res = &etm4_cfg_clear_feat_res;
- ops.set_on_enable = &etm4_cfg_on_enable_feat;
- ops.clear_on_disable = 0;
return cscfg_register_csdev(csdev, CS_CFG_ETM4_MATCH_FLAGS, &ops); }
+/*
- find first available bit in implemented mask @impl, that is not set in @used mask.
- set bit in @used and return. Return -ENOSPC if no available bits.
- */
+int etm4_cfg_find_unused_idx(unsigned long *impl, unsigned long *used, int size) +{
- unsigned long end_idx, unused_idx;
- end_idx = find_first_zero_bit(impl, size);
- unused_idx = find_first_zero_bit(used, size);
- if (unused_idx < end_idx) {
*used |= BIT(unused_idx);
return (int)unused_idx;
- }
- return -ENOSPC;
+}
+/*
- find first available pair of bits in implemented mask @impl, that are not set in
- @used mask. First bit of pair will always be an even index.
- Set bits in @used and return. Return -ENOSPC if no available bits.
- */
+int etm4_cfg_find_unused_idx_pair(unsigned long *impl, unsigned long *used, int size) +{
- unsigned long end_idx, first_unused_idx, next_unused_idx;
- end_idx = find_first_zero_bit(impl, size);
- first_unused_idx = find_first_zero_bit(used, size);
- /*
* even indexes are the 1st in a pair, look through the comparators
* till a pair found or we are at the end of the list.
*/
- while (first_unused_idx < end_idx) {
/* first is an even number, if the next is free we have a pair */
if (!(first_unused_idx % 2)) {
next_unused_idx = find_next_zero_bit(used, size, first_unused_idx);
if (next_unused_idx == (first_unused_idx + 1)) {
*used |= BIT(first_unused_idx);
*used |= BIT(next_unused_idx);
return (int)first_unused_idx;
}
first_unused_idx = next_unused_idx;
} else
first_unused_idx = find_next_zero_bit(used, size, first_unused_idx);
- }
- return -ENOSPC;
+}
+/* built in timestamp rate for etm4x */ +static struct cscfg_parameter_desc ts_rate_param[] = {
- {
.name = "ts_rate_cycles",
.value = 100.
- },
+};
+static struct cscfg_regval_desc ts_rate_regs[] = {
- {
.type = CS_CFG_REG_TYPE_RESOURCE | CS_CFG_REG_TYPE_VAL_PARAM,
.offset = 0,
.hw_info = ETM4_CFG_RES_TS,
.param_idx = 0,
- },
+};
+static struct cscfg_feature_desc ts_rate_etm4x = {
- .name = "timestamp-rate",
- .description = "Enable timestamps and set rate they appear in the trace.\n"
- "Rate value is number of cycles between timestamp requests. Min value 1.\n",
- .match_flags = CS_CFG_MATCH_CLASS_SRC_ETM4,
- .nr_params = ARRAY_SIZE(ts_rate_param),
- .params_desc = ts_rate_param,
- .nr_regs = ARRAY_SIZE(ts_rate_regs),
- .regs_desc = ts_rate_regs,
+};
I vote for leaving the timestamp rate feature out of this set. It is introducing a fair amount of complexity to a patchset that is already complex. When we move forward with it, it should be in its own file like we did for autoFDO.
I am out of time for this set and as such will stop here. We can tackle the rest in future revisions.
Thanks, Mathieu
+static struct cscfg_feature_desc *etm4x_feats[] = {
- &ts_rate_etm4x,
- NULL,
+};
+static struct cscfg_config_desc *etm4x_cfgs[] = {
- NULL,
+};
+static struct cscfg_load_owner_info etm4x_mod_owner = {
- .type = CSCFG_OWNER_MODULE,
- .owner_handle = THIS_MODULE,
+};
+/*
- check if incoming feature is ts-rate
- */
+static bool etm4_cfg_feat_is_ts_rate(const struct cscfg_feature_desc *feat_desc) +{
- if (!strcmp(feat_desc->name, ts_rate_etm4x.name))
return true;
- return false;
+}
+/* load the etm4 builtin ts_rate feature into the system */ +int etm4_cscfg_load_builtin_cfg(void) +{
- int err;
- err = cscfg_load_config_sets(etm4x_cfgs, etm4x_feats, &etm4x_mod_owner);
- /* if currently loaded matching devs ts_rate, still allow to load */
- if (err == -ENODEV)
err = 0;
- return err;
+}
+void etm4_cscfg_unload_builtin_cfg(void) +{
- cscfg_unload_config_sets(&etm4x_mod_owner);
+} diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.h b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h index 32dab34c1dac..dd69a8ef522d 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-cfg.h +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h @@ -13,18 +13,200 @@ /* resource IDs */ +/*
- 12 bit resource ID:
- 3:0 = resource type in use.
- 11:4 = additional resource specific information.
- */
#define ETM4_CFG_RES_CTR 0x001 #define ETM4_CFG_RES_CMP 0x002 -#define ETM4_CFG_RES_CMP_PAIR0 0x003 -#define ETM4_CFG_RES_CMP_PAIR1 0x004 -#define ETM4_CFG_RES_SEL 0x005 -#define ETM4_CFG_RES_SEL_PAIR0 0x006 -#define ETM4_CFG_RES_SEL_PAIR1 0x007 -#define ETM4_CFG_RES_SEQ 0x008 -#define ETM4_CFG_RES_TS 0x009 +#define ETM4_CFG_RES_SEL 0x003 +#define ETM4_CFG_RES_SEQ 0x004 +#define ETM4_CFG_RES_TS 0x005 +#define ETM4_CFG_RES_BITCTRL 0x006 +#define ETM4_CFG_RES_CID_CMP 0x007 +#define ETM4_CFG_RES_VID_CMP 0x008 #define ETM4_CFG_RES_MASK 0x00F +/* additional bits to supplement _CFG_RES_CTR */ +#define ETM4_CTR_VAL 0x010 +#define ETM4_CTR_RLD 0x020 +#define ETM4_CTR_CTRL 0x040
+/* additional bits for address comparators _CFG_RES_CMP */ +#define ETM4_CMP_PAIR0 0x010 +#define ETM4_CMP_PAIR1 0x020 +#define ETM4_CMP_VAL 0x040 +#define ETM4_CMP_CTL 0x080
+/* additional bits for resource selectors _CFG_RES_SEL */ +#define ETM4_SEL_PAIR0 0x010 +#define ETM4_SEL_PAIR1 0x020
+/* addtional bits for sequencer _CFG_RES_SEQ */ +#define ETM4_SEQ_STATE_R 0x010 +#define ETM4_SEQ_RESET_R 0x020
+/* additional bits to supplement _CFG_RES_BITCTRL */ +#define ETM4_BITCTRL_BRANCH_BROADCAST 0x010 +#define ETM4_BITCTRL_CYCLE_COUNT 0x020 +#define ETM4_BITCTRL_CTXTID 0x040 +#define ETM4_BITCTRL_VMID 0x080 +#define ETM4_BITCTRL_RETSTACK 0x100
+/* error value when calculating resource register offset (max offset = 0xFFC) */ +#define ETM4_RES_OFFSET_ERR 0xFFF
+/* skip value if a bit control that is resolved later */ +#define ETM4_RES_OFFSET_SKIP 0xFFE
+/**
- Masks to indicate resource usage.
- @selectors: The resource selector regs - max 32 off
- @comparators: Comparators - address (16 max), context ID (8 max), VMID (8 max).
- @misc:- bitselected features, sequencer etc.
- */
+struct etm4_cfg_resources {
- u32 selectors;
- u16 addr_cmp;
- u8 cid_cmp;
- u8 vmid_cmp;
- u8 counters;
- union {
u32 misc;
struct {
u32 cycle_cnt:1;
u32 branch_broadcast:1;
u32 ctxt_id:1;
u32 vm_id:1;
u32 sequencer:1;
u32 return_stack:1;
u32 timestamp:1;
u32 ts_rate:1;
};
- };
+};
+/* structure to hold implemented & used resources for the coresight device */ +struct cscfg_res_impl_used {
- struct etm4_cfg_resources impl;
- struct etm4_cfg_resources used;
+};
+/* resource mask tests */ +/* check implmented - ensure that all bits in @req exist in @impl */ +static inline bool etm4_cfg_check_impl(struct etm4_cfg_resources *impl,
struct etm4_cfg_resources *req)
+{
- /* invert impl then and req - anything set is outside impl mask */
- if ((~impl->selectors & req->selectors) ||
(~impl->addr_cmp & req->addr_cmp) ||
(~impl->cid_cmp & req->cid_cmp) ||
(~impl->vmid_cmp & req->vmid_cmp) ||
(~impl->counters & req->counters) ||
(~impl->misc & req->misc))
return false;
- return true;
+}
+/* check @req not @inuse, & set @inuse if free (assumes @req passed the impl check) */ +static inline bool etm4_cfg_check_set_inuse(struct etm4_cfg_resources *inuse,
struct etm4_cfg_resources *req)
+{
- /* first check for hits between inuse and requested bits */
- if ((inuse->selectors & req->selectors) ||
(inuse->addr_cmp & req->addr_cmp) ||
(inuse->cid_cmp & req->cid_cmp) ||
(inuse->vmid_cmp & req->vmid_cmp) ||
(inuse->counters & req->counters) ||
(inuse->misc & req->misc))
return false;
- /* set all requested bits as inuse */
- inuse->selectors |= req->selectors;
- inuse->addr_cmp |= req->addr_cmp;
- inuse->cid_cmp |= req->cid_cmp;
- inuse->vmid_cmp |= req->vmid_cmp;
- inuse->counters |= req->counters;
- inuse->misc |= req->misc;
- return true;
+}
+static inline void etm4_cfg_clear_inuse(struct etm4_cfg_resources *inuse,
struct etm4_cfg_resources *req)
+{
- /* clear requested bits from inuse */
- inuse->selectors &= ~req->selectors;
- inuse->addr_cmp &= ~req->addr_cmp;
- inuse->cid_cmp &= ~req->cid_cmp;
- inuse->vmid_cmp &= ~req->vmid_cmp;
- inuse->counters &= ~req->counters;
- inuse->misc &= ~req->misc;
+}
/* ETMv4 specific config functions */ int etm4_cscfg_register(struct coresight_device *csdev); +int etm4_cfg_find_unused_idx(unsigned long *impl, unsigned long *used, int size); +int etm4_cfg_find_unused_idx_pair(unsigned long *impl, unsigned long *used, int size); +void etm4_cfg_set_ts_rate(struct coresight_device *csdev, u32 ts_rate_val);
+/* register etm4x builtins with cscfg on module load */ +int etm4_cscfg_load_builtin_cfg(void); +void etm4_cscfg_unload_builtin_cfg(void);
+/*
- Set of functions to find an available resource from @res->impl, not already marked as used
- in @res->used.
- return index and mark as used in @res->used. return -ENOSPC if nothing available.
- */
+static inline int etm4_res_find_selector(struct cscfg_res_impl_used *res) +{
- unsigned long *impl, *used;
- if (!res->impl.selectors)
return -ENOSPC;
- impl = (unsigned long *)&res->impl.selectors;
- used = (unsigned long *)&res->used.selectors;
- return etm4_cfg_find_unused_idx(impl, used, ETM_MAX_RES_SEL);
+}
+static inline int etm4_res_find_counter(struct cscfg_res_impl_used *res) +{
- unsigned long *impl, *used;
- if (!res->impl.counters)
return -ENOSPC;
- impl = (unsigned long *)&res->impl.counters;
- used = (unsigned long *)&res->used.counters;
- return etm4_cfg_find_unused_idx(impl, used, ETMv4_MAX_CNTR);
+}
+static inline int etm4_res_find_addr_comparator(struct cscfg_res_impl_used *res) +{
- unsigned long *impl, *used;
- if (!res->impl.addr_cmp)
return -ENOSPC;
- impl = (unsigned long *)&res->impl.addr_cmp;
- used = (unsigned long *)&res->used.addr_cmp;
- return etm4_cfg_find_unused_idx(impl, used, ETM_MAX_SINGLE_ADDR_CMP);
+}
+static inline int etm4_res_find_addr_comp_pair(struct cscfg_res_impl_used *res) +{
- unsigned long *impl, *used;
- if (!res->impl.addr_cmp)
return -ENOSPC;
- impl = (unsigned long *)&res->impl.addr_cmp;
- used = (unsigned long *)&res->used.addr_cmp;
- return etm4_cfg_find_unused_idx_pair(impl, used, ETM_MAX_SINGLE_ADDR_CMP);
+} #endif /* CORESIGHT_ETM4X_CFG_H */ diff --git a/include/linux/coresight.h b/include/linux/coresight.h index a348049ee08b..b513964b9305 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -223,6 +223,7 @@ struct coresight_sysfs_link {
- @feature_csdev_list: List of complex feature programming added to the device.
- @config_csdev_list: List of system configurations added to the device.
- @active_cscfg_ctxt: Context information for current active system configuration.
*/
- @cscfg_res_mask: Available device specific resources usable in features.
struct coresight_device { struct coresight_platform_data *pdata; @@ -248,6 +249,7 @@ struct coresight_device { struct list_head feature_csdev_list; struct list_head config_csdev_list; void *active_cscfg_ctxt;
- void *cscfg_res_mask;
}; /* -- 2.17.1
HI Mathieu,
On Fri, 28 May 2021 at 17:17, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Wed, May 12, 2021 at 10:17:50PM +0100, Mike Leach wrote:
Adds resource management to configuration and feature handling for ETM4 using the system configuration resource management API.
Allows specification of ETM4 resources when creating configurations and features.
Adds in checking and validation of resources used by features to prevent over allocation when multiple features used in a configuration.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-etm4x-cfg.c | 533 ++++++++++++++++++ .../hwtracing/coresight/coresight-etm4x-cfg.h | 196 ++++++- include/linux/coresight.h | 2 + 3 files changed, 724 insertions(+), 7 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c index d2ea903231b2..ba6d20b58a9a 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c @@ -28,6 +28,11 @@ } \ }
+/* check for a match to ts rate */ +static bool etm4_cfg_feat_is_ts_rate(const struct cscfg_feature_desc *feat_desc);
+#define TS_RATE_REG_VAL_IDX 0
/**
- etm4_cfg_map_reg_offset - validate and map the register offset into a
location in the driver config struct.
@@ -128,6 +133,66 @@ static int etm4_cfg_map_reg_offset(struct etmv4_drvdata *drvdata, return err; }
+static void etm4_cfg_dump_res_mask(const char *name, struct etm4_cfg_resources *res) +{
pr_debug("Mask %s\n", name);
pr_debug("selectors %08x; addr_cmp %04x\n", res->selectors, res->addr_cmp);
pr_debug("cid_cmp %02x; vmid_cmp %02x; counters %02x\n", res->cid_cmp,
res->vmid_cmp, res->counters);
pr_debug("misc bits %08x\n", res->misc);
+}
+/*
- generate an address offset from a resource type and index
- Bit selected resources will return a ETM4_RES_OFFSET_SKIP value
- as these require special handling on enable / disable.
- */
+static u32 etm4_cfg_get_res_offset(u16 res_type, u32 res_idx) +{
u32 offset = ETM4_RES_OFFSET_ERR;
switch (res_type & ETM4_CFG_RES_MASK) {
case ETM4_CFG_RES_CTR:
if (res_type & ETM4_CTR_VAL)
offset = TRCCNTVRn(res_idx);
else if (res_type & ETM4_CTR_RLD)
offset = TRCCNTRLDVRn(res_idx);
else if (res_type & ETM4_CTR_CTRL)
offset = TRCCNTCTLRn(res_idx);
break;
case ETM4_CFG_RES_CMP:
if (res_type & ETM4_CMP_VAL)
offset = TRCACVRn(res_idx);
else if (res_type & ETM4_CMP_CTL)
offset = TRCACATRn(res_idx);
break;
case ETM4_CFG_RES_SEL:
offset = TRCRSCTLRn(res_idx);
break;
case ETM4_CFG_RES_SEQ:
if (res_type & ETM4_SEQ_STATE_R)
offset = TRCSEQEVRn(res_idx);
else if (res_type & ETM4_SEQ_RESET_R)
offset = TRCSEQRSTEVR;
break;
case ETM4_CFG_RES_CID_CMP:
offset = TRCCIDCVRn(res_idx);
break;
case ETM4_CFG_RES_VID_CMP:
offset = TRCVMIDCVRn(res_idx);
break;
/* these two have dedicated enable functions, no address needed */
case ETM4_CFG_RES_BITCTRL:
fallthrough;
OK
case ETM4_CFG_RES_TS:
offset = ETM4_RES_OFFSET_SKIP;
break;
}
return offset;
+}
/**
- etm4_cfg_load_feature - load a feature into a device instance.
@@ -163,11 +228,349 @@ static int etm4_cfg_load_feature(struct coresight_device *csdev, /* process the register descriptions */ for (i = 0; i < feat_csdev->nr_regs && !err; i++) { offset = feat_desc->regs_desc[i].offset;
/* resource needs conversion to a register access value */
if (feat_desc->regs_desc[i].type & CS_CFG_REG_TYPE_RESOURCE) {
offset = etm4_cfg_get_res_offset(feat_desc->regs_desc[i].hw_info,
offset);
if (offset == ETM4_RES_OFFSET_ERR) {
err = -ENODEV;
break;
} else if (offset == ETM4_RES_OFFSET_SKIP)
continue;
} err = etm4_cfg_map_reg_offset(drvdata, &feat_csdev->regs_csdev[i], offset); } return err;
}
+/*
- ts rate - set a counter to emit timestamp requests at a set interval.
- if we have sufficient resources then we use a counter and resource
- selector to achieve this.
- However, if not then do the best possible - which prevents the perf
- event timestamp request from failing if any configuration selection
- is using resources. e.g. when profiling, timestamps do not really matter.
To me it seems like we are adding too much intelligence, but we can have this conversation at a later time.
As noted below - this tries to balance the background perf resource requirements with the explicit user requirements.
- */
+void etm4_cfg_set_ts_rate(struct coresight_device *csdev, u32 ts_rate_val) +{
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
struct etmv4_config *drvcfg = &drvdata->config;
struct cscfg_res_impl_used *res_impl_used;
int counter_idx, res_sel_idx;
u32 tsctlr_val = 0;
res_impl_used = (struct cscfg_res_impl_used *)csdev->cscfg_res_mask;
/* look for resources */
counter_idx = etm4_res_find_counter(res_impl_used);
res_sel_idx = etm4_res_find_selector(res_impl_used);
if (counter_idx >= 0 && res_sel_idx >= 0) {
/* counter and selector - can set up ts rate normally */
/*
* counter @ 1 and reload @ rate supplied -
* immediate timestamp then every rate
*/
drvcfg->cntr_val[counter_idx] = 1;
drvcfg->cntrldvr[counter_idx] = ts_rate_val;
/*
* counter ctrl - bit 16: 1 for reload self,
* bit 7: 0 single event,
* bit 6:0 res sel 1 - true
*/
drvcfg->cntr_ctrl[counter_idx] = 0x1 << 16 | 0x1;
/*
* set up resource selector for the counter.
* bits 19:16 - group 0b0010 counter
* bits 15:0 - bit select for counter idx
*/
drvcfg->res_ctrl[res_sel_idx] = (0x2 << 16) | (0x1 << counter_idx);
/* single selector bit 7 == 0, bit 6:0 - selector index */
tsctlr_val = res_sel_idx;
} else if (ts_rate_val == 1) {
/*
* perf always tries to use a min value -
* emulate by setting the ts event to true
*/
/* single selector bit 7 == 0, bit 6:0 - selector 1 - always true */
tsctlr_val = 0x1;
}
/* set the configr reg to enable TS, and the ts control reg */
drvcfg->ts_ctrl = tsctlr_val;
drvcfg->cfg |= BIT(11);
+}
+/*
- on enable a feature - called after generic routine has programmed other registers.
- handle bit selects and custom elements
- */
+static int etm4_cfg_on_enable_feat(struct cscfg_feature_csdev *feat_csdev) +{
int err = 0;
struct etm4_cfg_resources *res_feat;
struct device *dev = feat_csdev->csdev->dev.parent;
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
struct etmv4_config *drvcfg = &drvdata->config;
u32 ts_rate_val;
/*
* look for the bit selected resources in this feature and set driver
* values to be programmed when enabling hardware.
*/
res_feat = (struct etm4_cfg_resources *)feat_csdev->res_used;
/* if none of the bit selected resources in use, exit early */
if (!res_feat->misc)
return 0;
/* otherwise check each and set as required */
if (res_feat->ctxt_id)
drvcfg->cfg |= BIT(6);
if (res_feat->vm_id)
drvcfg->cfg |= BIT(7);
/* return stack is bit 12 in config register */
if (res_feat->return_stack)
drvcfg->cfg |= BIT(12);
/* branch broadcast - feature using this must program the bbctlr */
if (res_feat->branch_broadcast)
drvcfg->cfg |= BIT(3);
/* cycle count */
if (res_feat->cycle_cnt) {
drvcfg->cfg |= BIT(4);
/* TRM: Must program this for cycacc to work - ensure mun permitted */
if (drvcfg->ccctlr < drvdata->ccitmin)
drvcfg->ccctlr = drvdata->ccitmin;
}
/*
* timestamps - if not ts-rate just set to on, otherwise
* set using reload counter according to requested rate
*/
if (res_feat->timestamp) {
/* the current feature is the ts-rate feature */
if (res_feat->ts_rate) {
ts_rate_val = feat_csdev->regs_csdev[TS_RATE_REG_VAL_IDX].reg_desc.val32;
etm4_cfg_set_ts_rate(feat_csdev->csdev, ts_rate_val);
I would like to see a better way to do this. Setting the timestamp rate should be treated like any other feature. If the current framework doesn't allow for it then it should be extended.
see the comment below.
} else
drvcfg->cfg |= BIT(11);
}
return err;
+}
+/* set the overall available resource masks for the device */ +static int etm4_cfg_set_res_mask(struct coresight_device *csdev) +{
struct device *dev = csdev->dev.parent;
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
struct etm4_cfg_resources *res;
struct cscfg_res_impl_used *res_impl_used;
res_impl_used = devm_kzalloc(dev, sizeof(*res_impl_used), GFP_KERNEL);
if (!res_impl_used)
return -ENOMEM;
res = &res_impl_used->impl;
/* selectors */
if (drvdata->nr_resource)
res->selectors = GENMASK((drvdata->nr_resource * 2) - 1, 0);
/* comparators */
if (drvdata->nr_addr_cmp)
res->addr_cmp = GENMASK(drvdata->nr_addr_cmp - 1, 0);
if (drvdata->numvmidc)
res->vmid_cmp = GENMASK(drvdata->numvmidc - 1, 0);
if (drvdata->numcidc)
res->cid_cmp = GENMASK(drvdata->numcidc - 1, 0);
/* misc resources */
if (drvdata->nr_cntr)
res->counters = GENMASK(drvdata->nr_cntr - 1, 0);
if (drvdata->trccci)
res->cycle_cnt = 1;
if (drvdata->trcbb)
res->branch_broadcast = 1;
if (drvdata->ctxid_size)
res->ctxt_id = 1;
if (drvdata->vmid_size)
res->vm_id = 1;
if (drvdata->nrseqstate)
res->sequencer = 1;
if (drvdata->retstack)
res->return_stack = 1;
if (drvdata->ts_size) {
res->timestamp = 1;
if (drvdata->nr_cntr && drvdata->nr_resource)
res->ts_rate = 1;
}
etm4_cfg_dump_res_mask("device impl resources", &res_impl_used->impl);
csdev->cscfg_res_mask = res_impl_used;
return 0;
+}
+/*
- reads a descriptor and updates the resource mask structure
- checks resource indexes are valid.
- */
+static int etm4_cfg_update_res_from_desc(const struct cscfg_feature_desc *feat_desc,
struct etm4_cfg_resources *res)
+{
struct cscfg_regval_desc *regs_desc = &feat_desc->regs_desc[0];
u32 res_idx, hw_info;
int i;
for (i = 0; i < feat_desc->nr_regs; i++) {
if (regs_desc[i].type & CS_CFG_REG_TYPE_RESOURCE) {
res_idx = regs_desc[i].offset;
hw_info = regs_desc[i].hw_info;
switch (hw_info & ETM4_CFG_RES_MASK) {
case ETM4_CFG_RES_CTR:
if (res_idx >= ETMv4_MAX_CNTR)
goto invalid_resource_err;
res->counters |= BIT(res_idx);
break;
case ETM4_CFG_RES_CMP:
if (res_idx >= ETM_MAX_SINGLE_ADDR_CMP)
goto invalid_resource_err;
res->addr_cmp |= BIT(res_idx);
break;
case ETM4_CFG_RES_SEL:
if (res_idx >= ETM_MAX_RES_SEL)
goto invalid_resource_err;
res->selectors |= BIT(res_idx);
break;
case ETM4_CFG_RES_SEQ:
res->sequencer = 1;
break;
case ETM4_CFG_RES_TS:
res->timestamp = 1;
if (etm4_cfg_feat_is_ts_rate(feat_desc))
res->ts_rate = 1;
break;
case ETM4_CFG_RES_BITCTRL:
if (hw_info & ETM4_BITCTRL_BRANCH_BROADCAST)
res->branch_broadcast = 1;
if (hw_info & ETM4_BITCTRL_CYCLE_COUNT)
res->cycle_cnt = 1;
if (hw_info & ETM4_BITCTRL_CTXTID)
res->ctxt_id = 1;
if (hw_info & ETM4_BITCTRL_VMID)
res->vm_id = 1;
if (hw_info & ETM4_BITCTRL_RETSTACK)
res->return_stack = 1;
break;
case ETM4_CFG_RES_CID_CMP:
if (res_idx >= ETMv4_MAX_CTXID_CMP)
goto invalid_resource_err;
res->cid_cmp |= BIT(res_idx);
break;
case ETM4_CFG_RES_VID_CMP:
if (res_idx >= ETM_MAX_VMID_CMP)
goto invalid_resource_err;
res->vmid_cmp |= BIT(res_idx);
break;
}
}
}
return 0;
+invalid_resource_err:
pr_err("Error: Invalid resource values in feature %s\n", feat_desc->name);
return -EINVAL;
+} +/*
- Check that the device contains the minimum resources required to support the
- described @feat_desc. Return -ENODEV if missing required resources.
- */
+static int etm4_cfg_check_feat_res(struct coresight_device *csdev,
struct cscfg_feature_desc *feat_desc)
+{
struct etm4_cfg_resources req_res;
struct cscfg_res_impl_used *dev_res;
int err;
/* create a resource mask from descriptor and validate */
memset(&req_res, 0, sizeof(req_res));
err = etm4_cfg_update_res_from_desc(feat_desc, &req_res);
etm4_cfg_dump_res_mask("check_feat_res", &req_res);
if (!err) {
dev_res = (struct cscfg_res_impl_used *)csdev->cscfg_res_mask;
if (!etm4_cfg_check_impl(&dev_res->impl, &req_res))
return -ENODEV;
}
return err;
+}
+/*
- Allocate resource requirements for the feature before
- it is programmed into the system. Ensures that two or more features in a
- configuration do not try to use the same resources on the device.
- At this point we use the absolute programmed resources - we do not attempt
- to find alternate available resources. (e.g. if 2 features use selector 3,
- fail the 2nd feature - do not look for an alternative free selector).
- */
+static int etm4_cfg_alloc_feat_res(struct cscfg_feature_csdev *feat_csdev) +{
struct coresight_device *csdev = feat_csdev->csdev;
struct device *dev = csdev->dev.parent;
struct etm4_cfg_resources *res_feat, *res_inuse;
int err = 0;
/* one off initialisation of resources required for this feature */
if (!feat_csdev->res_used) {
res_feat = devm_kzalloc(dev, sizeof(*res_feat), GFP_KERNEL);
if (!res_feat)
return -ENOMEM;
err = etm4_cfg_update_res_from_desc(feat_csdev->feat_desc, res_feat);
if (err)
return err;
feat_csdev->res_used = res_feat;
} else
res_feat = (struct etm4_cfg_resources *)feat_csdev->res_used;
/* check that the device resources reqiured are not in use */
res_inuse = &((struct cscfg_res_impl_used *)csdev->cscfg_res_mask)->used;
if (!etm4_cfg_check_set_inuse(res_inuse, res_feat))
err = -ENOSPC;
return err;
+}
+static void etm4_cfg_clear_feat_res(struct cscfg_feature_csdev *feat_csdev) +{
struct coresight_device *csdev = feat_csdev->csdev;
struct etm4_cfg_resources *res_feat, *res_inuse;
res_feat = (struct etm4_cfg_resources *)feat_csdev->res_used;
res_inuse = &((struct cscfg_res_impl_used *)csdev->cscfg_res_mask)->used;
etm4_cfg_clear_inuse(res_inuse, res_feat);
+}
/* match information when loading configurations */ #define CS_CFG_ETM4_MATCH_FLAGS (CS_CFG_MATCH_CLASS_SRC_ALL | \ CS_CFG_MATCH_CLASS_SRC_ETM4) @@ -175,8 +578,138 @@ static int etm4_cfg_load_feature(struct coresight_device *csdev, int etm4_cscfg_register(struct coresight_device *csdev) { struct cscfg_csdev_feat_ops ops;
int err = 0;
err = etm4_cfg_set_res_mask(csdev);
if (err)
return err; ops.load_feat = &etm4_cfg_load_feature;
ops.check_feat_res = &etm4_cfg_check_feat_res;
ops.alloc_feat_res = &etm4_cfg_alloc_feat_res;
ops.clear_feat_res = &etm4_cfg_clear_feat_res;
ops.set_on_enable = &etm4_cfg_on_enable_feat;
ops.clear_on_disable = 0; return cscfg_register_csdev(csdev, CS_CFG_ETM4_MATCH_FLAGS, &ops);
}
+/*
- find first available bit in implemented mask @impl, that is not set in @used mask.
- set bit in @used and return. Return -ENOSPC if no available bits.
- */
+int etm4_cfg_find_unused_idx(unsigned long *impl, unsigned long *used, int size) +{
unsigned long end_idx, unused_idx;
end_idx = find_first_zero_bit(impl, size);
unused_idx = find_first_zero_bit(used, size);
if (unused_idx < end_idx) {
*used |= BIT(unused_idx);
return (int)unused_idx;
}
return -ENOSPC;
+}
+/*
- find first available pair of bits in implemented mask @impl, that are not set in
- @used mask. First bit of pair will always be an even index.
- Set bits in @used and return. Return -ENOSPC if no available bits.
- */
+int etm4_cfg_find_unused_idx_pair(unsigned long *impl, unsigned long *used, int size) +{
unsigned long end_idx, first_unused_idx, next_unused_idx;
end_idx = find_first_zero_bit(impl, size);
first_unused_idx = find_first_zero_bit(used, size);
/*
* even indexes are the 1st in a pair, look through the comparators
* till a pair found or we are at the end of the list.
*/
while (first_unused_idx < end_idx) {
/* first is an even number, if the next is free we have a pair */
if (!(first_unused_idx % 2)) {
next_unused_idx = find_next_zero_bit(used, size, first_unused_idx);
if (next_unused_idx == (first_unused_idx + 1)) {
*used |= BIT(first_unused_idx);
*used |= BIT(next_unused_idx);
return (int)first_unused_idx;
}
first_unused_idx = next_unused_idx;
} else
first_unused_idx = find_next_zero_bit(used, size, first_unused_idx);
}
return -ENOSPC;
+}
+/* built in timestamp rate for etm4x */ +static struct cscfg_parameter_desc ts_rate_param[] = {
{
.name = "ts_rate_cycles",
.value = 100.
},
+};
+static struct cscfg_regval_desc ts_rate_regs[] = {
{
.type = CS_CFG_REG_TYPE_RESOURCE | CS_CFG_REG_TYPE_VAL_PARAM,
.offset = 0,
.hw_info = ETM4_CFG_RES_TS,
.param_idx = 0,
},
+};
+static struct cscfg_feature_desc ts_rate_etm4x = {
.name = "timestamp-rate",
.description = "Enable timestamps and set rate they appear in the trace.\n"
"Rate value is number of cycles between timestamp requests. Min value 1.\n",
.match_flags = CS_CFG_MATCH_CLASS_SRC_ETM4,
.nr_params = ARRAY_SIZE(ts_rate_param),
.params_desc = ts_rate_param,
.nr_regs = ARRAY_SIZE(ts_rate_regs),
.regs_desc = ts_rate_regs,
+};
I vote for leaving the timestamp rate feature out of this set. It is introducing a fair amount of complexity to a patchset that is already complex. When we move forward with it, it should be in its own file like we did for autoFDO.
ts rate was introduced as a way of managing the built-in requirement in perf enable that it would insist on a counted timestamp when in cpu wide mode for correlation, or fail to run. To manage these expectations with that of any configuration competing for the resource, ts_rate is treated in a way that it will degrade gracefully it all the resources are not available. This would mean that the perf enable event could try to set it, but configurations would take priority.
Looking at this now, it may be better to remove this as a feature as you suggest, and manage the perf requirements as part of the normal resource managment - with the caveat that if a user selects a configuration, then perf will not refuse to start the event if a counted timestamp cannot be implemented. This will avoid unexplained unavailable resource errors were the perf event takes resources in the background. (this is in fact the behaviour of the baseline set - though in this case a config simply overrides perfs counter usage).
Thanks
Mike
I am out of time for this set and as such will stop here. We can tackle the rest in future revisions.
Thanks, Mathieu
+static struct cscfg_feature_desc *etm4x_feats[] = {
&ts_rate_etm4x,
NULL,
+};
+static struct cscfg_config_desc *etm4x_cfgs[] = {
NULL,
+};
+static struct cscfg_load_owner_info etm4x_mod_owner = {
.type = CSCFG_OWNER_MODULE,
.owner_handle = THIS_MODULE,
+};
+/*
- check if incoming feature is ts-rate
- */
+static bool etm4_cfg_feat_is_ts_rate(const struct cscfg_feature_desc *feat_desc) +{
if (!strcmp(feat_desc->name, ts_rate_etm4x.name))
return true;
return false;
+}
+/* load the etm4 builtin ts_rate feature into the system */ +int etm4_cscfg_load_builtin_cfg(void) +{
int err;
err = cscfg_load_config_sets(etm4x_cfgs, etm4x_feats, &etm4x_mod_owner);
/* if currently loaded matching devs ts_rate, still allow to load */
if (err == -ENODEV)
err = 0;
return err;
+}
+void etm4_cscfg_unload_builtin_cfg(void) +{
cscfg_unload_config_sets(&etm4x_mod_owner);
+} diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.h b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h index 32dab34c1dac..dd69a8ef522d 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-cfg.h +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h @@ -13,18 +13,200 @@
/* resource IDs */
+/*
- 12 bit resource ID:
- 3:0 = resource type in use.
- 11:4 = additional resource specific information.
- */
#define ETM4_CFG_RES_CTR 0x001 #define ETM4_CFG_RES_CMP 0x002 -#define ETM4_CFG_RES_CMP_PAIR0 0x003 -#define ETM4_CFG_RES_CMP_PAIR1 0x004 -#define ETM4_CFG_RES_SEL 0x005 -#define ETM4_CFG_RES_SEL_PAIR0 0x006 -#define ETM4_CFG_RES_SEL_PAIR1 0x007 -#define ETM4_CFG_RES_SEQ 0x008 -#define ETM4_CFG_RES_TS 0x009 +#define ETM4_CFG_RES_SEL 0x003 +#define ETM4_CFG_RES_SEQ 0x004 +#define ETM4_CFG_RES_TS 0x005 +#define ETM4_CFG_RES_BITCTRL 0x006 +#define ETM4_CFG_RES_CID_CMP 0x007 +#define ETM4_CFG_RES_VID_CMP 0x008 #define ETM4_CFG_RES_MASK 0x00F
+/* additional bits to supplement _CFG_RES_CTR */ +#define ETM4_CTR_VAL 0x010 +#define ETM4_CTR_RLD 0x020 +#define ETM4_CTR_CTRL 0x040
+/* additional bits for address comparators _CFG_RES_CMP */ +#define ETM4_CMP_PAIR0 0x010 +#define ETM4_CMP_PAIR1 0x020 +#define ETM4_CMP_VAL 0x040 +#define ETM4_CMP_CTL 0x080
+/* additional bits for resource selectors _CFG_RES_SEL */ +#define ETM4_SEL_PAIR0 0x010 +#define ETM4_SEL_PAIR1 0x020
+/* addtional bits for sequencer _CFG_RES_SEQ */ +#define ETM4_SEQ_STATE_R 0x010 +#define ETM4_SEQ_RESET_R 0x020
+/* additional bits to supplement _CFG_RES_BITCTRL */ +#define ETM4_BITCTRL_BRANCH_BROADCAST 0x010 +#define ETM4_BITCTRL_CYCLE_COUNT 0x020 +#define ETM4_BITCTRL_CTXTID 0x040 +#define ETM4_BITCTRL_VMID 0x080 +#define ETM4_BITCTRL_RETSTACK 0x100
+/* error value when calculating resource register offset (max offset = 0xFFC) */ +#define ETM4_RES_OFFSET_ERR 0xFFF
+/* skip value if a bit control that is resolved later */ +#define ETM4_RES_OFFSET_SKIP 0xFFE
+/**
- Masks to indicate resource usage.
- @selectors: The resource selector regs - max 32 off
- @comparators: Comparators - address (16 max), context ID (8 max), VMID (8 max).
- @misc:- bitselected features, sequencer etc.
- */
+struct etm4_cfg_resources {
u32 selectors;
u16 addr_cmp;
u8 cid_cmp;
u8 vmid_cmp;
u8 counters;
union {
u32 misc;
struct {
u32 cycle_cnt:1;
u32 branch_broadcast:1;
u32 ctxt_id:1;
u32 vm_id:1;
u32 sequencer:1;
u32 return_stack:1;
u32 timestamp:1;
u32 ts_rate:1;
};
};
+};
+/* structure to hold implemented & used resources for the coresight device */ +struct cscfg_res_impl_used {
struct etm4_cfg_resources impl;
struct etm4_cfg_resources used;
+};
+/* resource mask tests */ +/* check implmented - ensure that all bits in @req exist in @impl */ +static inline bool etm4_cfg_check_impl(struct etm4_cfg_resources *impl,
struct etm4_cfg_resources *req)
+{
/* invert impl then and req - anything set is outside impl mask */
if ((~impl->selectors & req->selectors) ||
(~impl->addr_cmp & req->addr_cmp) ||
(~impl->cid_cmp & req->cid_cmp) ||
(~impl->vmid_cmp & req->vmid_cmp) ||
(~impl->counters & req->counters) ||
(~impl->misc & req->misc))
return false;
return true;
+}
+/* check @req not @inuse, & set @inuse if free (assumes @req passed the impl check) */ +static inline bool etm4_cfg_check_set_inuse(struct etm4_cfg_resources *inuse,
struct etm4_cfg_resources *req)
+{
/* first check for hits between inuse and requested bits */
if ((inuse->selectors & req->selectors) ||
(inuse->addr_cmp & req->addr_cmp) ||
(inuse->cid_cmp & req->cid_cmp) ||
(inuse->vmid_cmp & req->vmid_cmp) ||
(inuse->counters & req->counters) ||
(inuse->misc & req->misc))
return false;
/* set all requested bits as inuse */
inuse->selectors |= req->selectors;
inuse->addr_cmp |= req->addr_cmp;
inuse->cid_cmp |= req->cid_cmp;
inuse->vmid_cmp |= req->vmid_cmp;
inuse->counters |= req->counters;
inuse->misc |= req->misc;
return true;
+}
+static inline void etm4_cfg_clear_inuse(struct etm4_cfg_resources *inuse,
struct etm4_cfg_resources *req)
+{
/* clear requested bits from inuse */
inuse->selectors &= ~req->selectors;
inuse->addr_cmp &= ~req->addr_cmp;
inuse->cid_cmp &= ~req->cid_cmp;
inuse->vmid_cmp &= ~req->vmid_cmp;
inuse->counters &= ~req->counters;
inuse->misc &= ~req->misc;
+}
/* ETMv4 specific config functions */ int etm4_cscfg_register(struct coresight_device *csdev); +int etm4_cfg_find_unused_idx(unsigned long *impl, unsigned long *used, int size); +int etm4_cfg_find_unused_idx_pair(unsigned long *impl, unsigned long *used, int size); +void etm4_cfg_set_ts_rate(struct coresight_device *csdev, u32 ts_rate_val);
+/* register etm4x builtins with cscfg on module load */ +int etm4_cscfg_load_builtin_cfg(void); +void etm4_cscfg_unload_builtin_cfg(void);
+/*
- Set of functions to find an available resource from @res->impl, not already marked as used
- in @res->used.
- return index and mark as used in @res->used. return -ENOSPC if nothing available.
- */
+static inline int etm4_res_find_selector(struct cscfg_res_impl_used *res) +{
unsigned long *impl, *used;
if (!res->impl.selectors)
return -ENOSPC;
impl = (unsigned long *)&res->impl.selectors;
used = (unsigned long *)&res->used.selectors;
return etm4_cfg_find_unused_idx(impl, used, ETM_MAX_RES_SEL);
+}
+static inline int etm4_res_find_counter(struct cscfg_res_impl_used *res) +{
unsigned long *impl, *used;
if (!res->impl.counters)
return -ENOSPC;
impl = (unsigned long *)&res->impl.counters;
used = (unsigned long *)&res->used.counters;
return etm4_cfg_find_unused_idx(impl, used, ETMv4_MAX_CNTR);
+}
+static inline int etm4_res_find_addr_comparator(struct cscfg_res_impl_used *res) +{
unsigned long *impl, *used;
if (!res->impl.addr_cmp)
return -ENOSPC;
impl = (unsigned long *)&res->impl.addr_cmp;
used = (unsigned long *)&res->used.addr_cmp;
return etm4_cfg_find_unused_idx(impl, used, ETM_MAX_SINGLE_ADDR_CMP);
+}
+static inline int etm4_res_find_addr_comp_pair(struct cscfg_res_impl_used *res) +{
unsigned long *impl, *used;
if (!res->impl.addr_cmp)
return -ENOSPC;
impl = (unsigned long *)&res->impl.addr_cmp;
used = (unsigned long *)&res->used.addr_cmp;
return etm4_cfg_find_unused_idx_pair(impl, used, ETM_MAX_SINGLE_ADDR_CMP);
+}
#endif /* CORESIGHT_ETM4X_CFG_H */ diff --git a/include/linux/coresight.h b/include/linux/coresight.h index a348049ee08b..b513964b9305 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -223,6 +223,7 @@ struct coresight_sysfs_link {
- @feature_csdev_list: List of complex feature programming added to the device.
- @config_csdev_list: List of system configurations added to the device.
- @active_cscfg_ctxt: Context information for current active system configuration.
*/
- @cscfg_res_mask: Available device specific resources usable in features.
struct coresight_device { struct coresight_platform_data *pdata; @@ -248,6 +249,7 @@ struct coresight_device { struct list_head feature_csdev_list; struct list_head config_csdev_list; void *active_cscfg_ctxt;
void *cscfg_res_mask;
};
/*
2.17.1
Modifies current resource handling when parsing the perf event options to ensure that these are correctly allocated according to availability to ensure that configuration selection and other perf command line options can operated correctly together.
Signed-off-by: Mike Leach mike.leach@linaro.org --- .../coresight/coresight-etm4x-core.c | 245 ++++++------------ 1 file changed, 82 insertions(+), 163 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index 2637096c4621..4cc207d42976 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -58,7 +58,8 @@ MODULE_PARM_DESC(pm_save_enable, static struct etmv4_drvdata *etmdrvdata[NR_CPUS]; static void etm4_set_default_config(struct etmv4_config *config); static int etm4_set_event_filters(struct etmv4_drvdata *drvdata, - struct perf_event *event); + struct perf_event *event, + struct coresight_device *csdev); static u64 etm4_get_access_type(struct etmv4_config *config);
static enum cpuhp_state hp_online; @@ -478,91 +479,6 @@ static void etm4_enable_hw_smp_call(void *info) arg->rc = etm4_enable_hw(arg->drvdata); }
-/* - * The goal of function etm4_config_timestamp_event() is to configure a - * counter that will tell the tracer to emit a timestamp packet when it - * reaches zero. This is done in order to get a more fine grained idea - * of when instructions are executed so that they can be correlated - * with execution on other CPUs. - * - * To do this the counter itself is configured to self reload and - * TRCRSCTLR1 (always true) used to get the counter to decrement. From - * there a resource selector is configured with the counter and the - * timestamp control register to use the resource selector to trigger the - * event that will insert a timestamp packet in the stream. - */ -static int etm4_config_timestamp_event(struct etmv4_drvdata *drvdata) -{ - int ctridx, ret = -EINVAL; - int counter, rselector; - u32 val = 0; - struct etmv4_config *config = &drvdata->config; - - /* No point in trying if we don't have at least one counter */ - if (!drvdata->nr_cntr) - goto out; - - /* Find a counter that hasn't been initialised */ - for (ctridx = 0; ctridx < drvdata->nr_cntr; ctridx++) - if (config->cntr_val[ctridx] == 0) - break; - - /* All the counters have been configured already, bail out */ - if (ctridx == drvdata->nr_cntr) { - pr_debug("%s: no available counter found\n", __func__); - ret = -ENOSPC; - goto out; - } - - /* - * Searching for an available resource selector to use, starting at - * '2' since every implementation has at least 2 resource selector. - * ETMIDR4 gives the number of resource selector _pairs_, - * hence multiply by 2. - */ - for (rselector = 2; rselector < drvdata->nr_resource * 2; rselector++) - if (!config->res_ctrl[rselector]) - break; - - if (rselector == drvdata->nr_resource * 2) { - pr_debug("%s: no available resource selector found\n", - __func__); - ret = -ENOSPC; - goto out; - } - - /* Remember what counter we used */ - counter = 1 << ctridx; - - /* - * Initialise original and reload counter value to the smallest - * possible value in order to get as much precision as we can. - */ - config->cntr_val[ctridx] = 1; - config->cntrldvr[ctridx] = 1; - - /* Set the trace counter control register */ - val = 0x1 << 16 | /* Bit 16, reload counter automatically */ - 0x0 << 7 | /* Select single resource selector */ - 0x1; /* Resource selector 1, i.e always true */ - - config->cntr_ctrl[ctridx] = val; - - val = 0x2 << 16 | /* Group 0b0010 - Counter and sequencers */ - counter << 0; /* Counter to use */ - - config->res_ctrl[rselector] = val; - - val = 0x0 << 7 | /* Select single resource selector */ - rselector; /* Resource selector */ - - config->ts_ctrl = val; - - ret = 0; -out: - return ret; -} - static int etm4_parse_event_config(struct coresight_device *csdev, struct perf_event *event) { @@ -570,6 +486,7 @@ static int etm4_parse_event_config(struct coresight_device *csdev, struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct etmv4_config *config = &drvdata->config; struct perf_event_attr *attr = &event->attr; + struct etm4_cfg_resources *res_inuse; unsigned long cfg_hash; int preset;
@@ -581,6 +498,10 @@ static int etm4_parse_event_config(struct coresight_device *csdev, /* Clear configuration from previous run */ memset(config, 0, sizeof(struct etmv4_config));
+ /* clear any resources still marked as inuse */ + res_inuse = &((struct cscfg_res_impl_used *)csdev->cscfg_res_mask)->used; + memset(res_inuse, 0, sizeof(struct etm4_cfg_resources)); + if (attr->exclude_kernel) config->mode = ETM_MODE_EXCL_KERN;
@@ -590,37 +511,51 @@ static int etm4_parse_event_config(struct coresight_device *csdev, /* Always start from the default config */ etm4_set_default_config(config);
+ /* + * First set any selected configuration and preset. + * + * This extracts the values of PMU_FORMAT_ATTR(configid) and PMU_FORMAT_ATTR(preset) + * in the perf attributes defined in coresight-etm-perf.c. + * configid uses bits 63:32 of attr->config2, preset uses bits 3:0 of attr->config. + * A zero configid means no configuration active, preset = 0 means no preset selected. + */ + if (attr->config2 & GENMASK_ULL(63, 32)) { + cfg_hash = (u32)(attr->config2 >> 32); + preset = attr->config & 0xF; + ret = cscfg_csdev_enable_active_config(csdev, cfg_hash, preset); + if (ret) + goto out; + } + + /* + * once any config is set, then we can add any perf options - + * using resource management to prevent clashes & not set items that + * are currently set. + */ + /* Configure filters specified on the perf cmd line, if any. */ - ret = etm4_set_event_filters(drvdata, event); + ret = etm4_set_event_filters(drvdata, event, csdev); if (ret) - goto out; + goto out_disable_cfg;
/* Go from generic option to ETMv4 specifics */ - if (attr->config & BIT(ETM_OPT_CYCACC)) { + if ((attr->config & BIT(ETM_OPT_CYCACC)) && !res_inuse->cycle_cnt) { config->cfg |= BIT(4); /* TRM: Must program this for cycacc to work */ config->ccctlr = ETM_CYC_THRESHOLD_DEFAULT; } - if (attr->config & BIT(ETM_OPT_TS)) { + if ((attr->config & BIT(ETM_OPT_TS)) && !res_inuse->ts_rate) { /* * Configure timestamps to be emitted at regular intervals in * order to correlate instructions executed on different CPUs - * (CPU-wide trace scenarios). + * (CPU-wide trace scenarios). We use the built in ts rate function + * to manage any resource conflicts. Request a rate of 1 to maximise + * freqency of timestamps. */ - ret = etm4_config_timestamp_event(drvdata); - - /* - * No need to go further if timestamp intervals can't - * be configured. - */ - if (ret) - goto out; - - /* bit[11], Global timestamp tracing bit */ - config->cfg |= BIT(11); + etm4_cfg_set_ts_rate(csdev, 1); }
- if (attr->config & BIT(ETM_OPT_CTXTID)) + if ((attr->config & BIT(ETM_OPT_CTXTID)) && !res_inuse->ctxt_id) /* bit[6], Context ID tracing bit */ config->cfg |= BIT(ETM4_CFG_BIT_CTXTID);
@@ -632,30 +567,20 @@ static int etm4_parse_event_config(struct coresight_device *csdev, if (attr->config & BIT(ETM_OPT_CTXTID2)) { if (!is_kernel_in_hyp_mode()) { ret = -EINVAL; - goto out; + goto out_disable_cfg; } config->cfg |= BIT(ETM4_CFG_BIT_VMID) | BIT(ETM4_CFG_BIT_VMID_OPT); }
/* return stack - enable if selected and supported */ - if ((attr->config & BIT(ETM_OPT_RETSTK)) && drvdata->retstack) + if ((attr->config & BIT(ETM_OPT_RETSTK)) && drvdata->retstack && !res_inuse->return_stack) /* bit[12], Return stack enable bit */ config->cfg |= BIT(12);
- /* - * Set any selected configuration and preset. - * - * This extracts the values of PMU_FORMAT_ATTR(configid) and PMU_FORMAT_ATTR(preset) - * in the perf attributes defined in coresight-etm-perf.c. - * configid uses bits 63:32 of attr->config2, preset uses bits 3:0 of attr->config. - * A zero configid means no configuration active, preset = 0 means no preset selected. - */ - if (attr->config2 & GENMASK_ULL(63, 32)) { - cfg_hash = (u32)(attr->config2 >> 32); - preset = attr->config & 0xF; - ret = cscfg_csdev_enable_active_config(csdev, cfg_hash, preset); - } + return 0;
+out_disable_cfg: + cscfg_csdev_disable_active_config(csdev); out: return ret; } @@ -871,6 +796,8 @@ static void etm4_disable_sysfs(struct coresight_device *csdev) spin_unlock(&drvdata->spinlock); cpus_read_unlock();
+ cscfg_csdev_disable_active_config(csdev); + dev_dbg(&csdev->dev, "ETM tracing disabled\n"); }
@@ -1375,52 +1302,37 @@ static void etm4_set_default(struct etmv4_config *config) etm4_set_default_filter(config); }
-static int etm4_get_next_comparator(struct etmv4_drvdata *drvdata, u32 type) +static int etm4_get_next_comparator_res(struct cscfg_res_impl_used *res, + u32 type) { - int nr_comparator, index = 0; - struct etmv4_config *config = &drvdata->config; - - /* - * nr_addr_cmp holds the number of comparator _pair_, so time 2 - * for the total number of comparators. - */ - nr_comparator = drvdata->nr_addr_cmp * 2; + switch (type) { + case ETM_ADDR_TYPE_RANGE: + return etm4_res_find_addr_comp_pair(res);
- /* Go through the tally of comparators looking for a free one. */ - while (index < nr_comparator) { - switch (type) { - case ETM_ADDR_TYPE_RANGE: - if (config->addr_type[index] == ETM_ADDR_TYPE_NONE && - config->addr_type[index + 1] == ETM_ADDR_TYPE_NONE) - return index; - - /* Address range comparators go in pairs */ - index += 2; - break; - case ETM_ADDR_TYPE_START: - case ETM_ADDR_TYPE_STOP: - if (config->addr_type[index] == ETM_ADDR_TYPE_NONE) - return index; - - /* Start/stop address can have odd indexes */ - index += 1; - break; - default: - return -EINVAL; - } + case ETM_ADDR_TYPE_START: + case ETM_ADDR_TYPE_STOP: + return etm4_res_find_addr_comparator(res); } - - /* If we are here all the comparators have been used. */ - return -ENOSPC; + return -EINVAL; }
+/* + * Called to set up perf event filters - after any selected configuration. + * This allows the perf command line filters to be used in combination with + * any selected configuration. + * + * Use the config resources to find available comparators - only set default + * filter if no previous configuration has set up filtering. + */ static int etm4_set_event_filters(struct etmv4_drvdata *drvdata, - struct perf_event *event) + struct perf_event *event, + struct coresight_device *csdev) { int i, comparator, ret = 0; u64 address; struct etmv4_config *config = &drvdata->config; struct etm_filters *filters = event->hw.addr_filters; + struct cscfg_res_impl_used *res = (struct cscfg_res_impl_used *)csdev->cscfg_res_mask;
if (!filters) goto default_filter; @@ -1440,7 +1352,7 @@ static int etm4_set_event_filters(struct etmv4_drvdata *drvdata, enum etm_addr_type type = filter->type;
/* See if a comparator is free. */ - comparator = etm4_get_next_comparator(drvdata, type); + comparator = etm4_get_next_comparator_res(res, type); if (comparator < 0) { ret = comparator; goto out; @@ -1457,10 +1369,8 @@ static int etm4_set_event_filters(struct etmv4_drvdata *drvdata, * in the started state */ config->vinst_ctrl |= BIT(9); - - /* No start-stop filtering for ViewInst */ - config->vissctlr = 0x0; break; + case ETM_ADDR_TYPE_START: case ETM_ADDR_TYPE_STOP: /* Get the right start or stop address */ @@ -1485,10 +1395,8 @@ static int etm4_set_event_filters(struct etmv4_drvdata *drvdata, */ if (filters->ssstatus) config->vinst_ctrl |= BIT(9); - - /* No include/exclude filtering for ViewInst */ - config->viiectlr = 0x0; break; + default: ret = -EINVAL; goto out; @@ -1497,9 +1405,10 @@ static int etm4_set_event_filters(struct etmv4_drvdata *drvdata,
goto out;
- default_filter: - etm4_set_default_filter(config); + /* if no filtering has been set by any other method - set default here */ + if (!config->viiectlr && !config->vissctlr) + etm4_set_default_filter(config);
out: return ret; @@ -2161,10 +2070,19 @@ static int __init etm4x_init(void) }
ret = platform_driver_register(&etm4_platform_driver); + if (ret) { + pr_err("Error registering etm4x platform driver\n"); + goto clear_amba; + } + + ret = etm4_cscfg_load_builtin_cfg(); if (!ret) return 0;
- pr_err("Error registering etm4x platform driver\n"); + pr_err("Error loading etm4x coresight configurations\n"); + platform_driver_unregister(&etm4_platform_driver); + +clear_amba: amba_driver_unregister(&etm4x_amba_driver);
clear_pm: @@ -2174,6 +2092,7 @@ static int __init etm4x_init(void)
static void __exit etm4x_exit(void) { + etm4_cscfg_unload_builtin_cfg(); amba_driver_unregister(&etm4x_amba_driver); platform_driver_unregister(&etm4_platform_driver); etm4_pm_clear();
Update autofdo configuration and etm4 strobing feature example to use new resource allocation methods.
Signed-off-by: Mike Leach mike.leach@linaro.org --- .../hwtracing/coresight/coresight-cfg-afdo.c | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cfg-afdo.c b/drivers/hwtracing/coresight/coresight-cfg-afdo.c index 84b31184252b..cae142827d44 100644 --- a/drivers/hwtracing/coresight/coresight-cfg-afdo.c +++ b/drivers/hwtracing/coresight/coresight-cfg-afdo.c @@ -30,63 +30,63 @@ static struct cscfg_regval_desc strobe_regs[] = { /* resource selectors */ { .type = CS_CFG_REG_TYPE_RESOURCE, - .offset = TRCRSCTLRn(2), + .offset = 2, .hw_info = ETM4_CFG_RES_SEL, .val32 = 0x20001, }, { .type = CS_CFG_REG_TYPE_RESOURCE, - .offset = TRCRSCTLRn(3), - .hw_info = ETM4_CFG_RES_SEQ, + .offset = 3, + .hw_info = ETM4_CFG_RES_SEL, .val32 = 0x20002, }, /* strobe window counter 0 - reload from param 0 */ { .type = CS_CFG_REG_TYPE_RESOURCE | CS_CFG_REG_TYPE_VAL_SAVE, - .offset = TRCCNTVRn(0), - .hw_info = ETM4_CFG_RES_CTR, + .offset = 0, + .hw_info = ETM4_CFG_RES_CTR | ETM4_CTR_VAL, }, { .type = CS_CFG_REG_TYPE_RESOURCE | CS_CFG_REG_TYPE_VAL_PARAM, - .offset = TRCCNTRLDVRn(0), - .hw_info = ETM4_CFG_RES_CTR, + .offset = 0, + .hw_info = ETM4_CFG_RES_CTR | ETM4_CTR_RLD, .val32 = 0, }, { .type = CS_CFG_REG_TYPE_RESOURCE, - .offset = TRCCNTCTLRn(0), - .hw_info = ETM4_CFG_RES_CTR, + .offset = 0, + .hw_info = ETM4_CFG_RES_CTR | ETM4_CTR_CTRL, .val32 = 0x10001, }, /* strobe period counter 1 - reload from param 1 */ { .type = CS_CFG_REG_TYPE_RESOURCE | CS_CFG_REG_TYPE_VAL_SAVE, - .offset = TRCCNTVRn(1), - .hw_info = ETM4_CFG_RES_CTR, + .offset = 1, + .hw_info = ETM4_CFG_RES_CTR | ETM4_CTR_VAL, }, { .type = CS_CFG_REG_TYPE_RESOURCE | CS_CFG_REG_TYPE_VAL_PARAM, - .offset = TRCCNTRLDVRn(1), - .hw_info = ETM4_CFG_RES_CTR, + .offset = 1, + .hw_info = ETM4_CFG_RES_CTR | ETM4_CTR_RLD, .val32 = 1, }, { .type = CS_CFG_REG_TYPE_RESOURCE, - .offset = TRCCNTCTLRn(1), - .hw_info = ETM4_CFG_RES_CTR, + .offset = 1, + .hw_info = ETM4_CFG_RES_CTR | ETM4_CTR_CTRL, .val32 = 0x8102, }, /* sequencer */ { .type = CS_CFG_REG_TYPE_RESOURCE, - .offset = TRCSEQEVRn(0), - .hw_info = ETM4_CFG_RES_SEQ, + .offset = 0, + .hw_info = ETM4_CFG_RES_SEQ | ETM4_SEQ_STATE_R, .val32 = 0x0081, }, { .type = CS_CFG_REG_TYPE_RESOURCE, - .offset = TRCSEQEVRn(1), - .hw_info = ETM4_CFG_RES_SEQ, + .offset = 1, + .hw_info = ETM4_CFG_RES_SEQ | ETM4_SEQ_STATE_R, .val32 = 0x0000, }, /* view-inst */
Hey Mike,
On Wed, May 12, 2021 at 10:17:44PM +0100, Mike Leach wrote:
This patchset represents the second phase of CoreSight configuration management.
- API updated to allow dynamic load and unload of configurations and
features. Dependency management between loaded sets is added.
- New configuration and feature sets can be added using a loadable module.
An example in /samples/coresight is provided to demonstrate this.
I tried to compile the sample module and I get:
samples/coresight/coresight-cfg-sample.c:7:10: fatal error: coresight-config.h: No such file or directory #include "coresight-config.h" ^~~~~~~~~~~~~~~~~~~~ compilation terminated.
Can you reproduce on your side?
Thanks, Mathieu
- Resource management API is added. This allows the system to ensure that
loaded configurations and features are only loaded onto devices that can support them.
Further - it ensures that configurations with multiple features cannot over allocate resources.
- configfs can be used to activate a configuration which will then be used
when controlling tracing using sysfs.
- Resource management is added to ETMv4 configurations. This allows current
and future features and configurations to be defined in terms of resources used as well as registers to be programmed.
Defining features in this way allows the resource management to operate correctly.
The perf event parsing is also adjusted to allow the ETM resources requested on the command line (e.g. address filters, etc) to be correctly handled using resoruce management alongside the complex configurations such as autofdo.
Applies to coresight/next - which is 5.13-rc1 + initial Coresight configuration patchset.
To follow in future revisions / sets:- a) load of additional config and features by configfs b) ECT and CTI and other Coresight components support for configuration and features.
Mike Leach (8): coresight: syscfg: Update API to allow dynamic load and unload coresight: syscfg: Update load API for config loadable modules coresight: syscfg: Example CoreSight configuration loadable module coresight: configfs: Allow configfs to activate configuration. coresight: syscfg: Add API to check and validate device resources. coresight: etm4x: syscfg: Add resource management to etm4x. coresight: etm4x: Update perf event resource handling. coresight: etm4x: Update configuration example.
MAINTAINERS | 1 + .../hwtracing/coresight/coresight-cfg-afdo.c | 38 +- .../coresight/coresight-cfg-preload.c | 9 +- .../hwtracing/coresight/coresight-config.c | 71 ++- .../hwtracing/coresight/coresight-config.h | 45 +- .../hwtracing/coresight/coresight-etm4x-cfg.c | 533 ++++++++++++++++++ .../hwtracing/coresight/coresight-etm4x-cfg.h | 196 ++++++- .../coresight/coresight-etm4x-core.c | 250 +++----- .../coresight/coresight-syscfg-configfs.c | 87 +++ .../coresight/coresight-syscfg-configfs.h | 4 + .../hwtracing/coresight/coresight-syscfg.c | 390 +++++++++++-- .../hwtracing/coresight/coresight-syscfg.h | 38 +- include/linux/coresight.h | 2 + samples/Kconfig | 9 + samples/Makefile | 1 + samples/coresight/Makefile | 4 + samples/coresight/coresight-cfg-sample.c | 73 +++ 17 files changed, 1511 insertions(+), 240 deletions(-) create mode 100644 samples/coresight/Makefile create mode 100644 samples/coresight/coresight-cfg-sample.c
-- 2.17.1
Hi Mathieu,
On Thu, 13 May 2021 at 16:56, Mathieu Poirier mathieu.poirier@linaro.org wrote:
Hey Mike,
On Wed, May 12, 2021 at 10:17:44PM +0100, Mike Leach wrote:
This patchset represents the second phase of CoreSight configuration management.
- API updated to allow dynamic load and unload of configurations and
features. Dependency management between loaded sets is added.
- New configuration and feature sets can be added using a loadable module.
An example in /samples/coresight is provided to demonstrate this.
I tried to compile the sample module and I get:
samples/coresight/coresight-cfg-sample.c:7:10: fatal error: coresight-config.h: No such file or directory #include "coresight-config.h" ^~~~~~~~~~~~~~~~~~~~ compilation terminated.
Can you reproduce on your side?
No problem here. I cleaned and rechecked I had the sample selected in my .config (CONFIG_SAMPLE_CORESIGHT_SYSCFG=m) and it built just fine.
Regards
Mike
Thanks, Mathieu
- Resource management API is added. This allows the system to ensure that
loaded configurations and features are only loaded onto devices that can support them.
Further - it ensures that configurations with multiple features cannot over allocate resources.
- configfs can be used to activate a configuration which will then be used
when controlling tracing using sysfs.
- Resource management is added to ETMv4 configurations. This allows current
and future features and configurations to be defined in terms of resources used as well as registers to be programmed.
Defining features in this way allows the resource management to operate correctly.
The perf event parsing is also adjusted to allow the ETM resources requested on the command line (e.g. address filters, etc) to be correctly handled using resoruce management alongside the complex configurations such as autofdo.
Applies to coresight/next - which is 5.13-rc1 + initial Coresight configuration patchset.
To follow in future revisions / sets:- a) load of additional config and features by configfs b) ECT and CTI and other Coresight components support for configuration and features.
Mike Leach (8): coresight: syscfg: Update API to allow dynamic load and unload coresight: syscfg: Update load API for config loadable modules coresight: syscfg: Example CoreSight configuration loadable module coresight: configfs: Allow configfs to activate configuration. coresight: syscfg: Add API to check and validate device resources. coresight: etm4x: syscfg: Add resource management to etm4x. coresight: etm4x: Update perf event resource handling. coresight: etm4x: Update configuration example.
MAINTAINERS | 1 + .../hwtracing/coresight/coresight-cfg-afdo.c | 38 +- .../coresight/coresight-cfg-preload.c | 9 +- .../hwtracing/coresight/coresight-config.c | 71 ++- .../hwtracing/coresight/coresight-config.h | 45 +- .../hwtracing/coresight/coresight-etm4x-cfg.c | 533 ++++++++++++++++++ .../hwtracing/coresight/coresight-etm4x-cfg.h | 196 ++++++- .../coresight/coresight-etm4x-core.c | 250 +++----- .../coresight/coresight-syscfg-configfs.c | 87 +++ .../coresight/coresight-syscfg-configfs.h | 4 + .../hwtracing/coresight/coresight-syscfg.c | 390 +++++++++++-- .../hwtracing/coresight/coresight-syscfg.h | 38 +- include/linux/coresight.h | 2 + samples/Kconfig | 9 + samples/Makefile | 1 + samples/coresight/Makefile | 4 + samples/coresight/coresight-cfg-sample.c | 73 +++ 17 files changed, 1511 insertions(+), 240 deletions(-) create mode 100644 samples/coresight/Makefile create mode 100644 samples/coresight/coresight-cfg-sample.c
-- 2.17.1
On Thu, May 13, 2021 at 05:53:36PM +0100, Mike Leach wrote:
Hi Mathieu,
On Thu, 13 May 2021 at 16:56, Mathieu Poirier mathieu.poirier@linaro.org wrote:
Hey Mike,
On Wed, May 12, 2021 at 10:17:44PM +0100, Mike Leach wrote:
This patchset represents the second phase of CoreSight configuration management.
- API updated to allow dynamic load and unload of configurations and
features. Dependency management between loaded sets is added.
- New configuration and feature sets can be added using a loadable module.
An example in /samples/coresight is provided to demonstrate this.
I tried to compile the sample module and I get:
samples/coresight/coresight-cfg-sample.c:7:10: fatal error: coresight-config.h: No such file or directory #include "coresight-config.h" ^~~~~~~~~~~~~~~~~~~~ compilation terminated.
Can you reproduce on your side?
No problem here. I cleaned and rechecked I had the sample selected in my .config (CONFIG_SAMPLE_CORESIGHT_SYSCFG=m) and it built just fine.
Just reminding, before I tried other kernel samples (actually for eBPF samples), I firstly installed headers:
# make headers_install # creates "usr/include" directory in the build top directory # make samples/xxx
I didn't verify Mike's patch set, just in case Mathieu encountered the same issue and should install header ahead, so share the notes.
Thanks, Leo
Hi Mike
On 12/05/2021 22:17, Mike Leach wrote:
This patchset represents the second phase of CoreSight configuration management.
Thanks for the series.
- API updated to allow dynamic load and unload of configurations and
features. Dependency management between loaded sets is added.
- New configuration and feature sets can be added using a loadable module.
An example in /samples/coresight is provided to demonstrate this.
Is it possible to do this at runtime configfs ? I thought we tied this to configfs for this feautre.
While the module approach works, I feel is a hinderance for wider adoption and is something that doesn't allow for use on production systems.
- Resource management API is added. This allows the system to ensure that
loaded configurations and features are only loaded onto devices that can support them.
Further - it ensures that configurations with multiple features cannot over allocate resources.
- configfs can be used to activate a configuration which will then be used
when controlling tracing using sysfs.
- Resource management is added to ETMv4 configurations. This allows current
and future features and configurations to be defined in terms of resources used as well as registers to be programmed.
Defining features in this way allows the resource management to operate correctly.
The perf event parsing is also adjusted to allow the ETM resources requested on the command line (e.g. address filters, etc) to be correctly handled using resoruce management alongside the complex configurations such as autofdo.
Applies to coresight/next - which is 5.13-rc1 + initial Coresight configuration patchset.
To follow in future revisions / sets:- a) load of additional config and features by configfs
Is this something that implements what I requested above ? Then I don't see how the module approach will be used once that is in place and we will be left with something to maintain forever.
Suzuki
b) ECT and CTI and other Coresight components support for configuration and features.
Mike Leach (8): coresight: syscfg: Update API to allow dynamic load and unload coresight: syscfg: Update load API for config loadable modules coresight: syscfg: Example CoreSight configuration loadable module coresight: configfs: Allow configfs to activate configuration. coresight: syscfg: Add API to check and validate device resources. coresight: etm4x: syscfg: Add resource management to etm4x. coresight: etm4x: Update perf event resource handling. coresight: etm4x: Update configuration example.
MAINTAINERS | 1 + .../hwtracing/coresight/coresight-cfg-afdo.c | 38 +- .../coresight/coresight-cfg-preload.c | 9 +- .../hwtracing/coresight/coresight-config.c | 71 ++- .../hwtracing/coresight/coresight-config.h | 45 +- .../hwtracing/coresight/coresight-etm4x-cfg.c | 533 ++++++++++++++++++ .../hwtracing/coresight/coresight-etm4x-cfg.h | 196 ++++++- .../coresight/coresight-etm4x-core.c | 250 +++----- .../coresight/coresight-syscfg-configfs.c | 87 +++ .../coresight/coresight-syscfg-configfs.h | 4 + .../hwtracing/coresight/coresight-syscfg.c | 390 +++++++++++-- .../hwtracing/coresight/coresight-syscfg.h | 38 +- include/linux/coresight.h | 2 + samples/Kconfig | 9 + samples/Makefile | 1 + samples/coresight/Makefile | 4 + samples/coresight/coresight-cfg-sample.c | 73 +++ 17 files changed, 1511 insertions(+), 240 deletions(-) create mode 100644 samples/coresight/Makefile create mode 100644 samples/coresight/coresight-cfg-sample.c
Hi Suzuki,
On Tue, 18 May 2021 at 19:31, Suzuki K Poulose suzuki.poulose@arm.com wrote:
Hi Mike
On 12/05/2021 22:17, Mike Leach wrote:
This patchset represents the second phase of CoreSight configuration management.
Thanks for the series.
- API updated to allow dynamic load and unload of configurations and
features. Dependency management between loaded sets is added.
- New configuration and feature sets can be added using a loadable module.
An example in /samples/coresight is provided to demonstrate this.
Is it possible to do this at runtime configfs ? I thought we tied this to configfs for this feautre.
While the module approach works, I feel is a hinderance for wider adoption and is something that doesn't allow for use on production systems.
- Resource management API is added. This allows the system to ensure that
loaded configurations and features are only loaded onto devices that can support them.
Further - it ensures that configurations with multiple features cannot over allocate resources.
- configfs can be used to activate a configuration which will then be used
when controlling tracing using sysfs.
- Resource management is added to ETMv4 configurations. This allows current
and future features and configurations to be defined in terms of resources used as well as registers to be programmed.
Defining features in this way allows the resource management to operate correctly.
The perf event parsing is also adjusted to allow the ETM resources requested on the command line (e.g. address filters, etc) to be correctly handled using resoruce management alongside the complex configurations such as autofdo.
Applies to coresight/next - which is 5.13-rc1 + initial Coresight configuration patchset.
To follow in future revisions / sets:- a) load of additional config and features by configfs
Is this something that implements what I requested above ? Then I don't see how the module approach will be used once that is in place and we will be left with something to maintain forever.
The first couple of patches in this set implement the dynamic load and unload of configurations on top of the existing static load only infrastructure from the baseline set. This code is used when dynamic loading from both a module and in the case of a configfs load. So there is very little code that is specific to the module load case.
The module load was requested by Mathieu as a way of loading new configurations and I guess even in production environments, approved modules can be loaded at boot to provide standard configs should people want them - after all the coresight modules themselves have to be loaded.
Also - the module load tests the new code very nicely.
Regards
Mike
Suzuki
b) ECT and CTI and other Coresight components support for configuration and features.
Mike Leach (8): coresight: syscfg: Update API to allow dynamic load and unload coresight: syscfg: Update load API for config loadable modules coresight: syscfg: Example CoreSight configuration loadable module coresight: configfs: Allow configfs to activate configuration. coresight: syscfg: Add API to check and validate device resources. coresight: etm4x: syscfg: Add resource management to etm4x. coresight: etm4x: Update perf event resource handling. coresight: etm4x: Update configuration example.
MAINTAINERS | 1 + .../hwtracing/coresight/coresight-cfg-afdo.c | 38 +- .../coresight/coresight-cfg-preload.c | 9 +- .../hwtracing/coresight/coresight-config.c | 71 ++- .../hwtracing/coresight/coresight-config.h | 45 +- .../hwtracing/coresight/coresight-etm4x-cfg.c | 533 ++++++++++++++++++ .../hwtracing/coresight/coresight-etm4x-cfg.h | 196 ++++++- .../coresight/coresight-etm4x-core.c | 250 +++----- .../coresight/coresight-syscfg-configfs.c | 87 +++ .../coresight/coresight-syscfg-configfs.h | 4 + .../hwtracing/coresight/coresight-syscfg.c | 390 +++++++++++-- .../hwtracing/coresight/coresight-syscfg.h | 38 +- include/linux/coresight.h | 2 + samples/Kconfig | 9 + samples/Makefile | 1 + samples/coresight/Makefile | 4 + samples/coresight/coresight-cfg-sample.c | 73 +++ 17 files changed, 1511 insertions(+), 240 deletions(-) create mode 100644 samples/coresight/Makefile create mode 100644 samples/coresight/coresight-cfg-sample.c
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
On Wed, May 19, 2021 at 10:43:03AM +0100, Mike Leach wrote:
Hi Suzuki,
On Tue, 18 May 2021 at 19:31, Suzuki K Poulose suzuki.poulose@arm.com wrote:
Hi Mike
On 12/05/2021 22:17, Mike Leach wrote:
This patchset represents the second phase of CoreSight configuration management.
Thanks for the series.
- API updated to allow dynamic load and unload of configurations and
features. Dependency management between loaded sets is added.
- New configuration and feature sets can be added using a loadable module.
An example in /samples/coresight is provided to demonstrate this.
Is it possible to do this at runtime configfs ? I thought we tied this to configfs for this feautre.
While the module approach works, I feel is a hinderance for wider adoption and is something that doesn't allow for use on production systems.
- Resource management API is added. This allows the system to ensure that
loaded configurations and features are only loaded onto devices that can support them.
Further - it ensures that configurations with multiple features cannot over allocate resources.
- configfs can be used to activate a configuration which will then be used
when controlling tracing using sysfs.
- Resource management is added to ETMv4 configurations. This allows current
and future features and configurations to be defined in terms of resources used as well as registers to be programmed.
Defining features in this way allows the resource management to operate correctly.
The perf event parsing is also adjusted to allow the ETM resources requested on the command line (e.g. address filters, etc) to be correctly handled using resoruce management alongside the complex configurations such as autofdo.
Applies to coresight/next - which is 5.13-rc1 + initial Coresight configuration patchset.
To follow in future revisions / sets:- a) load of additional config and features by configfs
Is this something that implements what I requested above ? Then I don't see how the module approach will be used once that is in place and we will be left with something to maintain forever.
The first couple of patches in this set implement the dynamic load and unload of configurations on top of the existing static load only infrastructure from the baseline set. This code is used when dynamic loading from both a module and in the case of a configfs load. So there is very little code that is specific to the module load case.
The module load was requested by Mathieu as a way of loading new configurations and I guess even in production environments, approved modules can be loaded at boot to provide standard configs should people want them - after all the coresight modules themselves have to be loaded.
The goal in making the complex configurations dynamically configurable was to avoid bloat of the driver. I don't know how many new implemenation we'll end up with but I'd rather provision for a large number now rather than having to backtrack in a couple of years from now.
If I recall correctly we discussed the possibility of having all complex configuration go through configfs and be added from user space, i.e no static or module elements. But there were scenarios (and I can't recall those) where static/module elements were needed, which got us to the current implementation.
If my assesment of the situation is erroneous and static/module elements are not needed then I'd be in favour a configfs only solution.
Also - the module load tests the new code very nicely.
Regards
Mike
Suzuki
b) ECT and CTI and other Coresight components support for configuration and features.
Mike Leach (8): coresight: syscfg: Update API to allow dynamic load and unload coresight: syscfg: Update load API for config loadable modules coresight: syscfg: Example CoreSight configuration loadable module coresight: configfs: Allow configfs to activate configuration. coresight: syscfg: Add API to check and validate device resources. coresight: etm4x: syscfg: Add resource management to etm4x. coresight: etm4x: Update perf event resource handling. coresight: etm4x: Update configuration example.
MAINTAINERS | 1 + .../hwtracing/coresight/coresight-cfg-afdo.c | 38 +- .../coresight/coresight-cfg-preload.c | 9 +- .../hwtracing/coresight/coresight-config.c | 71 ++- .../hwtracing/coresight/coresight-config.h | 45 +- .../hwtracing/coresight/coresight-etm4x-cfg.c | 533 ++++++++++++++++++ .../hwtracing/coresight/coresight-etm4x-cfg.h | 196 ++++++- .../coresight/coresight-etm4x-core.c | 250 +++----- .../coresight/coresight-syscfg-configfs.c | 87 +++ .../coresight/coresight-syscfg-configfs.h | 4 + .../hwtracing/coresight/coresight-syscfg.c | 390 +++++++++++-- .../hwtracing/coresight/coresight-syscfg.h | 38 +- include/linux/coresight.h | 2 + samples/Kconfig | 9 + samples/Makefile | 1 + samples/coresight/Makefile | 4 + samples/coresight/coresight-cfg-sample.c | 73 +++ 17 files changed, 1511 insertions(+), 240 deletions(-) create mode 100644 samples/coresight/Makefile create mode 100644 samples/coresight/coresight-cfg-sample.c
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK