18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * System Trace Module (STM) master/channel allocation policy management 48c2ecf20Sopenharmony_ci * Copyright (c) 2014, Intel Corporation. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * A master/channel allocation policy allows mapping string identifiers to 78c2ecf20Sopenharmony_ci * master and channel ranges, where allocation can be done. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/types.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/device.h> 158c2ecf20Sopenharmony_ci#include <linux/configfs.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/stm.h> 188c2ecf20Sopenharmony_ci#include "stm.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* 218c2ecf20Sopenharmony_ci * STP Master/Channel allocation policy configfs layout. 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistruct stp_policy { 258c2ecf20Sopenharmony_ci struct config_group group; 268c2ecf20Sopenharmony_ci struct stm_device *stm; 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistruct stp_policy_node { 308c2ecf20Sopenharmony_ci struct config_group group; 318c2ecf20Sopenharmony_ci struct stp_policy *policy; 328c2ecf20Sopenharmony_ci unsigned int first_master; 338c2ecf20Sopenharmony_ci unsigned int last_master; 348c2ecf20Sopenharmony_ci unsigned int first_channel; 358c2ecf20Sopenharmony_ci unsigned int last_channel; 368c2ecf20Sopenharmony_ci /* this is the one that's exposed to the attributes */ 378c2ecf20Sopenharmony_ci unsigned char priv[]; 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_civoid *stp_policy_node_priv(struct stp_policy_node *pn) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci if (!pn) 438c2ecf20Sopenharmony_ci return NULL; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci return pn->priv; 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic struct configfs_subsystem stp_policy_subsys; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_civoid stp_policy_node_get_ranges(struct stp_policy_node *policy_node, 518c2ecf20Sopenharmony_ci unsigned int *mstart, unsigned int *mend, 528c2ecf20Sopenharmony_ci unsigned int *cstart, unsigned int *cend) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci *mstart = policy_node->first_master; 558c2ecf20Sopenharmony_ci *mend = policy_node->last_master; 568c2ecf20Sopenharmony_ci *cstart = policy_node->first_channel; 578c2ecf20Sopenharmony_ci *cend = policy_node->last_channel; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic inline char *stp_policy_node_name(struct stp_policy_node *policy_node) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci return policy_node->group.cg_item.ci_name ? : "<none>"; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic inline struct stp_policy *to_stp_policy(struct config_item *item) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci return item ? 688c2ecf20Sopenharmony_ci container_of(to_config_group(item), struct stp_policy, group) : 698c2ecf20Sopenharmony_ci NULL; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic inline struct stp_policy_node * 738c2ecf20Sopenharmony_cito_stp_policy_node(struct config_item *item) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci return item ? 768c2ecf20Sopenharmony_ci container_of(to_config_group(item), struct stp_policy_node, 778c2ecf20Sopenharmony_ci group) : 788c2ecf20Sopenharmony_ci NULL; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_civoid *to_pdrv_policy_node(struct config_item *item) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct stp_policy_node *node = to_stp_policy_node(item); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return stp_policy_node_priv(node); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(to_pdrv_policy_node); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic ssize_t 908c2ecf20Sopenharmony_cistp_policy_node_masters_show(struct config_item *item, char *page) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci struct stp_policy_node *policy_node = to_stp_policy_node(item); 938c2ecf20Sopenharmony_ci ssize_t count; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci count = sprintf(page, "%u %u\n", policy_node->first_master, 968c2ecf20Sopenharmony_ci policy_node->last_master); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci return count; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic ssize_t 1028c2ecf20Sopenharmony_cistp_policy_node_masters_store(struct config_item *item, const char *page, 1038c2ecf20Sopenharmony_ci size_t count) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct stp_policy_node *policy_node = to_stp_policy_node(item); 1068c2ecf20Sopenharmony_ci unsigned int first, last; 1078c2ecf20Sopenharmony_ci struct stm_device *stm; 1088c2ecf20Sopenharmony_ci char *p = (char *)page; 1098c2ecf20Sopenharmony_ci ssize_t ret = -ENODEV; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (sscanf(p, "%u %u", &first, &last) != 2) 1128c2ecf20Sopenharmony_ci return -EINVAL; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci mutex_lock(&stp_policy_subsys.su_mutex); 1158c2ecf20Sopenharmony_ci stm = policy_node->policy->stm; 1168c2ecf20Sopenharmony_ci if (!stm) 1178c2ecf20Sopenharmony_ci goto unlock; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci /* must be within [sw_start..sw_end], which is an inclusive range */ 1208c2ecf20Sopenharmony_ci if (first > last || first < stm->data->sw_start || 1218c2ecf20Sopenharmony_ci last > stm->data->sw_end) { 1228c2ecf20Sopenharmony_ci ret = -ERANGE; 1238c2ecf20Sopenharmony_ci goto unlock; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci ret = count; 1278c2ecf20Sopenharmony_ci policy_node->first_master = first; 1288c2ecf20Sopenharmony_ci policy_node->last_master = last; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ciunlock: 1318c2ecf20Sopenharmony_ci mutex_unlock(&stp_policy_subsys.su_mutex); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci return ret; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic ssize_t 1378c2ecf20Sopenharmony_cistp_policy_node_channels_show(struct config_item *item, char *page) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct stp_policy_node *policy_node = to_stp_policy_node(item); 1408c2ecf20Sopenharmony_ci ssize_t count; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci count = sprintf(page, "%u %u\n", policy_node->first_channel, 1438c2ecf20Sopenharmony_ci policy_node->last_channel); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci return count; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic ssize_t 1498c2ecf20Sopenharmony_cistp_policy_node_channels_store(struct config_item *item, const char *page, 1508c2ecf20Sopenharmony_ci size_t count) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct stp_policy_node *policy_node = to_stp_policy_node(item); 1538c2ecf20Sopenharmony_ci unsigned int first, last; 1548c2ecf20Sopenharmony_ci struct stm_device *stm; 1558c2ecf20Sopenharmony_ci char *p = (char *)page; 1568c2ecf20Sopenharmony_ci ssize_t ret = -ENODEV; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (sscanf(p, "%u %u", &first, &last) != 2) 1598c2ecf20Sopenharmony_ci return -EINVAL; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci mutex_lock(&stp_policy_subsys.su_mutex); 1628c2ecf20Sopenharmony_ci stm = policy_node->policy->stm; 1638c2ecf20Sopenharmony_ci if (!stm) 1648c2ecf20Sopenharmony_ci goto unlock; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (first > INT_MAX || last > INT_MAX || first > last || 1678c2ecf20Sopenharmony_ci last >= stm->data->sw_nchannels) { 1688c2ecf20Sopenharmony_ci ret = -ERANGE; 1698c2ecf20Sopenharmony_ci goto unlock; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci ret = count; 1738c2ecf20Sopenharmony_ci policy_node->first_channel = first; 1748c2ecf20Sopenharmony_ci policy_node->last_channel = last; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ciunlock: 1778c2ecf20Sopenharmony_ci mutex_unlock(&stp_policy_subsys.su_mutex); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return ret; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic void stp_policy_node_release(struct config_item *item) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci struct stp_policy_node *node = to_stp_policy_node(item); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci kfree(node); 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic struct configfs_item_operations stp_policy_node_item_ops = { 1908c2ecf20Sopenharmony_ci .release = stp_policy_node_release, 1918c2ecf20Sopenharmony_ci}; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ciCONFIGFS_ATTR(stp_policy_node_, masters); 1948c2ecf20Sopenharmony_ciCONFIGFS_ATTR(stp_policy_node_, channels); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic struct configfs_attribute *stp_policy_node_attrs[] = { 1978c2ecf20Sopenharmony_ci &stp_policy_node_attr_masters, 1988c2ecf20Sopenharmony_ci &stp_policy_node_attr_channels, 1998c2ecf20Sopenharmony_ci NULL, 2008c2ecf20Sopenharmony_ci}; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic const struct config_item_type stp_policy_type; 2038c2ecf20Sopenharmony_cistatic const struct config_item_type stp_policy_node_type; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ciconst struct config_item_type * 2068c2ecf20Sopenharmony_ciget_policy_node_type(struct configfs_attribute **attrs) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci struct config_item_type *type; 2098c2ecf20Sopenharmony_ci struct configfs_attribute **merged; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci type = kmemdup(&stp_policy_node_type, sizeof(stp_policy_node_type), 2128c2ecf20Sopenharmony_ci GFP_KERNEL); 2138c2ecf20Sopenharmony_ci if (!type) 2148c2ecf20Sopenharmony_ci return NULL; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci merged = memcat_p(stp_policy_node_attrs, attrs); 2178c2ecf20Sopenharmony_ci if (!merged) { 2188c2ecf20Sopenharmony_ci kfree(type); 2198c2ecf20Sopenharmony_ci return NULL; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci type->ct_attrs = merged; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci return type; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic struct config_group * 2288c2ecf20Sopenharmony_cistp_policy_node_make(struct config_group *group, const char *name) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci const struct config_item_type *type = &stp_policy_node_type; 2318c2ecf20Sopenharmony_ci struct stp_policy_node *policy_node, *parent_node; 2328c2ecf20Sopenharmony_ci const struct stm_protocol_driver *pdrv; 2338c2ecf20Sopenharmony_ci struct stp_policy *policy; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (group->cg_item.ci_type == &stp_policy_type) { 2368c2ecf20Sopenharmony_ci policy = container_of(group, struct stp_policy, group); 2378c2ecf20Sopenharmony_ci } else { 2388c2ecf20Sopenharmony_ci parent_node = container_of(group, struct stp_policy_node, 2398c2ecf20Sopenharmony_ci group); 2408c2ecf20Sopenharmony_ci policy = parent_node->policy; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if (!policy->stm) 2448c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci pdrv = policy->stm->pdrv; 2478c2ecf20Sopenharmony_ci policy_node = 2488c2ecf20Sopenharmony_ci kzalloc(offsetof(struct stp_policy_node, priv[pdrv->priv_sz]), 2498c2ecf20Sopenharmony_ci GFP_KERNEL); 2508c2ecf20Sopenharmony_ci if (!policy_node) 2518c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (pdrv->policy_node_init) 2548c2ecf20Sopenharmony_ci pdrv->policy_node_init((void *)policy_node->priv); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (policy->stm->pdrv_node_type) 2578c2ecf20Sopenharmony_ci type = policy->stm->pdrv_node_type; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci config_group_init_type_name(&policy_node->group, name, type); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci policy_node->policy = policy; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* default values for the attributes */ 2648c2ecf20Sopenharmony_ci policy_node->first_master = policy->stm->data->sw_start; 2658c2ecf20Sopenharmony_ci policy_node->last_master = policy->stm->data->sw_end; 2668c2ecf20Sopenharmony_ci policy_node->first_channel = 0; 2678c2ecf20Sopenharmony_ci policy_node->last_channel = policy->stm->data->sw_nchannels - 1; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci return &policy_node->group; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic void 2738c2ecf20Sopenharmony_cistp_policy_node_drop(struct config_group *group, struct config_item *item) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci config_item_put(item); 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic struct configfs_group_operations stp_policy_node_group_ops = { 2798c2ecf20Sopenharmony_ci .make_group = stp_policy_node_make, 2808c2ecf20Sopenharmony_ci .drop_item = stp_policy_node_drop, 2818c2ecf20Sopenharmony_ci}; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic const struct config_item_type stp_policy_node_type = { 2848c2ecf20Sopenharmony_ci .ct_item_ops = &stp_policy_node_item_ops, 2858c2ecf20Sopenharmony_ci .ct_group_ops = &stp_policy_node_group_ops, 2868c2ecf20Sopenharmony_ci .ct_attrs = stp_policy_node_attrs, 2878c2ecf20Sopenharmony_ci .ct_owner = THIS_MODULE, 2888c2ecf20Sopenharmony_ci}; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci/* 2918c2ecf20Sopenharmony_ci * Root group: policies. 2928c2ecf20Sopenharmony_ci */ 2938c2ecf20Sopenharmony_cistatic ssize_t stp_policy_device_show(struct config_item *item, 2948c2ecf20Sopenharmony_ci char *page) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci struct stp_policy *policy = to_stp_policy(item); 2978c2ecf20Sopenharmony_ci ssize_t count; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci count = sprintf(page, "%s\n", 3008c2ecf20Sopenharmony_ci (policy && policy->stm) ? 3018c2ecf20Sopenharmony_ci policy->stm->data->name : 3028c2ecf20Sopenharmony_ci "<none>"); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci return count; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ciCONFIGFS_ATTR_RO(stp_policy_, device); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic ssize_t stp_policy_protocol_show(struct config_item *item, 3108c2ecf20Sopenharmony_ci char *page) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci struct stp_policy *policy = to_stp_policy(item); 3138c2ecf20Sopenharmony_ci ssize_t count; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci count = sprintf(page, "%s\n", 3168c2ecf20Sopenharmony_ci (policy && policy->stm) ? 3178c2ecf20Sopenharmony_ci policy->stm->pdrv->name : 3188c2ecf20Sopenharmony_ci "<none>"); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci return count; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ciCONFIGFS_ATTR_RO(stp_policy_, protocol); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic struct configfs_attribute *stp_policy_attrs[] = { 3268c2ecf20Sopenharmony_ci &stp_policy_attr_device, 3278c2ecf20Sopenharmony_ci &stp_policy_attr_protocol, 3288c2ecf20Sopenharmony_ci NULL, 3298c2ecf20Sopenharmony_ci}; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_civoid stp_policy_unbind(struct stp_policy *policy) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci struct stm_device *stm = policy->stm; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci /* 3368c2ecf20Sopenharmony_ci * stp_policy_release() will not call here if the policy is already 3378c2ecf20Sopenharmony_ci * unbound; other users should not either, as no link exists between 3388c2ecf20Sopenharmony_ci * this policy and anything else in that case 3398c2ecf20Sopenharmony_ci */ 3408c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!policy->stm)) 3418c2ecf20Sopenharmony_ci return; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci lockdep_assert_held(&stm->policy_mutex); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci stm->policy = NULL; 3468c2ecf20Sopenharmony_ci policy->stm = NULL; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci /* 3498c2ecf20Sopenharmony_ci * Drop the reference on the protocol driver and lose the link. 3508c2ecf20Sopenharmony_ci */ 3518c2ecf20Sopenharmony_ci stm_put_protocol(stm->pdrv); 3528c2ecf20Sopenharmony_ci stm->pdrv = NULL; 3538c2ecf20Sopenharmony_ci stm_put_device(stm); 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic void stp_policy_release(struct config_item *item) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci struct stp_policy *policy = to_stp_policy(item); 3598c2ecf20Sopenharmony_ci struct stm_device *stm = policy->stm; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* a policy *can* be unbound and still exist in configfs tree */ 3628c2ecf20Sopenharmony_ci if (!stm) 3638c2ecf20Sopenharmony_ci return; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci mutex_lock(&stm->policy_mutex); 3668c2ecf20Sopenharmony_ci stp_policy_unbind(policy); 3678c2ecf20Sopenharmony_ci mutex_unlock(&stm->policy_mutex); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci kfree(policy); 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic struct configfs_item_operations stp_policy_item_ops = { 3738c2ecf20Sopenharmony_ci .release = stp_policy_release, 3748c2ecf20Sopenharmony_ci}; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic struct configfs_group_operations stp_policy_group_ops = { 3778c2ecf20Sopenharmony_ci .make_group = stp_policy_node_make, 3788c2ecf20Sopenharmony_ci}; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic const struct config_item_type stp_policy_type = { 3818c2ecf20Sopenharmony_ci .ct_item_ops = &stp_policy_item_ops, 3828c2ecf20Sopenharmony_ci .ct_group_ops = &stp_policy_group_ops, 3838c2ecf20Sopenharmony_ci .ct_attrs = stp_policy_attrs, 3848c2ecf20Sopenharmony_ci .ct_owner = THIS_MODULE, 3858c2ecf20Sopenharmony_ci}; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic struct config_group * 3888c2ecf20Sopenharmony_cistp_policy_make(struct config_group *group, const char *name) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci const struct config_item_type *pdrv_node_type; 3918c2ecf20Sopenharmony_ci const struct stm_protocol_driver *pdrv; 3928c2ecf20Sopenharmony_ci char *devname, *proto, *p; 3938c2ecf20Sopenharmony_ci struct config_group *ret; 3948c2ecf20Sopenharmony_ci struct stm_device *stm; 3958c2ecf20Sopenharmony_ci int err; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci devname = kasprintf(GFP_KERNEL, "%s", name); 3988c2ecf20Sopenharmony_ci if (!devname) 3998c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* 4028c2ecf20Sopenharmony_ci * node must look like <device_name>.<policy_name>, where 4038c2ecf20Sopenharmony_ci * <device_name> is the name of an existing stm device; may 4048c2ecf20Sopenharmony_ci * contain dots; 4058c2ecf20Sopenharmony_ci * <policy_name> is an arbitrary string; may not contain dots 4068c2ecf20Sopenharmony_ci * <device_name>:<protocol_name>.<policy_name> 4078c2ecf20Sopenharmony_ci */ 4088c2ecf20Sopenharmony_ci p = strrchr(devname, '.'); 4098c2ecf20Sopenharmony_ci if (!p) { 4108c2ecf20Sopenharmony_ci kfree(devname); 4118c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci *p = '\0'; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* 4178c2ecf20Sopenharmony_ci * look for ":<protocol_name>": 4188c2ecf20Sopenharmony_ci * + no protocol suffix: fall back to whatever is available; 4198c2ecf20Sopenharmony_ci * + unknown protocol: fail the whole thing 4208c2ecf20Sopenharmony_ci */ 4218c2ecf20Sopenharmony_ci proto = strrchr(devname, ':'); 4228c2ecf20Sopenharmony_ci if (proto) 4238c2ecf20Sopenharmony_ci *proto++ = '\0'; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci stm = stm_find_device(devname); 4268c2ecf20Sopenharmony_ci if (!stm) { 4278c2ecf20Sopenharmony_ci kfree(devname); 4288c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci err = stm_lookup_protocol(proto, &pdrv, &pdrv_node_type); 4328c2ecf20Sopenharmony_ci kfree(devname); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (err) { 4358c2ecf20Sopenharmony_ci stm_put_device(stm); 4368c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci mutex_lock(&stm->policy_mutex); 4408c2ecf20Sopenharmony_ci if (stm->policy) { 4418c2ecf20Sopenharmony_ci ret = ERR_PTR(-EBUSY); 4428c2ecf20Sopenharmony_ci goto unlock_policy; 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci stm->policy = kzalloc(sizeof(*stm->policy), GFP_KERNEL); 4468c2ecf20Sopenharmony_ci if (!stm->policy) { 4478c2ecf20Sopenharmony_ci ret = ERR_PTR(-ENOMEM); 4488c2ecf20Sopenharmony_ci goto unlock_policy; 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci config_group_init_type_name(&stm->policy->group, name, 4528c2ecf20Sopenharmony_ci &stp_policy_type); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci stm->pdrv = pdrv; 4558c2ecf20Sopenharmony_ci stm->pdrv_node_type = pdrv_node_type; 4568c2ecf20Sopenharmony_ci stm->policy->stm = stm; 4578c2ecf20Sopenharmony_ci ret = &stm->policy->group; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ciunlock_policy: 4608c2ecf20Sopenharmony_ci mutex_unlock(&stm->policy_mutex); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci if (IS_ERR(ret)) { 4638c2ecf20Sopenharmony_ci /* 4648c2ecf20Sopenharmony_ci * pdrv and stm->pdrv at this point can be quite different, 4658c2ecf20Sopenharmony_ci * and only one of them needs to be 'put' 4668c2ecf20Sopenharmony_ci */ 4678c2ecf20Sopenharmony_ci stm_put_protocol(pdrv); 4688c2ecf20Sopenharmony_ci stm_put_device(stm); 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci return ret; 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic struct configfs_group_operations stp_policy_root_group_ops = { 4758c2ecf20Sopenharmony_ci .make_group = stp_policy_make, 4768c2ecf20Sopenharmony_ci}; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_cistatic const struct config_item_type stp_policy_root_type = { 4798c2ecf20Sopenharmony_ci .ct_group_ops = &stp_policy_root_group_ops, 4808c2ecf20Sopenharmony_ci .ct_owner = THIS_MODULE, 4818c2ecf20Sopenharmony_ci}; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_cistatic struct configfs_subsystem stp_policy_subsys = { 4848c2ecf20Sopenharmony_ci .su_group = { 4858c2ecf20Sopenharmony_ci .cg_item = { 4868c2ecf20Sopenharmony_ci .ci_namebuf = "stp-policy", 4878c2ecf20Sopenharmony_ci .ci_type = &stp_policy_root_type, 4888c2ecf20Sopenharmony_ci }, 4898c2ecf20Sopenharmony_ci }, 4908c2ecf20Sopenharmony_ci}; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci/* 4938c2ecf20Sopenharmony_ci * Lock the policy mutex from the outside 4948c2ecf20Sopenharmony_ci */ 4958c2ecf20Sopenharmony_cistatic struct stp_policy_node * 4968c2ecf20Sopenharmony_ci__stp_policy_node_lookup(struct stp_policy *policy, char *s) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci struct stp_policy_node *policy_node, *ret = NULL; 4998c2ecf20Sopenharmony_ci struct list_head *head = &policy->group.cg_children; 5008c2ecf20Sopenharmony_ci struct config_item *item; 5018c2ecf20Sopenharmony_ci char *start, *end = s; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci if (list_empty(head)) 5048c2ecf20Sopenharmony_ci return NULL; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cinext: 5078c2ecf20Sopenharmony_ci for (;;) { 5088c2ecf20Sopenharmony_ci start = strsep(&end, "/"); 5098c2ecf20Sopenharmony_ci if (!start) 5108c2ecf20Sopenharmony_ci break; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci if (!*start) 5138c2ecf20Sopenharmony_ci continue; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci list_for_each_entry(item, head, ci_entry) { 5168c2ecf20Sopenharmony_ci policy_node = to_stp_policy_node(item); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci if (!strcmp(start, 5198c2ecf20Sopenharmony_ci policy_node->group.cg_item.ci_name)) { 5208c2ecf20Sopenharmony_ci ret = policy_node; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci if (!end) 5238c2ecf20Sopenharmony_ci goto out; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci head = &policy_node->group.cg_children; 5268c2ecf20Sopenharmony_ci goto next; 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci break; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ciout: 5338c2ecf20Sopenharmony_ci return ret; 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_cistruct stp_policy_node * 5388c2ecf20Sopenharmony_cistp_policy_node_lookup(struct stm_device *stm, char *s) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci struct stp_policy_node *policy_node = NULL; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci mutex_lock(&stp_policy_subsys.su_mutex); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci mutex_lock(&stm->policy_mutex); 5458c2ecf20Sopenharmony_ci if (stm->policy) 5468c2ecf20Sopenharmony_ci policy_node = __stp_policy_node_lookup(stm->policy, s); 5478c2ecf20Sopenharmony_ci mutex_unlock(&stm->policy_mutex); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (policy_node) 5508c2ecf20Sopenharmony_ci config_item_get(&policy_node->group.cg_item); 5518c2ecf20Sopenharmony_ci else 5528c2ecf20Sopenharmony_ci mutex_unlock(&stp_policy_subsys.su_mutex); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci return policy_node; 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_civoid stp_policy_node_put(struct stp_policy_node *policy_node) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci lockdep_assert_held(&stp_policy_subsys.su_mutex); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci mutex_unlock(&stp_policy_subsys.su_mutex); 5628c2ecf20Sopenharmony_ci config_item_put(&policy_node->group.cg_item); 5638c2ecf20Sopenharmony_ci} 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ciint __init stp_configfs_init(void) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci config_group_init(&stp_policy_subsys.su_group); 5688c2ecf20Sopenharmony_ci mutex_init(&stp_policy_subsys.su_mutex); 5698c2ecf20Sopenharmony_ci return configfs_register_subsystem(&stp_policy_subsys); 5708c2ecf20Sopenharmony_ci} 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_civoid __exit stp_configfs_exit(void) 5738c2ecf20Sopenharmony_ci{ 5748c2ecf20Sopenharmony_ci configfs_unregister_subsystem(&stp_policy_subsys); 5758c2ecf20Sopenharmony_ci} 576