19ea393d8SAlexander Shishkin // SPDX-License-Identifier: GPL-2.0 27bd1d409SAlexander Shishkin /* 37bd1d409SAlexander Shishkin * System Trace Module (STM) master/channel allocation policy management 47bd1d409SAlexander Shishkin * Copyright (c) 2014, Intel Corporation. 57bd1d409SAlexander Shishkin * 67bd1d409SAlexander Shishkin * A master/channel allocation policy allows mapping string identifiers to 77bd1d409SAlexander Shishkin * master and channel ranges, where allocation can be done. 87bd1d409SAlexander Shishkin */ 97bd1d409SAlexander Shishkin 107bd1d409SAlexander Shishkin #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 117bd1d409SAlexander Shishkin 127bd1d409SAlexander Shishkin #include <linux/types.h> 137bd1d409SAlexander Shishkin #include <linux/module.h> 147bd1d409SAlexander Shishkin #include <linux/device.h> 157bd1d409SAlexander Shishkin #include <linux/configfs.h> 167bd1d409SAlexander Shishkin #include <linux/slab.h> 177bd1d409SAlexander Shishkin #include <linux/stm.h> 187bd1d409SAlexander Shishkin #include "stm.h" 197bd1d409SAlexander Shishkin 207bd1d409SAlexander Shishkin /* 217bd1d409SAlexander Shishkin * STP Master/Channel allocation policy configfs layout. 227bd1d409SAlexander Shishkin */ 237bd1d409SAlexander Shishkin 247bd1d409SAlexander Shishkin struct stp_policy { 257bd1d409SAlexander Shishkin struct config_group group; 267bd1d409SAlexander Shishkin struct stm_device *stm; 277bd1d409SAlexander Shishkin }; 287bd1d409SAlexander Shishkin 297bd1d409SAlexander Shishkin struct stp_policy_node { 307bd1d409SAlexander Shishkin struct config_group group; 317bd1d409SAlexander Shishkin struct stp_policy *policy; 327bd1d409SAlexander Shishkin unsigned int first_master; 337bd1d409SAlexander Shishkin unsigned int last_master; 347bd1d409SAlexander Shishkin unsigned int first_channel; 357bd1d409SAlexander Shishkin unsigned int last_channel; 36c7fd62bcSAlexander Shishkin /* this is the one that's exposed to the attributes */ 37c7fd62bcSAlexander Shishkin unsigned char priv[0]; 387bd1d409SAlexander Shishkin }; 397bd1d409SAlexander Shishkin 40c7fd62bcSAlexander Shishkin void *stp_policy_node_priv(struct stp_policy_node *pn) 41c7fd62bcSAlexander Shishkin { 42c7fd62bcSAlexander Shishkin if (!pn) 43c7fd62bcSAlexander Shishkin return NULL; 44c7fd62bcSAlexander Shishkin 45c7fd62bcSAlexander Shishkin return pn->priv; 46c7fd62bcSAlexander Shishkin } 47c7fd62bcSAlexander Shishkin 487bd1d409SAlexander Shishkin static struct configfs_subsystem stp_policy_subsys; 497bd1d409SAlexander Shishkin 507bd1d409SAlexander Shishkin void stp_policy_node_get_ranges(struct stp_policy_node *policy_node, 517bd1d409SAlexander Shishkin unsigned int *mstart, unsigned int *mend, 527bd1d409SAlexander Shishkin unsigned int *cstart, unsigned int *cend) 537bd1d409SAlexander Shishkin { 547bd1d409SAlexander Shishkin *mstart = policy_node->first_master; 557bd1d409SAlexander Shishkin *mend = policy_node->last_master; 567bd1d409SAlexander Shishkin *cstart = policy_node->first_channel; 577bd1d409SAlexander Shishkin *cend = policy_node->last_channel; 587bd1d409SAlexander Shishkin } 597bd1d409SAlexander Shishkin 607bd1d409SAlexander Shishkin static inline char *stp_policy_node_name(struct stp_policy_node *policy_node) 617bd1d409SAlexander Shishkin { 627bd1d409SAlexander Shishkin return policy_node->group.cg_item.ci_name ? : "<none>"; 637bd1d409SAlexander Shishkin } 647bd1d409SAlexander Shishkin 657bd1d409SAlexander Shishkin static inline struct stp_policy *to_stp_policy(struct config_item *item) 667bd1d409SAlexander Shishkin { 677bd1d409SAlexander Shishkin return item ? 687bd1d409SAlexander Shishkin container_of(to_config_group(item), struct stp_policy, group) : 697bd1d409SAlexander Shishkin NULL; 707bd1d409SAlexander Shishkin } 717bd1d409SAlexander Shishkin 727bd1d409SAlexander Shishkin static inline struct stp_policy_node * 737bd1d409SAlexander Shishkin to_stp_policy_node(struct config_item *item) 747bd1d409SAlexander Shishkin { 757bd1d409SAlexander Shishkin return item ? 767bd1d409SAlexander Shishkin container_of(to_config_group(item), struct stp_policy_node, 777bd1d409SAlexander Shishkin group) : 787bd1d409SAlexander Shishkin NULL; 797bd1d409SAlexander Shishkin } 807bd1d409SAlexander Shishkin 81c7fd62bcSAlexander Shishkin void *to_pdrv_policy_node(struct config_item *item) 82c7fd62bcSAlexander Shishkin { 83c7fd62bcSAlexander Shishkin struct stp_policy_node *node = to_stp_policy_node(item); 84c7fd62bcSAlexander Shishkin 85c7fd62bcSAlexander Shishkin return stp_policy_node_priv(node); 86c7fd62bcSAlexander Shishkin } 87c7fd62bcSAlexander Shishkin EXPORT_SYMBOL_GPL(to_pdrv_policy_node); 88c7fd62bcSAlexander Shishkin 899aa3d651SLinus Torvalds static ssize_t 909aa3d651SLinus Torvalds stp_policy_node_masters_show(struct config_item *item, char *page) 917bd1d409SAlexander Shishkin { 929aa3d651SLinus Torvalds struct stp_policy_node *policy_node = to_stp_policy_node(item); 937bd1d409SAlexander Shishkin ssize_t count; 947bd1d409SAlexander Shishkin 957bd1d409SAlexander Shishkin count = sprintf(page, "%u %u\n", policy_node->first_master, 967bd1d409SAlexander Shishkin policy_node->last_master); 977bd1d409SAlexander Shishkin 987bd1d409SAlexander Shishkin return count; 997bd1d409SAlexander Shishkin } 1007bd1d409SAlexander Shishkin 1017bd1d409SAlexander Shishkin static ssize_t 1029aa3d651SLinus Torvalds stp_policy_node_masters_store(struct config_item *item, const char *page, 1039aa3d651SLinus Torvalds size_t count) 1047bd1d409SAlexander Shishkin { 1059aa3d651SLinus Torvalds struct stp_policy_node *policy_node = to_stp_policy_node(item); 1067bd1d409SAlexander Shishkin unsigned int first, last; 1077bd1d409SAlexander Shishkin struct stm_device *stm; 1087bd1d409SAlexander Shishkin char *p = (char *)page; 1097bd1d409SAlexander Shishkin ssize_t ret = -ENODEV; 1107bd1d409SAlexander Shishkin 1117bd1d409SAlexander Shishkin if (sscanf(p, "%u %u", &first, &last) != 2) 1127bd1d409SAlexander Shishkin return -EINVAL; 1137bd1d409SAlexander Shishkin 1147bd1d409SAlexander Shishkin mutex_lock(&stp_policy_subsys.su_mutex); 1157bd1d409SAlexander Shishkin stm = policy_node->policy->stm; 1167bd1d409SAlexander Shishkin if (!stm) 1177bd1d409SAlexander Shishkin goto unlock; 1187bd1d409SAlexander Shishkin 1197bd1d409SAlexander Shishkin /* must be within [sw_start..sw_end], which is an inclusive range */ 120f57af6dfSChunyan Zhang if (first > last || first < stm->data->sw_start || 1217bd1d409SAlexander Shishkin last > stm->data->sw_end) { 1227bd1d409SAlexander Shishkin ret = -ERANGE; 1237bd1d409SAlexander Shishkin goto unlock; 1247bd1d409SAlexander Shishkin } 1257bd1d409SAlexander Shishkin 1267bd1d409SAlexander Shishkin ret = count; 1277bd1d409SAlexander Shishkin policy_node->first_master = first; 1287bd1d409SAlexander Shishkin policy_node->last_master = last; 1297bd1d409SAlexander Shishkin 1307bd1d409SAlexander Shishkin unlock: 1317bd1d409SAlexander Shishkin mutex_unlock(&stp_policy_subsys.su_mutex); 1327bd1d409SAlexander Shishkin 1337bd1d409SAlexander Shishkin return ret; 1347bd1d409SAlexander Shishkin } 1357bd1d409SAlexander Shishkin 1367bd1d409SAlexander Shishkin static ssize_t 1379aa3d651SLinus Torvalds stp_policy_node_channels_show(struct config_item *item, char *page) 1387bd1d409SAlexander Shishkin { 1399aa3d651SLinus Torvalds struct stp_policy_node *policy_node = to_stp_policy_node(item); 1407bd1d409SAlexander Shishkin ssize_t count; 1417bd1d409SAlexander Shishkin 1427bd1d409SAlexander Shishkin count = sprintf(page, "%u %u\n", policy_node->first_channel, 1437bd1d409SAlexander Shishkin policy_node->last_channel); 1447bd1d409SAlexander Shishkin 1457bd1d409SAlexander Shishkin return count; 1467bd1d409SAlexander Shishkin } 1477bd1d409SAlexander Shishkin 1487bd1d409SAlexander Shishkin static ssize_t 1499aa3d651SLinus Torvalds stp_policy_node_channels_store(struct config_item *item, const char *page, 1509aa3d651SLinus Torvalds size_t count) 1517bd1d409SAlexander Shishkin { 1529aa3d651SLinus Torvalds struct stp_policy_node *policy_node = to_stp_policy_node(item); 1537bd1d409SAlexander Shishkin unsigned int first, last; 1547bd1d409SAlexander Shishkin struct stm_device *stm; 1557bd1d409SAlexander Shishkin char *p = (char *)page; 1567bd1d409SAlexander Shishkin ssize_t ret = -ENODEV; 1577bd1d409SAlexander Shishkin 1587bd1d409SAlexander Shishkin if (sscanf(p, "%u %u", &first, &last) != 2) 1597bd1d409SAlexander Shishkin return -EINVAL; 1607bd1d409SAlexander Shishkin 1617bd1d409SAlexander Shishkin mutex_lock(&stp_policy_subsys.su_mutex); 1627bd1d409SAlexander Shishkin stm = policy_node->policy->stm; 1637bd1d409SAlexander Shishkin if (!stm) 1647bd1d409SAlexander Shishkin goto unlock; 1657bd1d409SAlexander Shishkin 1667bd1d409SAlexander Shishkin if (first > INT_MAX || last > INT_MAX || first > last || 1677bd1d409SAlexander Shishkin last >= stm->data->sw_nchannels) { 1687bd1d409SAlexander Shishkin ret = -ERANGE; 1697bd1d409SAlexander Shishkin goto unlock; 1707bd1d409SAlexander Shishkin } 1717bd1d409SAlexander Shishkin 1727bd1d409SAlexander Shishkin ret = count; 1737bd1d409SAlexander Shishkin policy_node->first_channel = first; 1747bd1d409SAlexander Shishkin policy_node->last_channel = last; 1757bd1d409SAlexander Shishkin 1767bd1d409SAlexander Shishkin unlock: 1777bd1d409SAlexander Shishkin mutex_unlock(&stp_policy_subsys.su_mutex); 1787bd1d409SAlexander Shishkin 1797bd1d409SAlexander Shishkin return ret; 1807bd1d409SAlexander Shishkin } 1817bd1d409SAlexander Shishkin 1827bd1d409SAlexander Shishkin static void stp_policy_node_release(struct config_item *item) 1837bd1d409SAlexander Shishkin { 184c7fd62bcSAlexander Shishkin struct stp_policy_node *node = to_stp_policy_node(item); 185c7fd62bcSAlexander Shishkin 186c7fd62bcSAlexander Shishkin kfree(node); 1877bd1d409SAlexander Shishkin } 1887bd1d409SAlexander Shishkin 1897bd1d409SAlexander Shishkin static struct configfs_item_operations stp_policy_node_item_ops = { 1907bd1d409SAlexander Shishkin .release = stp_policy_node_release, 1917bd1d409SAlexander Shishkin }; 1927bd1d409SAlexander Shishkin 1939aa3d651SLinus Torvalds CONFIGFS_ATTR(stp_policy_node_, masters); 1949aa3d651SLinus Torvalds CONFIGFS_ATTR(stp_policy_node_, channels); 1957bd1d409SAlexander Shishkin 1967bd1d409SAlexander Shishkin static struct configfs_attribute *stp_policy_node_attrs[] = { 1979aa3d651SLinus Torvalds &stp_policy_node_attr_masters, 1989aa3d651SLinus Torvalds &stp_policy_node_attr_channels, 1997bd1d409SAlexander Shishkin NULL, 2007bd1d409SAlexander Shishkin }; 2017bd1d409SAlexander Shishkin 202085006e8SBhumika Goyal static const struct config_item_type stp_policy_type; 203085006e8SBhumika Goyal static const struct config_item_type stp_policy_node_type; 2047bd1d409SAlexander Shishkin 205c7fd62bcSAlexander Shishkin const struct config_item_type * 206c7fd62bcSAlexander Shishkin get_policy_node_type(struct configfs_attribute **attrs) 207c7fd62bcSAlexander Shishkin { 208c7fd62bcSAlexander Shishkin struct config_item_type *type; 209c7fd62bcSAlexander Shishkin struct configfs_attribute **merged; 210c7fd62bcSAlexander Shishkin 211c7fd62bcSAlexander Shishkin type = kmemdup(&stp_policy_node_type, sizeof(stp_policy_node_type), 212c7fd62bcSAlexander Shishkin GFP_KERNEL); 213c7fd62bcSAlexander Shishkin if (!type) 214c7fd62bcSAlexander Shishkin return NULL; 215c7fd62bcSAlexander Shishkin 216a23bbec2SAlexander Shishkin merged = memcat_p(stp_policy_node_attrs, attrs); 217c7fd62bcSAlexander Shishkin if (!merged) { 218c7fd62bcSAlexander Shishkin kfree(type); 219c7fd62bcSAlexander Shishkin return NULL; 220c7fd62bcSAlexander Shishkin } 221c7fd62bcSAlexander Shishkin 222c7fd62bcSAlexander Shishkin type->ct_attrs = merged; 223c7fd62bcSAlexander Shishkin 224c7fd62bcSAlexander Shishkin return type; 225c7fd62bcSAlexander Shishkin } 226c7fd62bcSAlexander Shishkin 2277bd1d409SAlexander Shishkin static struct config_group * 2287bd1d409SAlexander Shishkin stp_policy_node_make(struct config_group *group, const char *name) 2297bd1d409SAlexander Shishkin { 230c7fd62bcSAlexander Shishkin const struct config_item_type *type = &stp_policy_node_type; 2317bd1d409SAlexander Shishkin struct stp_policy_node *policy_node, *parent_node; 232c7fd62bcSAlexander Shishkin const struct stm_protocol_driver *pdrv; 2337bd1d409SAlexander Shishkin struct stp_policy *policy; 2347bd1d409SAlexander Shishkin 2357bd1d409SAlexander Shishkin if (group->cg_item.ci_type == &stp_policy_type) { 2367bd1d409SAlexander Shishkin policy = container_of(group, struct stp_policy, group); 2377bd1d409SAlexander Shishkin } else { 2387bd1d409SAlexander Shishkin parent_node = container_of(group, struct stp_policy_node, 2397bd1d409SAlexander Shishkin group); 2407bd1d409SAlexander Shishkin policy = parent_node->policy; 2417bd1d409SAlexander Shishkin } 2427bd1d409SAlexander Shishkin 2437bd1d409SAlexander Shishkin if (!policy->stm) 2447bd1d409SAlexander Shishkin return ERR_PTR(-ENODEV); 2457bd1d409SAlexander Shishkin 246c7fd62bcSAlexander Shishkin pdrv = policy->stm->pdrv; 247c7fd62bcSAlexander Shishkin policy_node = 248c7fd62bcSAlexander Shishkin kzalloc(offsetof(struct stp_policy_node, priv[pdrv->priv_sz]), 249c7fd62bcSAlexander Shishkin GFP_KERNEL); 2507bd1d409SAlexander Shishkin if (!policy_node) 2517bd1d409SAlexander Shishkin return ERR_PTR(-ENOMEM); 2527bd1d409SAlexander Shishkin 253c7fd62bcSAlexander Shishkin if (pdrv->policy_node_init) 254c7fd62bcSAlexander Shishkin pdrv->policy_node_init((void *)policy_node->priv); 255c7fd62bcSAlexander Shishkin 256c7fd62bcSAlexander Shishkin if (policy->stm->pdrv_node_type) 257c7fd62bcSAlexander Shishkin type = policy->stm->pdrv_node_type; 258c7fd62bcSAlexander Shishkin 259c7fd62bcSAlexander Shishkin config_group_init_type_name(&policy_node->group, name, type); 2607bd1d409SAlexander Shishkin 2617bd1d409SAlexander Shishkin policy_node->policy = policy; 2627bd1d409SAlexander Shishkin 2637bd1d409SAlexander Shishkin /* default values for the attributes */ 2647bd1d409SAlexander Shishkin policy_node->first_master = policy->stm->data->sw_start; 2657bd1d409SAlexander Shishkin policy_node->last_master = policy->stm->data->sw_end; 2667bd1d409SAlexander Shishkin policy_node->first_channel = 0; 2677bd1d409SAlexander Shishkin policy_node->last_channel = policy->stm->data->sw_nchannels - 1; 2687bd1d409SAlexander Shishkin 2697bd1d409SAlexander Shishkin return &policy_node->group; 2707bd1d409SAlexander Shishkin } 2717bd1d409SAlexander Shishkin 2727bd1d409SAlexander Shishkin static void 2737bd1d409SAlexander Shishkin stp_policy_node_drop(struct config_group *group, struct config_item *item) 2747bd1d409SAlexander Shishkin { 2757bd1d409SAlexander Shishkin config_item_put(item); 2767bd1d409SAlexander Shishkin } 2777bd1d409SAlexander Shishkin 2787bd1d409SAlexander Shishkin static struct configfs_group_operations stp_policy_node_group_ops = { 2797bd1d409SAlexander Shishkin .make_group = stp_policy_node_make, 2807bd1d409SAlexander Shishkin .drop_item = stp_policy_node_drop, 2817bd1d409SAlexander Shishkin }; 2827bd1d409SAlexander Shishkin 283085006e8SBhumika Goyal static const struct config_item_type stp_policy_node_type = { 2847bd1d409SAlexander Shishkin .ct_item_ops = &stp_policy_node_item_ops, 2857bd1d409SAlexander Shishkin .ct_group_ops = &stp_policy_node_group_ops, 2867bd1d409SAlexander Shishkin .ct_attrs = stp_policy_node_attrs, 2877bd1d409SAlexander Shishkin .ct_owner = THIS_MODULE, 2887bd1d409SAlexander Shishkin }; 2897bd1d409SAlexander Shishkin 2907bd1d409SAlexander Shishkin /* 2917bd1d409SAlexander Shishkin * Root group: policies. 2927bd1d409SAlexander Shishkin */ 2939aa3d651SLinus Torvalds static ssize_t stp_policy_device_show(struct config_item *item, 2947bd1d409SAlexander Shishkin char *page) 2957bd1d409SAlexander Shishkin { 2967bd1d409SAlexander Shishkin struct stp_policy *policy = to_stp_policy(item); 2977bd1d409SAlexander Shishkin ssize_t count; 2987bd1d409SAlexander Shishkin 2997bd1d409SAlexander Shishkin count = sprintf(page, "%s\n", 3007bd1d409SAlexander Shishkin (policy && policy->stm) ? 3017bd1d409SAlexander Shishkin policy->stm->data->name : 3027bd1d409SAlexander Shishkin "<none>"); 3037bd1d409SAlexander Shishkin 3047bd1d409SAlexander Shishkin return count; 3057bd1d409SAlexander Shishkin } 3067bd1d409SAlexander Shishkin 3079aa3d651SLinus Torvalds CONFIGFS_ATTR_RO(stp_policy_, device); 3089aa3d651SLinus Torvalds 309c7fd62bcSAlexander Shishkin static ssize_t stp_policy_protocol_show(struct config_item *item, 310c7fd62bcSAlexander Shishkin char *page) 311c7fd62bcSAlexander Shishkin { 312c7fd62bcSAlexander Shishkin struct stp_policy *policy = to_stp_policy(item); 313c7fd62bcSAlexander Shishkin ssize_t count; 314c7fd62bcSAlexander Shishkin 315c7fd62bcSAlexander Shishkin count = sprintf(page, "%s\n", 316c7fd62bcSAlexander Shishkin (policy && policy->stm) ? 317c7fd62bcSAlexander Shishkin policy->stm->pdrv->name : 318c7fd62bcSAlexander Shishkin "<none>"); 319c7fd62bcSAlexander Shishkin 320c7fd62bcSAlexander Shishkin return count; 321c7fd62bcSAlexander Shishkin } 322c7fd62bcSAlexander Shishkin 323c7fd62bcSAlexander Shishkin CONFIGFS_ATTR_RO(stp_policy_, protocol); 324c7fd62bcSAlexander Shishkin 3259aa3d651SLinus Torvalds static struct configfs_attribute *stp_policy_attrs[] = { 3269aa3d651SLinus Torvalds &stp_policy_attr_device, 327c7fd62bcSAlexander Shishkin &stp_policy_attr_protocol, 3289aa3d651SLinus Torvalds NULL, 3299aa3d651SLinus Torvalds }; 3309aa3d651SLinus Torvalds 3317bd1d409SAlexander Shishkin void stp_policy_unbind(struct stp_policy *policy) 3327bd1d409SAlexander Shishkin { 3337bd1d409SAlexander Shishkin struct stm_device *stm = policy->stm; 3347bd1d409SAlexander Shishkin 3354c127fd1SAlexander Shishkin /* 3364c127fd1SAlexander Shishkin * stp_policy_release() will not call here if the policy is already 3374c127fd1SAlexander Shishkin * unbound; other users should not either, as no link exists between 3384c127fd1SAlexander Shishkin * this policy and anything else in that case 3394c127fd1SAlexander Shishkin */ 3407bd1d409SAlexander Shishkin if (WARN_ON_ONCE(!policy->stm)) 3417bd1d409SAlexander Shishkin return; 3427bd1d409SAlexander Shishkin 3434c127fd1SAlexander Shishkin lockdep_assert_held(&stm->policy_mutex); 3447bd1d409SAlexander Shishkin 3454c127fd1SAlexander Shishkin stm->policy = NULL; 3467bd1d409SAlexander Shishkin policy->stm = NULL; 3477bd1d409SAlexander Shishkin 348c7fd62bcSAlexander Shishkin stm_put_protocol(stm->pdrv); 3497bd1d409SAlexander Shishkin stm_put_device(stm); 3507bd1d409SAlexander Shishkin } 3517bd1d409SAlexander Shishkin 3527bd1d409SAlexander Shishkin static void stp_policy_release(struct config_item *item) 3537bd1d409SAlexander Shishkin { 3547bd1d409SAlexander Shishkin struct stp_policy *policy = to_stp_policy(item); 3554c127fd1SAlexander Shishkin struct stm_device *stm = policy->stm; 3567bd1d409SAlexander Shishkin 3574c127fd1SAlexander Shishkin /* a policy *can* be unbound and still exist in configfs tree */ 3584c127fd1SAlexander Shishkin if (!stm) 3594c127fd1SAlexander Shishkin return; 3604c127fd1SAlexander Shishkin 3614c127fd1SAlexander Shishkin mutex_lock(&stm->policy_mutex); 3627bd1d409SAlexander Shishkin stp_policy_unbind(policy); 3634c127fd1SAlexander Shishkin mutex_unlock(&stm->policy_mutex); 3644c127fd1SAlexander Shishkin 3657bd1d409SAlexander Shishkin kfree(policy); 3667bd1d409SAlexander Shishkin } 3677bd1d409SAlexander Shishkin 3687bd1d409SAlexander Shishkin static struct configfs_item_operations stp_policy_item_ops = { 3697bd1d409SAlexander Shishkin .release = stp_policy_release, 3707bd1d409SAlexander Shishkin }; 3717bd1d409SAlexander Shishkin 3727bd1d409SAlexander Shishkin static struct configfs_group_operations stp_policy_group_ops = { 3737bd1d409SAlexander Shishkin .make_group = stp_policy_node_make, 3747bd1d409SAlexander Shishkin }; 3757bd1d409SAlexander Shishkin 376085006e8SBhumika Goyal static const struct config_item_type stp_policy_type = { 3777bd1d409SAlexander Shishkin .ct_item_ops = &stp_policy_item_ops, 3787bd1d409SAlexander Shishkin .ct_group_ops = &stp_policy_group_ops, 3797bd1d409SAlexander Shishkin .ct_attrs = stp_policy_attrs, 3807bd1d409SAlexander Shishkin .ct_owner = THIS_MODULE, 3817bd1d409SAlexander Shishkin }; 3827bd1d409SAlexander Shishkin 3837bd1d409SAlexander Shishkin static struct config_group * 38425e3c006SAlexander Shishkin stp_policy_make(struct config_group *group, const char *name) 3857bd1d409SAlexander Shishkin { 386c7fd62bcSAlexander Shishkin const struct config_item_type *pdrv_node_type; 387c7fd62bcSAlexander Shishkin const struct stm_protocol_driver *pdrv; 388c7fd62bcSAlexander Shishkin char *devname, *proto, *p; 3897bd1d409SAlexander Shishkin struct config_group *ret; 3907bd1d409SAlexander Shishkin struct stm_device *stm; 391c7fd62bcSAlexander Shishkin int err; 3927bd1d409SAlexander Shishkin 3937bd1d409SAlexander Shishkin devname = kasprintf(GFP_KERNEL, "%s", name); 3947bd1d409SAlexander Shishkin if (!devname) 3957bd1d409SAlexander Shishkin return ERR_PTR(-ENOMEM); 3967bd1d409SAlexander Shishkin 3977bd1d409SAlexander Shishkin /* 3987bd1d409SAlexander Shishkin * node must look like <device_name>.<policy_name>, where 39959be422eSAlexander Shishkin * <device_name> is the name of an existing stm device; may 40059be422eSAlexander Shishkin * contain dots; 40159be422eSAlexander Shishkin * <policy_name> is an arbitrary string; may not contain dots 402c7fd62bcSAlexander Shishkin * <device_name>:<protocol_name>.<policy_name> 4037bd1d409SAlexander Shishkin */ 40459be422eSAlexander Shishkin p = strrchr(devname, '.'); 4057bd1d409SAlexander Shishkin if (!p) { 4067bd1d409SAlexander Shishkin kfree(devname); 4077bd1d409SAlexander Shishkin return ERR_PTR(-EINVAL); 4087bd1d409SAlexander Shishkin } 4097bd1d409SAlexander Shishkin 410fb080190SAlexander Shishkin *p = '\0'; 4117bd1d409SAlexander Shishkin 412c7fd62bcSAlexander Shishkin /* 413c7fd62bcSAlexander Shishkin * look for ":<protocol_name>": 414c7fd62bcSAlexander Shishkin * + no protocol suffix: fall back to whatever is available; 415c7fd62bcSAlexander Shishkin * + unknown protocol: fail the whole thing 416c7fd62bcSAlexander Shishkin */ 417c7fd62bcSAlexander Shishkin proto = strrchr(devname, ':'); 418c7fd62bcSAlexander Shishkin if (proto) 419c7fd62bcSAlexander Shishkin *proto++ = '\0'; 420c7fd62bcSAlexander Shishkin 4217bd1d409SAlexander Shishkin stm = stm_find_device(devname); 422c7fd62bcSAlexander Shishkin if (!stm) { 423c7fd62bcSAlexander Shishkin kfree(devname); 424c7fd62bcSAlexander Shishkin return ERR_PTR(-ENODEV); 425c7fd62bcSAlexander Shishkin } 426c7fd62bcSAlexander Shishkin 427c7fd62bcSAlexander Shishkin err = stm_lookup_protocol(proto, &pdrv, &pdrv_node_type); 4287bd1d409SAlexander Shishkin kfree(devname); 4297bd1d409SAlexander Shishkin 43024c7bcb6SAlexander Shishkin if (err) { 431c7fd62bcSAlexander Shishkin stm_put_device(stm); 4327bd1d409SAlexander Shishkin return ERR_PTR(-ENODEV); 433c7fd62bcSAlexander Shishkin } 4347bd1d409SAlexander Shishkin 4357bd1d409SAlexander Shishkin mutex_lock(&stm->policy_mutex); 4367bd1d409SAlexander Shishkin if (stm->policy) { 4377bd1d409SAlexander Shishkin ret = ERR_PTR(-EBUSY); 4387bd1d409SAlexander Shishkin goto unlock_policy; 4397bd1d409SAlexander Shishkin } 4407bd1d409SAlexander Shishkin 4417bd1d409SAlexander Shishkin stm->policy = kzalloc(sizeof(*stm->policy), GFP_KERNEL); 4427bd1d409SAlexander Shishkin if (!stm->policy) { 443c7fd62bcSAlexander Shishkin mutex_unlock(&stm->policy_mutex); 444c7fd62bcSAlexander Shishkin stm_put_protocol(pdrv); 445c7fd62bcSAlexander Shishkin stm_put_device(stm); 446c7fd62bcSAlexander Shishkin return ERR_PTR(-ENOMEM); 4477bd1d409SAlexander Shishkin } 4487bd1d409SAlexander Shishkin 4497bd1d409SAlexander Shishkin config_group_init_type_name(&stm->policy->group, name, 4507bd1d409SAlexander Shishkin &stp_policy_type); 4517bd1d409SAlexander Shishkin 452c7fd62bcSAlexander Shishkin stm->pdrv = pdrv; 453c7fd62bcSAlexander Shishkin stm->pdrv_node_type = pdrv_node_type; 454c7fd62bcSAlexander Shishkin stm->policy->stm = stm; 4557bd1d409SAlexander Shishkin ret = &stm->policy->group; 4567bd1d409SAlexander Shishkin 4577bd1d409SAlexander Shishkin unlock_policy: 4587bd1d409SAlexander Shishkin mutex_unlock(&stm->policy_mutex); 4597bd1d409SAlexander Shishkin 460c7fd62bcSAlexander Shishkin if (IS_ERR(ret)) { 461c7fd62bcSAlexander Shishkin stm_put_protocol(stm->pdrv); 4627bd1d409SAlexander Shishkin stm_put_device(stm); 463c7fd62bcSAlexander Shishkin } 4647bd1d409SAlexander Shishkin 4657bd1d409SAlexander Shishkin return ret; 4667bd1d409SAlexander Shishkin } 4677bd1d409SAlexander Shishkin 46825e3c006SAlexander Shishkin static struct configfs_group_operations stp_policy_root_group_ops = { 46925e3c006SAlexander Shishkin .make_group = stp_policy_make, 4707bd1d409SAlexander Shishkin }; 4717bd1d409SAlexander Shishkin 47225e3c006SAlexander Shishkin static const struct config_item_type stp_policy_root_type = { 47325e3c006SAlexander Shishkin .ct_group_ops = &stp_policy_root_group_ops, 4747bd1d409SAlexander Shishkin .ct_owner = THIS_MODULE, 4757bd1d409SAlexander Shishkin }; 4767bd1d409SAlexander Shishkin 4777bd1d409SAlexander Shishkin static struct configfs_subsystem stp_policy_subsys = { 4787bd1d409SAlexander Shishkin .su_group = { 4797bd1d409SAlexander Shishkin .cg_item = { 4807bd1d409SAlexander Shishkin .ci_namebuf = "stp-policy", 48125e3c006SAlexander Shishkin .ci_type = &stp_policy_root_type, 4827bd1d409SAlexander Shishkin }, 4837bd1d409SAlexander Shishkin }, 4847bd1d409SAlexander Shishkin }; 4857bd1d409SAlexander Shishkin 4867bd1d409SAlexander Shishkin /* 4877bd1d409SAlexander Shishkin * Lock the policy mutex from the outside 4887bd1d409SAlexander Shishkin */ 4897bd1d409SAlexander Shishkin static struct stp_policy_node * 4907bd1d409SAlexander Shishkin __stp_policy_node_lookup(struct stp_policy *policy, char *s) 4917bd1d409SAlexander Shishkin { 492cb6102bdSAlexander Shishkin struct stp_policy_node *policy_node, *ret = NULL; 4937bd1d409SAlexander Shishkin struct list_head *head = &policy->group.cg_children; 4947bd1d409SAlexander Shishkin struct config_item *item; 4957bd1d409SAlexander Shishkin char *start, *end = s; 4967bd1d409SAlexander Shishkin 4977bd1d409SAlexander Shishkin if (list_empty(head)) 4987bd1d409SAlexander Shishkin return NULL; 4997bd1d409SAlexander Shishkin 5007bd1d409SAlexander Shishkin next: 5017bd1d409SAlexander Shishkin for (;;) { 5027bd1d409SAlexander Shishkin start = strsep(&end, "/"); 5037bd1d409SAlexander Shishkin if (!start) 5047bd1d409SAlexander Shishkin break; 5057bd1d409SAlexander Shishkin 5067bd1d409SAlexander Shishkin if (!*start) 5077bd1d409SAlexander Shishkin continue; 5087bd1d409SAlexander Shishkin 5097bd1d409SAlexander Shishkin list_for_each_entry(item, head, ci_entry) { 5107bd1d409SAlexander Shishkin policy_node = to_stp_policy_node(item); 5117bd1d409SAlexander Shishkin 5127bd1d409SAlexander Shishkin if (!strcmp(start, 5137bd1d409SAlexander Shishkin policy_node->group.cg_item.ci_name)) { 5147bd1d409SAlexander Shishkin ret = policy_node; 5157bd1d409SAlexander Shishkin 5167bd1d409SAlexander Shishkin if (!end) 5177bd1d409SAlexander Shishkin goto out; 5187bd1d409SAlexander Shishkin 5197bd1d409SAlexander Shishkin head = &policy_node->group.cg_children; 5207bd1d409SAlexander Shishkin goto next; 5217bd1d409SAlexander Shishkin } 5227bd1d409SAlexander Shishkin } 5237bd1d409SAlexander Shishkin break; 5247bd1d409SAlexander Shishkin } 5257bd1d409SAlexander Shishkin 5267bd1d409SAlexander Shishkin out: 5277bd1d409SAlexander Shishkin return ret; 5287bd1d409SAlexander Shishkin } 5297bd1d409SAlexander Shishkin 5307bd1d409SAlexander Shishkin 5317bd1d409SAlexander Shishkin struct stp_policy_node * 5327bd1d409SAlexander Shishkin stp_policy_node_lookup(struct stm_device *stm, char *s) 5337bd1d409SAlexander Shishkin { 5347bd1d409SAlexander Shishkin struct stp_policy_node *policy_node = NULL; 5357bd1d409SAlexander Shishkin 5367bd1d409SAlexander Shishkin mutex_lock(&stp_policy_subsys.su_mutex); 5377bd1d409SAlexander Shishkin 5387bd1d409SAlexander Shishkin mutex_lock(&stm->policy_mutex); 5397bd1d409SAlexander Shishkin if (stm->policy) 5407bd1d409SAlexander Shishkin policy_node = __stp_policy_node_lookup(stm->policy, s); 5417bd1d409SAlexander Shishkin mutex_unlock(&stm->policy_mutex); 5427bd1d409SAlexander Shishkin 5437bd1d409SAlexander Shishkin if (policy_node) 5447bd1d409SAlexander Shishkin config_item_get(&policy_node->group.cg_item); 545cb6102bdSAlexander Shishkin else 5467bd1d409SAlexander Shishkin mutex_unlock(&stp_policy_subsys.su_mutex); 5477bd1d409SAlexander Shishkin 5487bd1d409SAlexander Shishkin return policy_node; 5497bd1d409SAlexander Shishkin } 5507bd1d409SAlexander Shishkin 5517bd1d409SAlexander Shishkin void stp_policy_node_put(struct stp_policy_node *policy_node) 5527bd1d409SAlexander Shishkin { 553cb6102bdSAlexander Shishkin lockdep_assert_held(&stp_policy_subsys.su_mutex); 554cb6102bdSAlexander Shishkin 555cb6102bdSAlexander Shishkin mutex_unlock(&stp_policy_subsys.su_mutex); 5567bd1d409SAlexander Shishkin config_item_put(&policy_node->group.cg_item); 5577bd1d409SAlexander Shishkin } 5587bd1d409SAlexander Shishkin 5597bd1d409SAlexander Shishkin int __init stp_configfs_init(void) 5607bd1d409SAlexander Shishkin { 5617bd1d409SAlexander Shishkin config_group_init(&stp_policy_subsys.su_group); 5627bd1d409SAlexander Shishkin mutex_init(&stp_policy_subsys.su_mutex); 563e967b8bdSAlexander Shishkin return configfs_register_subsystem(&stp_policy_subsys); 5647bd1d409SAlexander Shishkin } 5657bd1d409SAlexander Shishkin 5667bd1d409SAlexander Shishkin void __exit stp_configfs_exit(void) 5677bd1d409SAlexander Shishkin { 5687bd1d409SAlexander Shishkin configfs_unregister_subsystem(&stp_policy_subsys); 5697bd1d409SAlexander Shishkin } 570