1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * System Trace Module (STM) master/channel allocation policy management |
4 | * Copyright (c) 2014, Intel Corporation. |
5 | * |
6 | * A master/channel allocation policy allows mapping string identifiers to |
7 | * master and channel ranges, where allocation can be done. |
8 | */ |
9 | |
10 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
11 | |
12 | #include <linux/types.h> |
13 | #include <linux/module.h> |
14 | #include <linux/device.h> |
15 | #include <linux/configfs.h> |
16 | #include <linux/slab.h> |
17 | #include <linux/stm.h> |
18 | #include "stm.h" |
19 | |
20 | /* |
21 | * STP Master/Channel allocation policy configfs layout. |
22 | */ |
23 | |
24 | struct stp_policy { |
25 | struct config_group group; |
26 | struct stm_device *stm; |
27 | }; |
28 | |
29 | struct stp_policy_node { |
30 | struct config_group group; |
31 | struct stp_policy *policy; |
32 | unsigned int first_master; |
33 | unsigned int last_master; |
34 | unsigned int first_channel; |
35 | unsigned int last_channel; |
36 | /* this is the one that's exposed to the attributes */ |
37 | unsigned char priv[]; |
38 | }; |
39 | |
40 | void *stp_policy_node_priv(struct stp_policy_node *pn) |
41 | { |
42 | if (!pn) |
43 | return NULL; |
44 | |
45 | return pn->priv; |
46 | } |
47 | |
48 | static struct configfs_subsystem stp_policy_subsys; |
49 | |
50 | void stp_policy_node_get_ranges(struct stp_policy_node *policy_node, |
51 | unsigned int *mstart, unsigned int *mend, |
52 | unsigned int *cstart, unsigned int *cend) |
53 | { |
54 | *mstart = policy_node->first_master; |
55 | *mend = policy_node->last_master; |
56 | *cstart = policy_node->first_channel; |
57 | *cend = policy_node->last_channel; |
58 | } |
59 | |
60 | static inline struct stp_policy *to_stp_policy(struct config_item *item) |
61 | { |
62 | return item ? |
63 | container_of(to_config_group(item), struct stp_policy, group) : |
64 | NULL; |
65 | } |
66 | |
67 | static inline struct stp_policy_node * |
68 | to_stp_policy_node(struct config_item *item) |
69 | { |
70 | return item ? |
71 | container_of(to_config_group(item), struct stp_policy_node, |
72 | group) : |
73 | NULL; |
74 | } |
75 | |
76 | void *to_pdrv_policy_node(struct config_item *item) |
77 | { |
78 | struct stp_policy_node *node = to_stp_policy_node(item); |
79 | |
80 | return stp_policy_node_priv(pn: node); |
81 | } |
82 | EXPORT_SYMBOL_GPL(to_pdrv_policy_node); |
83 | |
84 | static ssize_t |
85 | stp_policy_node_masters_show(struct config_item *item, char *page) |
86 | { |
87 | struct stp_policy_node *policy_node = to_stp_policy_node(item); |
88 | ssize_t count; |
89 | |
90 | count = sprintf(buf: page, fmt: "%u %u\n" , policy_node->first_master, |
91 | policy_node->last_master); |
92 | |
93 | return count; |
94 | } |
95 | |
96 | static ssize_t |
97 | stp_policy_node_masters_store(struct config_item *item, const char *page, |
98 | size_t count) |
99 | { |
100 | struct stp_policy_node *policy_node = to_stp_policy_node(item); |
101 | unsigned int first, last; |
102 | struct stm_device *stm; |
103 | char *p = (char *)page; |
104 | ssize_t ret = -ENODEV; |
105 | |
106 | if (sscanf(p, "%u %u" , &first, &last) != 2) |
107 | return -EINVAL; |
108 | |
109 | mutex_lock(&stp_policy_subsys.su_mutex); |
110 | stm = policy_node->policy->stm; |
111 | if (!stm) |
112 | goto unlock; |
113 | |
114 | /* must be within [sw_start..sw_end], which is an inclusive range */ |
115 | if (first > last || first < stm->data->sw_start || |
116 | last > stm->data->sw_end) { |
117 | ret = -ERANGE; |
118 | goto unlock; |
119 | } |
120 | |
121 | ret = count; |
122 | policy_node->first_master = first; |
123 | policy_node->last_master = last; |
124 | |
125 | unlock: |
126 | mutex_unlock(lock: &stp_policy_subsys.su_mutex); |
127 | |
128 | return ret; |
129 | } |
130 | |
131 | static ssize_t |
132 | stp_policy_node_channels_show(struct config_item *item, char *page) |
133 | { |
134 | struct stp_policy_node *policy_node = to_stp_policy_node(item); |
135 | ssize_t count; |
136 | |
137 | count = sprintf(buf: page, fmt: "%u %u\n" , policy_node->first_channel, |
138 | policy_node->last_channel); |
139 | |
140 | return count; |
141 | } |
142 | |
143 | static ssize_t |
144 | stp_policy_node_channels_store(struct config_item *item, const char *page, |
145 | size_t count) |
146 | { |
147 | struct stp_policy_node *policy_node = to_stp_policy_node(item); |
148 | unsigned int first, last; |
149 | struct stm_device *stm; |
150 | char *p = (char *)page; |
151 | ssize_t ret = -ENODEV; |
152 | |
153 | if (sscanf(p, "%u %u" , &first, &last) != 2) |
154 | return -EINVAL; |
155 | |
156 | mutex_lock(&stp_policy_subsys.su_mutex); |
157 | stm = policy_node->policy->stm; |
158 | if (!stm) |
159 | goto unlock; |
160 | |
161 | if (first > INT_MAX || last > INT_MAX || first > last || |
162 | last >= stm->data->sw_nchannels) { |
163 | ret = -ERANGE; |
164 | goto unlock; |
165 | } |
166 | |
167 | ret = count; |
168 | policy_node->first_channel = first; |
169 | policy_node->last_channel = last; |
170 | |
171 | unlock: |
172 | mutex_unlock(lock: &stp_policy_subsys.su_mutex); |
173 | |
174 | return ret; |
175 | } |
176 | |
177 | static void stp_policy_node_release(struct config_item *item) |
178 | { |
179 | struct stp_policy_node *node = to_stp_policy_node(item); |
180 | |
181 | kfree(objp: node); |
182 | } |
183 | |
184 | static struct configfs_item_operations stp_policy_node_item_ops = { |
185 | .release = stp_policy_node_release, |
186 | }; |
187 | |
188 | CONFIGFS_ATTR(stp_policy_node_, masters); |
189 | CONFIGFS_ATTR(stp_policy_node_, channels); |
190 | |
191 | static struct configfs_attribute *stp_policy_node_attrs[] = { |
192 | &stp_policy_node_attr_masters, |
193 | &stp_policy_node_attr_channels, |
194 | NULL, |
195 | }; |
196 | |
197 | static const struct config_item_type stp_policy_type; |
198 | static const struct config_item_type stp_policy_node_type; |
199 | |
200 | const struct config_item_type * |
201 | get_policy_node_type(struct configfs_attribute **attrs) |
202 | { |
203 | struct config_item_type *type; |
204 | struct configfs_attribute **merged; |
205 | |
206 | type = kmemdup(p: &stp_policy_node_type, size: sizeof(stp_policy_node_type), |
207 | GFP_KERNEL); |
208 | if (!type) |
209 | return NULL; |
210 | |
211 | merged = memcat_p(stp_policy_node_attrs, attrs); |
212 | if (!merged) { |
213 | kfree(objp: type); |
214 | return NULL; |
215 | } |
216 | |
217 | type->ct_attrs = merged; |
218 | |
219 | return type; |
220 | } |
221 | |
222 | static struct config_group * |
223 | stp_policy_node_make(struct config_group *group, const char *name) |
224 | { |
225 | const struct config_item_type *type = &stp_policy_node_type; |
226 | struct stp_policy_node *policy_node, *parent_node; |
227 | const struct stm_protocol_driver *pdrv; |
228 | struct stp_policy *policy; |
229 | |
230 | if (group->cg_item.ci_type == &stp_policy_type) { |
231 | policy = container_of(group, struct stp_policy, group); |
232 | } else { |
233 | parent_node = container_of(group, struct stp_policy_node, |
234 | group); |
235 | policy = parent_node->policy; |
236 | } |
237 | |
238 | if (!policy->stm) |
239 | return ERR_PTR(error: -ENODEV); |
240 | |
241 | pdrv = policy->stm->pdrv; |
242 | policy_node = |
243 | kzalloc(offsetof(struct stp_policy_node, priv[pdrv->priv_sz]), |
244 | GFP_KERNEL); |
245 | if (!policy_node) |
246 | return ERR_PTR(error: -ENOMEM); |
247 | |
248 | if (pdrv->policy_node_init) |
249 | pdrv->policy_node_init((void *)policy_node->priv); |
250 | |
251 | if (policy->stm->pdrv_node_type) |
252 | type = policy->stm->pdrv_node_type; |
253 | |
254 | config_group_init_type_name(group: &policy_node->group, name, type); |
255 | |
256 | policy_node->policy = policy; |
257 | |
258 | /* default values for the attributes */ |
259 | policy_node->first_master = policy->stm->data->sw_start; |
260 | policy_node->last_master = policy->stm->data->sw_end; |
261 | policy_node->first_channel = 0; |
262 | policy_node->last_channel = policy->stm->data->sw_nchannels - 1; |
263 | |
264 | return &policy_node->group; |
265 | } |
266 | |
267 | static void |
268 | stp_policy_node_drop(struct config_group *group, struct config_item *item) |
269 | { |
270 | config_item_put(item); |
271 | } |
272 | |
273 | static struct configfs_group_operations stp_policy_node_group_ops = { |
274 | .make_group = stp_policy_node_make, |
275 | .drop_item = stp_policy_node_drop, |
276 | }; |
277 | |
278 | static const struct config_item_type stp_policy_node_type = { |
279 | .ct_item_ops = &stp_policy_node_item_ops, |
280 | .ct_group_ops = &stp_policy_node_group_ops, |
281 | .ct_attrs = stp_policy_node_attrs, |
282 | .ct_owner = THIS_MODULE, |
283 | }; |
284 | |
285 | /* |
286 | * Root group: policies. |
287 | */ |
288 | static ssize_t stp_policy_device_show(struct config_item *item, |
289 | char *page) |
290 | { |
291 | struct stp_policy *policy = to_stp_policy(item); |
292 | ssize_t count; |
293 | |
294 | count = sprintf(buf: page, fmt: "%s\n" , |
295 | (policy && policy->stm) ? |
296 | policy->stm->data->name : |
297 | "<none>" ); |
298 | |
299 | return count; |
300 | } |
301 | |
302 | CONFIGFS_ATTR_RO(stp_policy_, device); |
303 | |
304 | static ssize_t stp_policy_protocol_show(struct config_item *item, |
305 | char *page) |
306 | { |
307 | struct stp_policy *policy = to_stp_policy(item); |
308 | ssize_t count; |
309 | |
310 | count = sprintf(buf: page, fmt: "%s\n" , |
311 | (policy && policy->stm) ? |
312 | policy->stm->pdrv->name : |
313 | "<none>" ); |
314 | |
315 | return count; |
316 | } |
317 | |
318 | CONFIGFS_ATTR_RO(stp_policy_, protocol); |
319 | |
320 | static struct configfs_attribute *stp_policy_attrs[] = { |
321 | &stp_policy_attr_device, |
322 | &stp_policy_attr_protocol, |
323 | NULL, |
324 | }; |
325 | |
326 | void stp_policy_unbind(struct stp_policy *policy) |
327 | { |
328 | struct stm_device *stm = policy->stm; |
329 | |
330 | /* |
331 | * stp_policy_release() will not call here if the policy is already |
332 | * unbound; other users should not either, as no link exists between |
333 | * this policy and anything else in that case |
334 | */ |
335 | if (WARN_ON_ONCE(!policy->stm)) |
336 | return; |
337 | |
338 | lockdep_assert_held(&stm->policy_mutex); |
339 | |
340 | stm->policy = NULL; |
341 | policy->stm = NULL; |
342 | |
343 | /* |
344 | * Drop the reference on the protocol driver and lose the link. |
345 | */ |
346 | stm_put_protocol(pdrv: stm->pdrv); |
347 | stm->pdrv = NULL; |
348 | stm_put_device(stm); |
349 | } |
350 | |
351 | static void stp_policy_release(struct config_item *item) |
352 | { |
353 | struct stp_policy *policy = to_stp_policy(item); |
354 | struct stm_device *stm = policy->stm; |
355 | |
356 | /* a policy *can* be unbound and still exist in configfs tree */ |
357 | if (!stm) |
358 | return; |
359 | |
360 | mutex_lock(&stm->policy_mutex); |
361 | stp_policy_unbind(policy); |
362 | mutex_unlock(lock: &stm->policy_mutex); |
363 | |
364 | kfree(objp: policy); |
365 | } |
366 | |
367 | static struct configfs_item_operations stp_policy_item_ops = { |
368 | .release = stp_policy_release, |
369 | }; |
370 | |
371 | static struct configfs_group_operations stp_policy_group_ops = { |
372 | .make_group = stp_policy_node_make, |
373 | }; |
374 | |
375 | static const struct config_item_type stp_policy_type = { |
376 | .ct_item_ops = &stp_policy_item_ops, |
377 | .ct_group_ops = &stp_policy_group_ops, |
378 | .ct_attrs = stp_policy_attrs, |
379 | .ct_owner = THIS_MODULE, |
380 | }; |
381 | |
382 | static struct config_group * |
383 | stp_policy_make(struct config_group *group, const char *name) |
384 | { |
385 | const struct config_item_type *pdrv_node_type; |
386 | const struct stm_protocol_driver *pdrv; |
387 | char *devname, *proto, *p; |
388 | struct config_group *ret; |
389 | struct stm_device *stm; |
390 | int err; |
391 | |
392 | devname = kasprintf(GFP_KERNEL, fmt: "%s" , name); |
393 | if (!devname) |
394 | return ERR_PTR(error: -ENOMEM); |
395 | |
396 | /* |
397 | * node must look like <device_name>.<policy_name>, where |
398 | * <device_name> is the name of an existing stm device; may |
399 | * contain dots; |
400 | * <policy_name> is an arbitrary string; may not contain dots |
401 | * <device_name>:<protocol_name>.<policy_name> |
402 | */ |
403 | p = strrchr(devname, '.'); |
404 | if (!p) { |
405 | kfree(objp: devname); |
406 | return ERR_PTR(error: -EINVAL); |
407 | } |
408 | |
409 | *p = '\0'; |
410 | |
411 | /* |
412 | * look for ":<protocol_name>": |
413 | * + no protocol suffix: fall back to whatever is available; |
414 | * + unknown protocol: fail the whole thing |
415 | */ |
416 | proto = strrchr(devname, ':'); |
417 | if (proto) |
418 | *proto++ = '\0'; |
419 | |
420 | stm = stm_find_device(name: devname); |
421 | if (!stm) { |
422 | kfree(objp: devname); |
423 | return ERR_PTR(error: -ENODEV); |
424 | } |
425 | |
426 | err = stm_lookup_protocol(name: proto, pdrv: &pdrv, type: &pdrv_node_type); |
427 | kfree(objp: devname); |
428 | |
429 | if (err) { |
430 | stm_put_device(stm); |
431 | return ERR_PTR(error: -ENODEV); |
432 | } |
433 | |
434 | mutex_lock(&stm->policy_mutex); |
435 | if (stm->policy) { |
436 | ret = ERR_PTR(error: -EBUSY); |
437 | goto unlock_policy; |
438 | } |
439 | |
440 | stm->policy = kzalloc(size: sizeof(*stm->policy), GFP_KERNEL); |
441 | if (!stm->policy) { |
442 | ret = ERR_PTR(error: -ENOMEM); |
443 | goto unlock_policy; |
444 | } |
445 | |
446 | config_group_init_type_name(group: &stm->policy->group, name, |
447 | type: &stp_policy_type); |
448 | |
449 | stm->pdrv = pdrv; |
450 | stm->pdrv_node_type = pdrv_node_type; |
451 | stm->policy->stm = stm; |
452 | ret = &stm->policy->group; |
453 | |
454 | unlock_policy: |
455 | mutex_unlock(lock: &stm->policy_mutex); |
456 | |
457 | if (IS_ERR(ptr: ret)) { |
458 | /* |
459 | * pdrv and stm->pdrv at this point can be quite different, |
460 | * and only one of them needs to be 'put' |
461 | */ |
462 | stm_put_protocol(pdrv); |
463 | stm_put_device(stm); |
464 | } |
465 | |
466 | return ret; |
467 | } |
468 | |
469 | static struct configfs_group_operations stp_policy_root_group_ops = { |
470 | .make_group = stp_policy_make, |
471 | }; |
472 | |
473 | static const struct config_item_type stp_policy_root_type = { |
474 | .ct_group_ops = &stp_policy_root_group_ops, |
475 | .ct_owner = THIS_MODULE, |
476 | }; |
477 | |
478 | static struct configfs_subsystem stp_policy_subsys = { |
479 | .su_group = { |
480 | .cg_item = { |
481 | .ci_namebuf = "stp-policy" , |
482 | .ci_type = &stp_policy_root_type, |
483 | }, |
484 | }, |
485 | }; |
486 | |
487 | /* |
488 | * Lock the policy mutex from the outside |
489 | */ |
490 | static struct stp_policy_node * |
491 | __stp_policy_node_lookup(struct stp_policy *policy, char *s) |
492 | { |
493 | struct stp_policy_node *policy_node, *ret = NULL; |
494 | struct list_head *head = &policy->group.cg_children; |
495 | struct config_item *item; |
496 | char *start, *end = s; |
497 | |
498 | if (list_empty(head)) |
499 | return NULL; |
500 | |
501 | next: |
502 | for (;;) { |
503 | start = strsep(&end, "/" ); |
504 | if (!start) |
505 | break; |
506 | |
507 | if (!*start) |
508 | continue; |
509 | |
510 | list_for_each_entry(item, head, ci_entry) { |
511 | policy_node = to_stp_policy_node(item); |
512 | |
513 | if (!strcmp(start, |
514 | policy_node->group.cg_item.ci_name)) { |
515 | ret = policy_node; |
516 | |
517 | if (!end) |
518 | goto out; |
519 | |
520 | head = &policy_node->group.cg_children; |
521 | goto next; |
522 | } |
523 | } |
524 | break; |
525 | } |
526 | |
527 | out: |
528 | return ret; |
529 | } |
530 | |
531 | |
532 | struct stp_policy_node * |
533 | stp_policy_node_lookup(struct stm_device *stm, char *s) |
534 | { |
535 | struct stp_policy_node *policy_node = NULL; |
536 | |
537 | mutex_lock(&stp_policy_subsys.su_mutex); |
538 | |
539 | mutex_lock(&stm->policy_mutex); |
540 | if (stm->policy) |
541 | policy_node = __stp_policy_node_lookup(policy: stm->policy, s); |
542 | mutex_unlock(lock: &stm->policy_mutex); |
543 | |
544 | if (policy_node) |
545 | config_item_get(&policy_node->group.cg_item); |
546 | else |
547 | mutex_unlock(lock: &stp_policy_subsys.su_mutex); |
548 | |
549 | return policy_node; |
550 | } |
551 | |
552 | void stp_policy_node_put(struct stp_policy_node *policy_node) |
553 | { |
554 | lockdep_assert_held(&stp_policy_subsys.su_mutex); |
555 | |
556 | mutex_unlock(lock: &stp_policy_subsys.su_mutex); |
557 | config_item_put(&policy_node->group.cg_item); |
558 | } |
559 | |
560 | int __init stp_configfs_init(void) |
561 | { |
562 | config_group_init(group: &stp_policy_subsys.su_group); |
563 | mutex_init(&stp_policy_subsys.su_mutex); |
564 | return configfs_register_subsystem(subsys: &stp_policy_subsys); |
565 | } |
566 | |
567 | void __exit stp_configfs_exit(void) |
568 | { |
569 | configfs_unregister_subsystem(subsys: &stp_policy_subsys); |
570 | } |
571 | |