1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (c) 2020 Linaro Limited, All rights reserved. |
4 | * Author: Mike Leach <mike.leach@linaro.org> |
5 | */ |
6 | |
7 | #include <linux/platform_device.h> |
8 | #include <linux/slab.h> |
9 | |
10 | #include "coresight-config.h" |
11 | #include "coresight-etm-perf.h" |
12 | #include "coresight-syscfg.h" |
13 | #include "coresight-syscfg-configfs.h" |
14 | |
15 | /* |
16 | * cscfg_ API manages configurations and features for the entire coresight |
17 | * infrastructure. |
18 | * |
19 | * It allows the loading of configurations and features, and loads these into |
20 | * coresight devices as appropriate. |
21 | */ |
22 | |
23 | /* protect the cscsg_data and device */ |
24 | static DEFINE_MUTEX(cscfg_mutex); |
25 | |
26 | /* only one of these */ |
27 | static struct cscfg_manager *cscfg_mgr; |
28 | |
29 | /* load features and configuations into the lists */ |
30 | |
31 | /* get name feature instance from a coresight device list of features */ |
32 | static struct cscfg_feature_csdev * |
33 | cscfg_get_feat_csdev(struct coresight_device *csdev, const char *name) |
34 | { |
35 | struct cscfg_feature_csdev *feat_csdev = NULL; |
36 | |
37 | list_for_each_entry(feat_csdev, &csdev->feature_csdev_list, node) { |
38 | if (strcmp(feat_csdev->feat_desc->name, name) == 0) |
39 | return feat_csdev; |
40 | } |
41 | return NULL; |
42 | } |
43 | |
44 | /* allocate the device config instance - with max number of used features */ |
45 | static struct cscfg_config_csdev * |
46 | cscfg_alloc_csdev_cfg(struct coresight_device *csdev, int nr_feats) |
47 | { |
48 | struct cscfg_config_csdev *config_csdev = NULL; |
49 | struct device *dev = csdev->dev.parent; |
50 | |
51 | /* this is being allocated using the devm for the coresight device */ |
52 | config_csdev = devm_kzalloc(dev, |
53 | offsetof(struct cscfg_config_csdev, feats_csdev[nr_feats]), |
54 | GFP_KERNEL); |
55 | if (!config_csdev) |
56 | return NULL; |
57 | |
58 | config_csdev->csdev = csdev; |
59 | return config_csdev; |
60 | } |
61 | |
62 | /* Load a config into a device if there are any feature matches between config and device */ |
63 | static int cscfg_add_csdev_cfg(struct coresight_device *csdev, |
64 | struct cscfg_config_desc *config_desc) |
65 | { |
66 | struct cscfg_config_csdev *config_csdev = NULL; |
67 | struct cscfg_feature_csdev *feat_csdev; |
68 | unsigned long flags; |
69 | int i; |
70 | |
71 | /* look at each required feature and see if it matches any feature on the device */ |
72 | for (i = 0; i < config_desc->nr_feat_refs; i++) { |
73 | /* look for a matching name */ |
74 | feat_csdev = cscfg_get_feat_csdev(csdev, name: config_desc->feat_ref_names[i]); |
75 | if (feat_csdev) { |
76 | /* |
77 | * At least one feature on this device matches the config |
78 | * add a config instance to the device and a reference to the feature. |
79 | */ |
80 | if (!config_csdev) { |
81 | config_csdev = cscfg_alloc_csdev_cfg(csdev, |
82 | nr_feats: config_desc->nr_feat_refs); |
83 | if (!config_csdev) |
84 | return -ENOMEM; |
85 | config_csdev->config_desc = config_desc; |
86 | } |
87 | config_csdev->feats_csdev[config_csdev->nr_feat++] = feat_csdev; |
88 | } |
89 | } |
90 | /* if matched features, add config to device.*/ |
91 | if (config_csdev) { |
92 | spin_lock_irqsave(&csdev->cscfg_csdev_lock, flags); |
93 | list_add(new: &config_csdev->node, head: &csdev->config_csdev_list); |
94 | spin_unlock_irqrestore(lock: &csdev->cscfg_csdev_lock, flags); |
95 | } |
96 | |
97 | return 0; |
98 | } |
99 | |
100 | /* |
101 | * Add the config to the set of registered devices - call with mutex locked. |
102 | * Iterates through devices - any device that matches one or more of the |
103 | * configuration features will load it, the others will ignore it. |
104 | */ |
105 | static int cscfg_add_cfg_to_csdevs(struct cscfg_config_desc *config_desc) |
106 | { |
107 | struct cscfg_registered_csdev *csdev_item; |
108 | int err; |
109 | |
110 | list_for_each_entry(csdev_item, &cscfg_mgr->csdev_desc_list, item) { |
111 | err = cscfg_add_csdev_cfg(csdev: csdev_item->csdev, config_desc); |
112 | if (err) |
113 | return err; |
114 | } |
115 | return 0; |
116 | } |
117 | |
118 | /* |
119 | * Allocate a feature object for load into a csdev. |
120 | * memory allocated using the csdev->dev object using devm managed allocator. |
121 | */ |
122 | static struct cscfg_feature_csdev * |
123 | cscfg_alloc_csdev_feat(struct coresight_device *csdev, struct cscfg_feature_desc *feat_desc) |
124 | { |
125 | struct cscfg_feature_csdev *feat_csdev = NULL; |
126 | struct device *dev = csdev->dev.parent; |
127 | int i; |
128 | |
129 | feat_csdev = devm_kzalloc(dev, size: sizeof(struct cscfg_feature_csdev), GFP_KERNEL); |
130 | if (!feat_csdev) |
131 | return NULL; |
132 | |
133 | /* parameters are optional - could be 0 */ |
134 | feat_csdev->nr_params = feat_desc->nr_params; |
135 | |
136 | /* |
137 | * if we need parameters, zero alloc the space here, the load routine in |
138 | * the csdev device driver will fill out some information according to |
139 | * feature descriptor. |
140 | */ |
141 | if (feat_csdev->nr_params) { |
142 | feat_csdev->params_csdev = devm_kcalloc(dev, n: feat_csdev->nr_params, |
143 | size: sizeof(struct cscfg_parameter_csdev), |
144 | GFP_KERNEL); |
145 | if (!feat_csdev->params_csdev) |
146 | return NULL; |
147 | |
148 | /* |
149 | * fill in the feature reference in the param - other fields |
150 | * handled by loader in csdev. |
151 | */ |
152 | for (i = 0; i < feat_csdev->nr_params; i++) |
153 | feat_csdev->params_csdev[i].feat_csdev = feat_csdev; |
154 | } |
155 | |
156 | /* |
157 | * Always have registers to program - again the load routine in csdev device |
158 | * will fill out according to feature descriptor and device requirements. |
159 | */ |
160 | feat_csdev->nr_regs = feat_desc->nr_regs; |
161 | feat_csdev->regs_csdev = devm_kcalloc(dev, n: feat_csdev->nr_regs, |
162 | size: sizeof(struct cscfg_regval_csdev), |
163 | GFP_KERNEL); |
164 | if (!feat_csdev->regs_csdev) |
165 | return NULL; |
166 | |
167 | /* load the feature default values */ |
168 | feat_csdev->feat_desc = feat_desc; |
169 | feat_csdev->csdev = csdev; |
170 | |
171 | return feat_csdev; |
172 | } |
173 | |
174 | /* load one feature into one coresight device */ |
175 | static int cscfg_load_feat_csdev(struct coresight_device *csdev, |
176 | struct cscfg_feature_desc *feat_desc, |
177 | struct cscfg_csdev_feat_ops *ops) |
178 | { |
179 | struct cscfg_feature_csdev *feat_csdev; |
180 | unsigned long flags; |
181 | int err; |
182 | |
183 | if (!ops->load_feat) |
184 | return -EINVAL; |
185 | |
186 | feat_csdev = cscfg_alloc_csdev_feat(csdev, feat_desc); |
187 | if (!feat_csdev) |
188 | return -ENOMEM; |
189 | |
190 | /* load the feature into the device */ |
191 | err = ops->load_feat(csdev, feat_csdev); |
192 | if (err) |
193 | return err; |
194 | |
195 | /* add to internal csdev feature list & initialise using reset call */ |
196 | cscfg_reset_feat(feat_csdev); |
197 | spin_lock_irqsave(&csdev->cscfg_csdev_lock, flags); |
198 | list_add(new: &feat_csdev->node, head: &csdev->feature_csdev_list); |
199 | spin_unlock_irqrestore(lock: &csdev->cscfg_csdev_lock, flags); |
200 | |
201 | return 0; |
202 | } |
203 | |
204 | /* |
205 | * Add feature to any matching devices - call with mutex locked. |
206 | * Iterates through devices - any device that matches the feature will be |
207 | * called to load it. |
208 | */ |
209 | static int cscfg_add_feat_to_csdevs(struct cscfg_feature_desc *feat_desc) |
210 | { |
211 | struct cscfg_registered_csdev *csdev_item; |
212 | int err; |
213 | |
214 | list_for_each_entry(csdev_item, &cscfg_mgr->csdev_desc_list, item) { |
215 | if (csdev_item->match_flags & feat_desc->match_flags) { |
216 | err = cscfg_load_feat_csdev(csdev: csdev_item->csdev, feat_desc, ops: &csdev_item->ops); |
217 | if (err) |
218 | return err; |
219 | } |
220 | } |
221 | return 0; |
222 | } |
223 | |
224 | /* check feature list for a named feature - call with mutex locked. */ |
225 | static bool cscfg_match_list_feat(const char *name) |
226 | { |
227 | struct cscfg_feature_desc *feat_desc; |
228 | |
229 | list_for_each_entry(feat_desc, &cscfg_mgr->feat_desc_list, item) { |
230 | if (strcmp(feat_desc->name, name) == 0) |
231 | return true; |
232 | } |
233 | return false; |
234 | } |
235 | |
236 | /* check all feat needed for cfg are in the list - call with mutex locked. */ |
237 | static int cscfg_check_feat_for_cfg(struct cscfg_config_desc *config_desc) |
238 | { |
239 | int i; |
240 | |
241 | for (i = 0; i < config_desc->nr_feat_refs; i++) |
242 | if (!cscfg_match_list_feat(name: config_desc->feat_ref_names[i])) |
243 | return -EINVAL; |
244 | return 0; |
245 | } |
246 | |
247 | /* |
248 | * load feature - add to feature list. |
249 | */ |
250 | static int cscfg_load_feat(struct cscfg_feature_desc *feat_desc) |
251 | { |
252 | int err; |
253 | struct cscfg_feature_desc *feat_desc_exist; |
254 | |
255 | /* new feature must have unique name */ |
256 | list_for_each_entry(feat_desc_exist, &cscfg_mgr->feat_desc_list, item) { |
257 | if (!strcmp(feat_desc_exist->name, feat_desc->name)) |
258 | return -EEXIST; |
259 | } |
260 | |
261 | /* add feature to any matching registered devices */ |
262 | err = cscfg_add_feat_to_csdevs(feat_desc); |
263 | if (err) |
264 | return err; |
265 | |
266 | list_add(new: &feat_desc->item, head: &cscfg_mgr->feat_desc_list); |
267 | return 0; |
268 | } |
269 | |
270 | /* |
271 | * load config into the system - validate used features exist then add to |
272 | * config list. |
273 | */ |
274 | static int cscfg_load_config(struct cscfg_config_desc *config_desc) |
275 | { |
276 | int err; |
277 | struct cscfg_config_desc *config_desc_exist; |
278 | |
279 | /* new configuration must have a unique name */ |
280 | list_for_each_entry(config_desc_exist, &cscfg_mgr->config_desc_list, item) { |
281 | if (!strcmp(config_desc_exist->name, config_desc->name)) |
282 | return -EEXIST; |
283 | } |
284 | |
285 | /* validate features are present */ |
286 | err = cscfg_check_feat_for_cfg(config_desc); |
287 | if (err) |
288 | return err; |
289 | |
290 | /* add config to any matching registered device */ |
291 | err = cscfg_add_cfg_to_csdevs(config_desc); |
292 | if (err) |
293 | return err; |
294 | |
295 | /* add config to perf fs to allow selection */ |
296 | err = etm_perf_add_symlink_cscfg(dev: cscfg_device(), config_desc); |
297 | if (err) |
298 | return err; |
299 | |
300 | list_add(new: &config_desc->item, head: &cscfg_mgr->config_desc_list); |
301 | atomic_set(v: &config_desc->active_cnt, i: 0); |
302 | return 0; |
303 | } |
304 | |
305 | /* get a feature descriptor by name */ |
306 | const struct cscfg_feature_desc *cscfg_get_named_feat_desc(const char *name) |
307 | { |
308 | const struct cscfg_feature_desc *feat_desc = NULL, *feat_desc_item; |
309 | |
310 | mutex_lock(&cscfg_mutex); |
311 | |
312 | list_for_each_entry(feat_desc_item, &cscfg_mgr->feat_desc_list, item) { |
313 | if (strcmp(feat_desc_item->name, name) == 0) { |
314 | feat_desc = feat_desc_item; |
315 | break; |
316 | } |
317 | } |
318 | |
319 | mutex_unlock(lock: &cscfg_mutex); |
320 | return feat_desc; |
321 | } |
322 | |
323 | /* called with cscfg_mutex held */ |
324 | static struct cscfg_feature_csdev * |
325 | cscfg_csdev_get_feat_from_desc(struct coresight_device *csdev, |
326 | struct cscfg_feature_desc *feat_desc) |
327 | { |
328 | struct cscfg_feature_csdev *feat_csdev; |
329 | |
330 | list_for_each_entry(feat_csdev, &csdev->feature_csdev_list, node) { |
331 | if (feat_csdev->feat_desc == feat_desc) |
332 | return feat_csdev; |
333 | } |
334 | return NULL; |
335 | } |
336 | |
337 | int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc, |
338 | int param_idx, u64 value) |
339 | { |
340 | int err = 0; |
341 | struct cscfg_feature_csdev *feat_csdev; |
342 | struct cscfg_registered_csdev *csdev_item; |
343 | |
344 | mutex_lock(&cscfg_mutex); |
345 | |
346 | /* check if any config active & return busy */ |
347 | if (atomic_read(v: &cscfg_mgr->sys_active_cnt)) { |
348 | err = -EBUSY; |
349 | goto unlock_exit; |
350 | } |
351 | |
352 | /* set the value */ |
353 | if ((param_idx < 0) || (param_idx >= feat_desc->nr_params)) { |
354 | err = -EINVAL; |
355 | goto unlock_exit; |
356 | } |
357 | feat_desc->params_desc[param_idx].value = value; |
358 | |
359 | /* update loaded instances.*/ |
360 | list_for_each_entry(csdev_item, &cscfg_mgr->csdev_desc_list, item) { |
361 | feat_csdev = cscfg_csdev_get_feat_from_desc(csdev: csdev_item->csdev, feat_desc); |
362 | if (feat_csdev) |
363 | feat_csdev->params_csdev[param_idx].current_value = value; |
364 | } |
365 | |
366 | unlock_exit: |
367 | mutex_unlock(lock: &cscfg_mutex); |
368 | return err; |
369 | } |
370 | |
371 | /* |
372 | * Conditionally up reference count on owner to prevent unload. |
373 | * |
374 | * module loaded configs need to be locked in to prevent premature unload. |
375 | */ |
376 | static int cscfg_owner_get(struct cscfg_load_owner_info *owner_info) |
377 | { |
378 | if ((owner_info->type == CSCFG_OWNER_MODULE) && |
379 | (!try_module_get(module: owner_info->owner_handle))) |
380 | return -EINVAL; |
381 | return 0; |
382 | } |
383 | |
384 | /* conditionally lower ref count on an owner */ |
385 | static void cscfg_owner_put(struct cscfg_load_owner_info *owner_info) |
386 | { |
387 | if (owner_info->type == CSCFG_OWNER_MODULE) |
388 | module_put(module: owner_info->owner_handle); |
389 | } |
390 | |
391 | static void cscfg_remove_owned_csdev_configs(struct coresight_device *csdev, void *load_owner) |
392 | { |
393 | struct cscfg_config_csdev *config_csdev, *tmp; |
394 | |
395 | if (list_empty(head: &csdev->config_csdev_list)) |
396 | return; |
397 | |
398 | list_for_each_entry_safe(config_csdev, tmp, &csdev->config_csdev_list, node) { |
399 | if (config_csdev->config_desc->load_owner == load_owner) |
400 | list_del(entry: &config_csdev->node); |
401 | } |
402 | } |
403 | |
404 | static void cscfg_remove_owned_csdev_features(struct coresight_device *csdev, void *load_owner) |
405 | { |
406 | struct cscfg_feature_csdev *feat_csdev, *tmp; |
407 | |
408 | if (list_empty(head: &csdev->feature_csdev_list)) |
409 | return; |
410 | |
411 | list_for_each_entry_safe(feat_csdev, tmp, &csdev->feature_csdev_list, node) { |
412 | if (feat_csdev->feat_desc->load_owner == load_owner) |
413 | list_del(entry: &feat_csdev->node); |
414 | } |
415 | } |
416 | |
417 | /* |
418 | * Unregister all configuration and features from configfs owned by load_owner. |
419 | * Although this is called without the list mutex being held, it is in the |
420 | * context of an unload operation which are strictly serialised, |
421 | * so the lists cannot change during this call. |
422 | */ |
423 | static void cscfg_fs_unregister_cfgs_feats(void *load_owner) |
424 | { |
425 | struct cscfg_config_desc *config_desc; |
426 | struct cscfg_feature_desc *feat_desc; |
427 | |
428 | list_for_each_entry(config_desc, &cscfg_mgr->config_desc_list, item) { |
429 | if (config_desc->load_owner == load_owner) |
430 | cscfg_configfs_del_config(config_desc); |
431 | } |
432 | list_for_each_entry(feat_desc, &cscfg_mgr->feat_desc_list, item) { |
433 | if (feat_desc->load_owner == load_owner) |
434 | cscfg_configfs_del_feature(feat_desc); |
435 | } |
436 | } |
437 | |
438 | /* |
439 | * removal is relatively easy - just remove from all lists, anything that |
440 | * matches the owner. Memory for the descriptors will be managed by the owner, |
441 | * memory for the csdev items is devm_ allocated with the individual csdev |
442 | * devices. |
443 | */ |
444 | static void cscfg_unload_owned_cfgs_feats(void *load_owner) |
445 | { |
446 | struct cscfg_config_desc *config_desc, *cfg_tmp; |
447 | struct cscfg_feature_desc *feat_desc, *feat_tmp; |
448 | struct cscfg_registered_csdev *csdev_item; |
449 | |
450 | lockdep_assert_held(&cscfg_mutex); |
451 | |
452 | /* remove from each csdev instance feature and config lists */ |
453 | list_for_each_entry(csdev_item, &cscfg_mgr->csdev_desc_list, item) { |
454 | /* |
455 | * for each csdev, check the loaded lists and remove if |
456 | * referenced descriptor is owned |
457 | */ |
458 | cscfg_remove_owned_csdev_configs(csdev: csdev_item->csdev, load_owner); |
459 | cscfg_remove_owned_csdev_features(csdev: csdev_item->csdev, load_owner); |
460 | } |
461 | |
462 | /* remove from the config descriptor lists */ |
463 | list_for_each_entry_safe(config_desc, cfg_tmp, &cscfg_mgr->config_desc_list, item) { |
464 | if (config_desc->load_owner == load_owner) { |
465 | etm_perf_del_symlink_cscfg(config_desc); |
466 | list_del(entry: &config_desc->item); |
467 | } |
468 | } |
469 | |
470 | /* remove from the feature descriptor lists */ |
471 | list_for_each_entry_safe(feat_desc, feat_tmp, &cscfg_mgr->feat_desc_list, item) { |
472 | if (feat_desc->load_owner == load_owner) { |
473 | list_del(entry: &feat_desc->item); |
474 | } |
475 | } |
476 | } |
477 | |
478 | /* |
479 | * load the features and configs to the lists - called with list mutex held |
480 | */ |
481 | static int cscfg_load_owned_cfgs_feats(struct cscfg_config_desc **config_descs, |
482 | struct cscfg_feature_desc **feat_descs, |
483 | struct cscfg_load_owner_info *owner_info) |
484 | { |
485 | int i, err; |
486 | |
487 | lockdep_assert_held(&cscfg_mutex); |
488 | |
489 | /* load features first */ |
490 | if (feat_descs) { |
491 | for (i = 0; feat_descs[i]; i++) { |
492 | err = cscfg_load_feat(feat_desc: feat_descs[i]); |
493 | if (err) { |
494 | pr_err("coresight-syscfg: Failed to load feature %s\n" , |
495 | feat_descs[i]->name); |
496 | return err; |
497 | } |
498 | feat_descs[i]->load_owner = owner_info; |
499 | } |
500 | } |
501 | |
502 | /* next any configurations to check feature dependencies */ |
503 | if (config_descs) { |
504 | for (i = 0; config_descs[i]; i++) { |
505 | err = cscfg_load_config(config_desc: config_descs[i]); |
506 | if (err) { |
507 | pr_err("coresight-syscfg: Failed to load configuration %s\n" , |
508 | config_descs[i]->name); |
509 | return err; |
510 | } |
511 | config_descs[i]->load_owner = owner_info; |
512 | config_descs[i]->available = false; |
513 | } |
514 | } |
515 | return 0; |
516 | } |
517 | |
518 | /* set configurations as available to activate at the end of the load process */ |
519 | static void cscfg_set_configs_available(struct cscfg_config_desc **config_descs) |
520 | { |
521 | int i; |
522 | |
523 | lockdep_assert_held(&cscfg_mutex); |
524 | |
525 | if (config_descs) { |
526 | for (i = 0; config_descs[i]; i++) |
527 | config_descs[i]->available = true; |
528 | } |
529 | } |
530 | |
531 | /* |
532 | * Create and register each of the configurations and features with configfs. |
533 | * Called without mutex being held. |
534 | */ |
535 | static int cscfg_fs_register_cfgs_feats(struct cscfg_config_desc **config_descs, |
536 | struct cscfg_feature_desc **feat_descs) |
537 | { |
538 | int i, err; |
539 | |
540 | if (feat_descs) { |
541 | for (i = 0; feat_descs[i]; i++) { |
542 | err = cscfg_configfs_add_feature(feat_desc: feat_descs[i]); |
543 | if (err) |
544 | return err; |
545 | } |
546 | } |
547 | if (config_descs) { |
548 | for (i = 0; config_descs[i]; i++) { |
549 | err = cscfg_configfs_add_config(config_desc: config_descs[i]); |
550 | if (err) |
551 | return err; |
552 | } |
553 | } |
554 | return 0; |
555 | } |
556 | |
557 | /** |
558 | * cscfg_load_config_sets - API function to load feature and config sets. |
559 | * |
560 | * Take a 0 terminated array of feature descriptors and/or configuration |
561 | * descriptors and load into the system. |
562 | * Features are loaded first to ensure configuration dependencies can be met. |
563 | * |
564 | * To facilitate dynamic loading and unloading, features and configurations |
565 | * have a "load_owner", to allow later unload by the same owner. An owner may |
566 | * be a loadable module or configuration dynamically created via configfs. |
567 | * As later loaded configurations can use earlier loaded features, creating load |
568 | * dependencies, a load order list is maintained. Unload is strictly in the |
569 | * reverse order to load. |
570 | * |
571 | * @config_descs: 0 terminated array of configuration descriptors. |
572 | * @feat_descs: 0 terminated array of feature descriptors. |
573 | * @owner_info: Information on the owner of this set. |
574 | */ |
575 | int cscfg_load_config_sets(struct cscfg_config_desc **config_descs, |
576 | struct cscfg_feature_desc **feat_descs, |
577 | struct cscfg_load_owner_info *owner_info) |
578 | { |
579 | int err = 0; |
580 | |
581 | mutex_lock(&cscfg_mutex); |
582 | if (cscfg_mgr->load_state != CSCFG_NONE) { |
583 | mutex_unlock(lock: &cscfg_mutex); |
584 | return -EBUSY; |
585 | } |
586 | cscfg_mgr->load_state = CSCFG_LOAD; |
587 | |
588 | /* first load and add to the lists */ |
589 | err = cscfg_load_owned_cfgs_feats(config_descs, feat_descs, owner_info); |
590 | if (err) |
591 | goto err_clean_load; |
592 | |
593 | /* add the load owner to the load order list */ |
594 | list_add_tail(new: &owner_info->item, head: &cscfg_mgr->load_order_list); |
595 | if (!list_is_singular(head: &cscfg_mgr->load_order_list)) { |
596 | /* lock previous item in load order list */ |
597 | err = cscfg_owner_get(list_prev_entry(owner_info, item)); |
598 | if (err) |
599 | goto err_clean_owner_list; |
600 | } |
601 | |
602 | /* |
603 | * make visible to configfs - configfs manipulation must occur outside |
604 | * the list mutex lock to avoid circular lockdep issues with configfs |
605 | * built in mutexes and semaphores. This is safe as it is not possible |
606 | * to start a new load/unload operation till the current one is done. |
607 | */ |
608 | mutex_unlock(lock: &cscfg_mutex); |
609 | |
610 | /* create the configfs elements */ |
611 | err = cscfg_fs_register_cfgs_feats(config_descs, feat_descs); |
612 | mutex_lock(&cscfg_mutex); |
613 | |
614 | if (err) |
615 | goto err_clean_cfs; |
616 | |
617 | /* mark any new configs as available for activation */ |
618 | cscfg_set_configs_available(config_descs); |
619 | goto exit_unlock; |
620 | |
621 | err_clean_cfs: |
622 | /* cleanup after error registering with configfs */ |
623 | cscfg_fs_unregister_cfgs_feats(load_owner: owner_info); |
624 | |
625 | if (!list_is_singular(head: &cscfg_mgr->load_order_list)) |
626 | cscfg_owner_put(list_prev_entry(owner_info, item)); |
627 | |
628 | err_clean_owner_list: |
629 | list_del(entry: &owner_info->item); |
630 | |
631 | err_clean_load: |
632 | cscfg_unload_owned_cfgs_feats(load_owner: owner_info); |
633 | |
634 | exit_unlock: |
635 | cscfg_mgr->load_state = CSCFG_NONE; |
636 | mutex_unlock(lock: &cscfg_mutex); |
637 | return err; |
638 | } |
639 | EXPORT_SYMBOL_GPL(cscfg_load_config_sets); |
640 | |
641 | /** |
642 | * cscfg_unload_config_sets - unload a set of configurations by owner. |
643 | * |
644 | * Dynamic unload of configuration and feature sets is done on the basis of |
645 | * the load owner of that set. Later loaded configurations can depend on |
646 | * features loaded earlier. |
647 | * |
648 | * Therefore, unload is only possible if:- |
649 | * 1) no configurations are active. |
650 | * 2) the set being unloaded was the last to be loaded to maintain dependencies. |
651 | * |
652 | * Once the unload operation commences, we disallow any configuration being |
653 | * made active until it is complete. |
654 | * |
655 | * @owner_info: Information on owner for set being unloaded. |
656 | */ |
657 | int cscfg_unload_config_sets(struct cscfg_load_owner_info *owner_info) |
658 | { |
659 | int err = 0; |
660 | struct cscfg_load_owner_info *load_list_item = NULL; |
661 | |
662 | mutex_lock(&cscfg_mutex); |
663 | if (cscfg_mgr->load_state != CSCFG_NONE) { |
664 | mutex_unlock(lock: &cscfg_mutex); |
665 | return -EBUSY; |
666 | } |
667 | |
668 | /* unload op in progress also prevents activation of any config */ |
669 | cscfg_mgr->load_state = CSCFG_UNLOAD; |
670 | |
671 | /* cannot unload if anything is active */ |
672 | if (atomic_read(v: &cscfg_mgr->sys_active_cnt)) { |
673 | err = -EBUSY; |
674 | goto exit_unlock; |
675 | } |
676 | |
677 | /* cannot unload if not last loaded in load order */ |
678 | if (!list_empty(head: &cscfg_mgr->load_order_list)) { |
679 | load_list_item = list_last_entry(&cscfg_mgr->load_order_list, |
680 | struct cscfg_load_owner_info, item); |
681 | if (load_list_item != owner_info) |
682 | load_list_item = NULL; |
683 | } |
684 | |
685 | if (!load_list_item) { |
686 | err = -EINVAL; |
687 | goto exit_unlock; |
688 | } |
689 | |
690 | /* remove from configfs - again outside the scope of the list mutex */ |
691 | mutex_unlock(lock: &cscfg_mutex); |
692 | cscfg_fs_unregister_cfgs_feats(load_owner: owner_info); |
693 | mutex_lock(&cscfg_mutex); |
694 | |
695 | /* unload everything from lists belonging to load_owner */ |
696 | cscfg_unload_owned_cfgs_feats(load_owner: owner_info); |
697 | |
698 | /* remove from load order list */ |
699 | if (!list_is_singular(head: &cscfg_mgr->load_order_list)) { |
700 | /* unlock previous item in load order list */ |
701 | cscfg_owner_put(list_prev_entry(owner_info, item)); |
702 | } |
703 | list_del(entry: &owner_info->item); |
704 | |
705 | exit_unlock: |
706 | cscfg_mgr->load_state = CSCFG_NONE; |
707 | mutex_unlock(lock: &cscfg_mutex); |
708 | return err; |
709 | } |
710 | EXPORT_SYMBOL_GPL(cscfg_unload_config_sets); |
711 | |
712 | /* Handle coresight device registration and add configs and features to devices */ |
713 | |
714 | /* iterate through config lists and load matching configs to device */ |
715 | static int cscfg_add_cfgs_csdev(struct coresight_device *csdev) |
716 | { |
717 | struct cscfg_config_desc *config_desc; |
718 | int err = 0; |
719 | |
720 | list_for_each_entry(config_desc, &cscfg_mgr->config_desc_list, item) { |
721 | err = cscfg_add_csdev_cfg(csdev, config_desc); |
722 | if (err) |
723 | break; |
724 | } |
725 | return err; |
726 | } |
727 | |
728 | /* iterate through feature lists and load matching features to device */ |
729 | static int cscfg_add_feats_csdev(struct coresight_device *csdev, |
730 | u32 match_flags, |
731 | struct cscfg_csdev_feat_ops *ops) |
732 | { |
733 | struct cscfg_feature_desc *feat_desc; |
734 | int err = 0; |
735 | |
736 | if (!ops->load_feat) |
737 | return -EINVAL; |
738 | |
739 | list_for_each_entry(feat_desc, &cscfg_mgr->feat_desc_list, item) { |
740 | if (feat_desc->match_flags & match_flags) { |
741 | err = cscfg_load_feat_csdev(csdev, feat_desc, ops); |
742 | if (err) |
743 | break; |
744 | } |
745 | } |
746 | return err; |
747 | } |
748 | |
749 | /* Add coresight device to list and copy its matching info */ |
750 | static int cscfg_list_add_csdev(struct coresight_device *csdev, |
751 | u32 match_flags, |
752 | struct cscfg_csdev_feat_ops *ops) |
753 | { |
754 | struct cscfg_registered_csdev *csdev_item; |
755 | |
756 | /* allocate the list entry structure */ |
757 | csdev_item = kzalloc(size: sizeof(struct cscfg_registered_csdev), GFP_KERNEL); |
758 | if (!csdev_item) |
759 | return -ENOMEM; |
760 | |
761 | csdev_item->csdev = csdev; |
762 | csdev_item->match_flags = match_flags; |
763 | csdev_item->ops.load_feat = ops->load_feat; |
764 | list_add(new: &csdev_item->item, head: &cscfg_mgr->csdev_desc_list); |
765 | |
766 | INIT_LIST_HEAD(list: &csdev->feature_csdev_list); |
767 | INIT_LIST_HEAD(list: &csdev->config_csdev_list); |
768 | spin_lock_init(&csdev->cscfg_csdev_lock); |
769 | |
770 | return 0; |
771 | } |
772 | |
773 | /* remove a coresight device from the list and free data */ |
774 | static void cscfg_list_remove_csdev(struct coresight_device *csdev) |
775 | { |
776 | struct cscfg_registered_csdev *csdev_item, *tmp; |
777 | |
778 | list_for_each_entry_safe(csdev_item, tmp, &cscfg_mgr->csdev_desc_list, item) { |
779 | if (csdev_item->csdev == csdev) { |
780 | list_del(entry: &csdev_item->item); |
781 | kfree(objp: csdev_item); |
782 | break; |
783 | } |
784 | } |
785 | } |
786 | |
787 | /** |
788 | * cscfg_register_csdev - register a coresight device with the syscfg manager. |
789 | * |
790 | * Registers the coresight device with the system. @match_flags used to check |
791 | * if the device is a match for registered features. Any currently registered |
792 | * configurations and features that match the device will be loaded onto it. |
793 | * |
794 | * @csdev: The coresight device to register. |
795 | * @match_flags: Matching information to load features. |
796 | * @ops: Standard operations supported by the device. |
797 | */ |
798 | int cscfg_register_csdev(struct coresight_device *csdev, |
799 | u32 match_flags, |
800 | struct cscfg_csdev_feat_ops *ops) |
801 | { |
802 | int ret = 0; |
803 | |
804 | mutex_lock(&cscfg_mutex); |
805 | |
806 | /* add device to list of registered devices */ |
807 | ret = cscfg_list_add_csdev(csdev, match_flags, ops); |
808 | if (ret) |
809 | goto reg_csdev_unlock; |
810 | |
811 | /* now load any registered features and configs matching the device. */ |
812 | ret = cscfg_add_feats_csdev(csdev, match_flags, ops); |
813 | if (ret) { |
814 | cscfg_list_remove_csdev(csdev); |
815 | goto reg_csdev_unlock; |
816 | } |
817 | |
818 | ret = cscfg_add_cfgs_csdev(csdev); |
819 | if (ret) { |
820 | cscfg_list_remove_csdev(csdev); |
821 | goto reg_csdev_unlock; |
822 | } |
823 | |
824 | pr_info("CSCFG registered %s" , dev_name(&csdev->dev)); |
825 | |
826 | reg_csdev_unlock: |
827 | mutex_unlock(lock: &cscfg_mutex); |
828 | return ret; |
829 | } |
830 | EXPORT_SYMBOL_GPL(cscfg_register_csdev); |
831 | |
832 | /** |
833 | * cscfg_unregister_csdev - remove coresight device from syscfg manager. |
834 | * |
835 | * @csdev: Device to remove. |
836 | */ |
837 | void cscfg_unregister_csdev(struct coresight_device *csdev) |
838 | { |
839 | mutex_lock(&cscfg_mutex); |
840 | cscfg_list_remove_csdev(csdev); |
841 | mutex_unlock(lock: &cscfg_mutex); |
842 | } |
843 | EXPORT_SYMBOL_GPL(cscfg_unregister_csdev); |
844 | |
845 | /** |
846 | * cscfg_csdev_reset_feats - reset features for a CoreSight device. |
847 | * |
848 | * Resets all parameters and register values for any features loaded |
849 | * into @csdev to their default values. |
850 | * |
851 | * @csdev: The CoreSight device. |
852 | */ |
853 | void cscfg_csdev_reset_feats(struct coresight_device *csdev) |
854 | { |
855 | struct cscfg_feature_csdev *feat_csdev; |
856 | unsigned long flags; |
857 | |
858 | spin_lock_irqsave(&csdev->cscfg_csdev_lock, flags); |
859 | if (list_empty(head: &csdev->feature_csdev_list)) |
860 | goto unlock_exit; |
861 | |
862 | list_for_each_entry(feat_csdev, &csdev->feature_csdev_list, node) |
863 | cscfg_reset_feat(feat_csdev); |
864 | |
865 | unlock_exit: |
866 | spin_unlock_irqrestore(lock: &csdev->cscfg_csdev_lock, flags); |
867 | } |
868 | EXPORT_SYMBOL_GPL(cscfg_csdev_reset_feats); |
869 | |
870 | /* |
871 | * This activate configuration for either perf or sysfs. Perf can have multiple |
872 | * active configs, selected per event, sysfs is limited to one. |
873 | * |
874 | * Increments the configuration descriptor active count and the global active |
875 | * count. |
876 | * |
877 | * @cfg_hash: Hash value of the selected configuration name. |
878 | */ |
879 | static int _cscfg_activate_config(unsigned long cfg_hash) |
880 | { |
881 | struct cscfg_config_desc *config_desc; |
882 | int err = -EINVAL; |
883 | |
884 | if (cscfg_mgr->load_state == CSCFG_UNLOAD) |
885 | return -EBUSY; |
886 | |
887 | list_for_each_entry(config_desc, &cscfg_mgr->config_desc_list, item) { |
888 | if ((unsigned long)config_desc->event_ea->var == cfg_hash) { |
889 | /* if we happen upon a partly loaded config, can't use it */ |
890 | if (config_desc->available == false) |
891 | return -EBUSY; |
892 | |
893 | /* must ensure that config cannot be unloaded in use */ |
894 | err = cscfg_owner_get(owner_info: config_desc->load_owner); |
895 | if (err) |
896 | break; |
897 | /* |
898 | * increment the global active count - control changes to |
899 | * active configurations |
900 | */ |
901 | atomic_inc(v: &cscfg_mgr->sys_active_cnt); |
902 | |
903 | /* |
904 | * mark the descriptor as active so enable config on a |
905 | * device instance will use it |
906 | */ |
907 | atomic_inc(v: &config_desc->active_cnt); |
908 | |
909 | err = 0; |
910 | dev_dbg(cscfg_device(), "Activate config %s.\n" , config_desc->name); |
911 | break; |
912 | } |
913 | } |
914 | return err; |
915 | } |
916 | |
917 | static void _cscfg_deactivate_config(unsigned long cfg_hash) |
918 | { |
919 | struct cscfg_config_desc *config_desc; |
920 | |
921 | list_for_each_entry(config_desc, &cscfg_mgr->config_desc_list, item) { |
922 | if ((unsigned long)config_desc->event_ea->var == cfg_hash) { |
923 | atomic_dec(v: &config_desc->active_cnt); |
924 | atomic_dec(v: &cscfg_mgr->sys_active_cnt); |
925 | cscfg_owner_put(owner_info: config_desc->load_owner); |
926 | dev_dbg(cscfg_device(), "Deactivate config %s.\n" , config_desc->name); |
927 | break; |
928 | } |
929 | } |
930 | } |
931 | |
932 | /* |
933 | * called from configfs to set/clear the active configuration for use when |
934 | * using sysfs to control trace. |
935 | */ |
936 | int cscfg_config_sysfs_activate(struct cscfg_config_desc *config_desc, bool activate) |
937 | { |
938 | unsigned long cfg_hash; |
939 | int err = 0; |
940 | |
941 | mutex_lock(&cscfg_mutex); |
942 | |
943 | cfg_hash = (unsigned long)config_desc->event_ea->var; |
944 | |
945 | if (activate) { |
946 | /* cannot be a current active value to activate this */ |
947 | if (cscfg_mgr->sysfs_active_config) { |
948 | err = -EBUSY; |
949 | goto exit_unlock; |
950 | } |
951 | err = _cscfg_activate_config(cfg_hash); |
952 | if (!err) |
953 | cscfg_mgr->sysfs_active_config = cfg_hash; |
954 | } else { |
955 | /* disable if matching current value */ |
956 | if (cscfg_mgr->sysfs_active_config == cfg_hash) { |
957 | _cscfg_deactivate_config(cfg_hash); |
958 | cscfg_mgr->sysfs_active_config = 0; |
959 | } else |
960 | err = -EINVAL; |
961 | } |
962 | |
963 | exit_unlock: |
964 | mutex_unlock(lock: &cscfg_mutex); |
965 | return err; |
966 | } |
967 | |
968 | /* set the sysfs preset value */ |
969 | void cscfg_config_sysfs_set_preset(int preset) |
970 | { |
971 | mutex_lock(&cscfg_mutex); |
972 | cscfg_mgr->sysfs_active_preset = preset; |
973 | mutex_unlock(lock: &cscfg_mutex); |
974 | } |
975 | |
976 | /* |
977 | * Used by a device to get the config and preset selected as active in configfs, |
978 | * when using sysfs to control trace. |
979 | */ |
980 | void cscfg_config_sysfs_get_active_cfg(unsigned long *cfg_hash, int *preset) |
981 | { |
982 | mutex_lock(&cscfg_mutex); |
983 | *preset = cscfg_mgr->sysfs_active_preset; |
984 | *cfg_hash = cscfg_mgr->sysfs_active_config; |
985 | mutex_unlock(lock: &cscfg_mutex); |
986 | } |
987 | EXPORT_SYMBOL_GPL(cscfg_config_sysfs_get_active_cfg); |
988 | |
989 | /** |
990 | * cscfg_activate_config - Mark a configuration descriptor as active. |
991 | * |
992 | * This will be seen when csdev devices are enabled in the system. |
993 | * Only activated configurations can be enabled on individual devices. |
994 | * Activation protects the configuration from alteration or removal while |
995 | * active. |
996 | * |
997 | * Selection by hash value - generated from the configuration name when it |
998 | * was loaded and added to the cs_etm/configurations file system for selection |
999 | * by perf. |
1000 | * |
1001 | * @cfg_hash: Hash value of the selected configuration name. |
1002 | */ |
1003 | int cscfg_activate_config(unsigned long cfg_hash) |
1004 | { |
1005 | int err = 0; |
1006 | |
1007 | mutex_lock(&cscfg_mutex); |
1008 | err = _cscfg_activate_config(cfg_hash); |
1009 | mutex_unlock(lock: &cscfg_mutex); |
1010 | |
1011 | return err; |
1012 | } |
1013 | EXPORT_SYMBOL_GPL(cscfg_activate_config); |
1014 | |
1015 | /** |
1016 | * cscfg_deactivate_config - Mark a config descriptor as inactive. |
1017 | * |
1018 | * Decrement the configuration and global active counts. |
1019 | * |
1020 | * @cfg_hash: Hash value of the selected configuration name. |
1021 | */ |
1022 | void cscfg_deactivate_config(unsigned long cfg_hash) |
1023 | { |
1024 | mutex_lock(&cscfg_mutex); |
1025 | _cscfg_deactivate_config(cfg_hash); |
1026 | mutex_unlock(lock: &cscfg_mutex); |
1027 | } |
1028 | EXPORT_SYMBOL_GPL(cscfg_deactivate_config); |
1029 | |
1030 | /** |
1031 | * cscfg_csdev_enable_active_config - Enable matching active configuration for device. |
1032 | * |
1033 | * Enables the configuration selected by @cfg_hash if the configuration is supported |
1034 | * on the device and has been activated. |
1035 | * |
1036 | * If active and supported the CoreSight device @csdev will be programmed with the |
1037 | * configuration, using @preset parameters. |
1038 | * |
1039 | * Should be called before driver hardware enable for the requested device, prior to |
1040 | * programming and enabling the physical hardware. |
1041 | * |
1042 | * @csdev: CoreSight device to program. |
1043 | * @cfg_hash: Selector for the configuration. |
1044 | * @preset: Preset parameter values to use, 0 for current / default values. |
1045 | */ |
1046 | int cscfg_csdev_enable_active_config(struct coresight_device *csdev, |
1047 | unsigned long cfg_hash, int preset) |
1048 | { |
1049 | struct cscfg_config_csdev *config_csdev_active = NULL, *config_csdev_item; |
1050 | const struct cscfg_config_desc *config_desc; |
1051 | unsigned long flags; |
1052 | int err = 0; |
1053 | |
1054 | /* quickly check global count */ |
1055 | if (!atomic_read(v: &cscfg_mgr->sys_active_cnt)) |
1056 | return 0; |
1057 | |
1058 | /* |
1059 | * Look for matching configuration - set the active configuration |
1060 | * context if found. |
1061 | */ |
1062 | spin_lock_irqsave(&csdev->cscfg_csdev_lock, flags); |
1063 | list_for_each_entry(config_csdev_item, &csdev->config_csdev_list, node) { |
1064 | config_desc = config_csdev_item->config_desc; |
1065 | if ((atomic_read(v: &config_desc->active_cnt)) && |
1066 | ((unsigned long)config_desc->event_ea->var == cfg_hash)) { |
1067 | config_csdev_active = config_csdev_item; |
1068 | csdev->active_cscfg_ctxt = (void *)config_csdev_active; |
1069 | break; |
1070 | } |
1071 | } |
1072 | spin_unlock_irqrestore(lock: &csdev->cscfg_csdev_lock, flags); |
1073 | |
1074 | /* |
1075 | * If found, attempt to enable |
1076 | */ |
1077 | if (config_csdev_active) { |
1078 | /* |
1079 | * Call the generic routine that will program up the internal |
1080 | * driver structures prior to programming up the hardware. |
1081 | * This routine takes the driver spinlock saved in the configs. |
1082 | */ |
1083 | err = cscfg_csdev_enable_config(config_csdev: config_csdev_active, preset); |
1084 | if (!err) { |
1085 | /* |
1086 | * Successful programming. Check the active_cscfg_ctxt |
1087 | * pointer to ensure no pre-emption disabled it via |
1088 | * cscfg_csdev_disable_active_config() before |
1089 | * we could start. |
1090 | * |
1091 | * Set enabled if OK, err if not. |
1092 | */ |
1093 | spin_lock_irqsave(&csdev->cscfg_csdev_lock, flags); |
1094 | if (csdev->active_cscfg_ctxt) |
1095 | config_csdev_active->enabled = true; |
1096 | else |
1097 | err = -EBUSY; |
1098 | spin_unlock_irqrestore(lock: &csdev->cscfg_csdev_lock, flags); |
1099 | } |
1100 | } |
1101 | return err; |
1102 | } |
1103 | EXPORT_SYMBOL_GPL(cscfg_csdev_enable_active_config); |
1104 | |
1105 | /** |
1106 | * cscfg_csdev_disable_active_config - disable an active config on the device. |
1107 | * |
1108 | * Disables the active configuration on the CoreSight device @csdev. |
1109 | * Disable will save the values of any registers marked in the configurations |
1110 | * as save on disable. |
1111 | * |
1112 | * Should be called after driver hardware disable for the requested device, |
1113 | * after disabling the physical hardware and reading back registers. |
1114 | * |
1115 | * @csdev: The CoreSight device. |
1116 | */ |
1117 | void cscfg_csdev_disable_active_config(struct coresight_device *csdev) |
1118 | { |
1119 | struct cscfg_config_csdev *config_csdev; |
1120 | unsigned long flags; |
1121 | |
1122 | /* |
1123 | * Check if we have an active config, and that it was successfully enabled. |
1124 | * If it was not enabled, we have no work to do, otherwise mark as disabled. |
1125 | * Clear the active config pointer. |
1126 | */ |
1127 | spin_lock_irqsave(&csdev->cscfg_csdev_lock, flags); |
1128 | config_csdev = (struct cscfg_config_csdev *)csdev->active_cscfg_ctxt; |
1129 | if (config_csdev) { |
1130 | if (!config_csdev->enabled) |
1131 | config_csdev = NULL; |
1132 | else |
1133 | config_csdev->enabled = false; |
1134 | } |
1135 | csdev->active_cscfg_ctxt = NULL; |
1136 | spin_unlock_irqrestore(lock: &csdev->cscfg_csdev_lock, flags); |
1137 | |
1138 | /* true if there was an enabled active config */ |
1139 | if (config_csdev) |
1140 | cscfg_csdev_disable_config(config_csdev); |
1141 | } |
1142 | EXPORT_SYMBOL_GPL(cscfg_csdev_disable_active_config); |
1143 | |
1144 | /* Initialise system configuration management device. */ |
1145 | |
1146 | struct device *cscfg_device(void) |
1147 | { |
1148 | return cscfg_mgr ? &cscfg_mgr->dev : NULL; |
1149 | } |
1150 | |
1151 | /* Must have a release function or the kernel will complain on module unload */ |
1152 | static void cscfg_dev_release(struct device *dev) |
1153 | { |
1154 | mutex_lock(&cscfg_mutex); |
1155 | kfree(objp: cscfg_mgr); |
1156 | cscfg_mgr = NULL; |
1157 | mutex_unlock(lock: &cscfg_mutex); |
1158 | } |
1159 | |
1160 | /* a device is needed to "own" some kernel elements such as sysfs entries. */ |
1161 | static int cscfg_create_device(void) |
1162 | { |
1163 | struct device *dev; |
1164 | int err = -ENOMEM; |
1165 | |
1166 | mutex_lock(&cscfg_mutex); |
1167 | if (cscfg_mgr) { |
1168 | err = -EINVAL; |
1169 | goto create_dev_exit_unlock; |
1170 | } |
1171 | |
1172 | cscfg_mgr = kzalloc(size: sizeof(struct cscfg_manager), GFP_KERNEL); |
1173 | if (!cscfg_mgr) |
1174 | goto create_dev_exit_unlock; |
1175 | |
1176 | /* initialise the cscfg_mgr structure */ |
1177 | INIT_LIST_HEAD(list: &cscfg_mgr->csdev_desc_list); |
1178 | INIT_LIST_HEAD(list: &cscfg_mgr->feat_desc_list); |
1179 | INIT_LIST_HEAD(list: &cscfg_mgr->config_desc_list); |
1180 | INIT_LIST_HEAD(list: &cscfg_mgr->load_order_list); |
1181 | atomic_set(v: &cscfg_mgr->sys_active_cnt, i: 0); |
1182 | cscfg_mgr->load_state = CSCFG_NONE; |
1183 | |
1184 | /* setup the device */ |
1185 | dev = cscfg_device(); |
1186 | dev->release = cscfg_dev_release; |
1187 | dev->init_name = "cs_system_cfg" ; |
1188 | |
1189 | err = device_register(dev); |
1190 | if (err) |
1191 | put_device(dev); |
1192 | |
1193 | create_dev_exit_unlock: |
1194 | mutex_unlock(lock: &cscfg_mutex); |
1195 | return err; |
1196 | } |
1197 | |
1198 | /* |
1199 | * Loading and unloading is generally on user discretion. |
1200 | * If exiting due to coresight module unload, we need to unload any configurations that remain, |
1201 | * before we unregister the configfs intrastructure. |
1202 | * |
1203 | * Do this by walking the load_owner list and taking appropriate action, depending on the load |
1204 | * owner type. |
1205 | */ |
1206 | static void cscfg_unload_cfgs_on_exit(void) |
1207 | { |
1208 | struct cscfg_load_owner_info *owner_info = NULL; |
1209 | |
1210 | /* |
1211 | * grab the mutex - even though we are exiting, some configfs files |
1212 | * may still be live till we dump them, so ensure list data is |
1213 | * protected from a race condition. |
1214 | */ |
1215 | mutex_lock(&cscfg_mutex); |
1216 | while (!list_empty(head: &cscfg_mgr->load_order_list)) { |
1217 | |
1218 | /* remove in reverse order of loading */ |
1219 | owner_info = list_last_entry(&cscfg_mgr->load_order_list, |
1220 | struct cscfg_load_owner_info, item); |
1221 | |
1222 | /* action according to type */ |
1223 | switch (owner_info->type) { |
1224 | case CSCFG_OWNER_PRELOAD: |
1225 | /* |
1226 | * preloaded descriptors are statically allocated in |
1227 | * this module - just need to unload dynamic items from |
1228 | * csdev lists, and remove from configfs directories. |
1229 | */ |
1230 | pr_info("cscfg: unloading preloaded configurations\n" ); |
1231 | break; |
1232 | |
1233 | case CSCFG_OWNER_MODULE: |
1234 | /* |
1235 | * this is an error - the loadable module must have been unloaded prior |
1236 | * to the coresight module unload. Therefore that module has not |
1237 | * correctly unloaded configs in its own exit code. |
1238 | * Nothing to do other than emit an error string as the static descriptor |
1239 | * references we need to unload will have disappeared with the module. |
1240 | */ |
1241 | pr_err("cscfg: ERROR: prior module failed to unload configuration\n" ); |
1242 | goto list_remove; |
1243 | } |
1244 | |
1245 | /* remove from configfs - outside the scope of the list mutex */ |
1246 | mutex_unlock(lock: &cscfg_mutex); |
1247 | cscfg_fs_unregister_cfgs_feats(load_owner: owner_info); |
1248 | mutex_lock(&cscfg_mutex); |
1249 | |
1250 | /* Next unload from csdev lists. */ |
1251 | cscfg_unload_owned_cfgs_feats(load_owner: owner_info); |
1252 | |
1253 | list_remove: |
1254 | /* remove from load order list */ |
1255 | list_del(entry: &owner_info->item); |
1256 | } |
1257 | mutex_unlock(lock: &cscfg_mutex); |
1258 | } |
1259 | |
1260 | static void cscfg_clear_device(void) |
1261 | { |
1262 | cscfg_unload_cfgs_on_exit(); |
1263 | cscfg_configfs_release(cscfg_mgr); |
1264 | device_unregister(dev: cscfg_device()); |
1265 | } |
1266 | |
1267 | /* Initialise system config management API device */ |
1268 | int __init cscfg_init(void) |
1269 | { |
1270 | int err = 0; |
1271 | |
1272 | /* create the device and init cscfg_mgr */ |
1273 | err = cscfg_create_device(); |
1274 | if (err) |
1275 | return err; |
1276 | |
1277 | /* initialise configfs subsystem */ |
1278 | err = cscfg_configfs_init(cscfg_mgr); |
1279 | if (err) |
1280 | goto exit_err; |
1281 | |
1282 | /* preload built-in configurations */ |
1283 | err = cscfg_preload(THIS_MODULE); |
1284 | if (err) |
1285 | goto exit_err; |
1286 | |
1287 | dev_info(cscfg_device(), "CoreSight Configuration manager initialised" ); |
1288 | return 0; |
1289 | |
1290 | exit_err: |
1291 | cscfg_clear_device(); |
1292 | return err; |
1293 | } |
1294 | |
1295 | void cscfg_exit(void) |
1296 | { |
1297 | cscfg_clear_device(); |
1298 | } |
1299 | |