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 /* lifted from arch/x86/events/core.c */ 206c7fd62bcSAlexander Shishkin static struct configfs_attribute **merge_attr(struct configfs_attribute **a, struct configfs_attribute **b) 207c7fd62bcSAlexander Shishkin { 208c7fd62bcSAlexander Shishkin struct configfs_attribute **new; 209c7fd62bcSAlexander Shishkin int j, i; 210c7fd62bcSAlexander Shishkin 211c7fd62bcSAlexander Shishkin for (j = 0; a[j]; j++) 212c7fd62bcSAlexander Shishkin ; 213c7fd62bcSAlexander Shishkin for (i = 0; b[i]; i++) 214c7fd62bcSAlexander Shishkin j++; 215c7fd62bcSAlexander Shishkin j++; 216c7fd62bcSAlexander Shishkin 217c7fd62bcSAlexander Shishkin new = kmalloc_array(j, sizeof(struct configfs_attribute *), 218c7fd62bcSAlexander Shishkin GFP_KERNEL); 219c7fd62bcSAlexander Shishkin if (!new) 220c7fd62bcSAlexander Shishkin return NULL; 221c7fd62bcSAlexander Shishkin 222c7fd62bcSAlexander Shishkin j = 0; 223c7fd62bcSAlexander Shishkin for (i = 0; a[i]; i++) 224c7fd62bcSAlexander Shishkin new[j++] = a[i]; 225c7fd62bcSAlexander Shishkin for (i = 0; b[i]; i++) 226c7fd62bcSAlexander Shishkin new[j++] = b[i]; 227c7fd62bcSAlexander Shishkin new[j] = NULL; 228c7fd62bcSAlexander Shishkin 229c7fd62bcSAlexander Shishkin return new; 230c7fd62bcSAlexander Shishkin } 231c7fd62bcSAlexander Shishkin 232c7fd62bcSAlexander Shishkin const struct config_item_type * 233c7fd62bcSAlexander Shishkin get_policy_node_type(struct configfs_attribute **attrs) 234c7fd62bcSAlexander Shishkin { 235c7fd62bcSAlexander Shishkin struct config_item_type *type; 236c7fd62bcSAlexander Shishkin struct configfs_attribute **merged; 237c7fd62bcSAlexander Shishkin 238c7fd62bcSAlexander Shishkin type = kmemdup(&stp_policy_node_type, sizeof(stp_policy_node_type), 239c7fd62bcSAlexander Shishkin GFP_KERNEL); 240c7fd62bcSAlexander Shishkin if (!type) 241c7fd62bcSAlexander Shishkin return NULL; 242c7fd62bcSAlexander Shishkin 243c7fd62bcSAlexander Shishkin merged = merge_attr(stp_policy_node_attrs, attrs); 244c7fd62bcSAlexander Shishkin if (!merged) { 245c7fd62bcSAlexander Shishkin kfree(type); 246c7fd62bcSAlexander Shishkin return NULL; 247c7fd62bcSAlexander Shishkin } 248c7fd62bcSAlexander Shishkin 249c7fd62bcSAlexander Shishkin type->ct_attrs = merged; 250c7fd62bcSAlexander Shishkin 251c7fd62bcSAlexander Shishkin return type; 252c7fd62bcSAlexander Shishkin } 253c7fd62bcSAlexander Shishkin 2547bd1d409SAlexander Shishkin static struct config_group * 2557bd1d409SAlexander Shishkin stp_policy_node_make(struct config_group *group, const char *name) 2567bd1d409SAlexander Shishkin { 257c7fd62bcSAlexander Shishkin const struct config_item_type *type = &stp_policy_node_type; 2587bd1d409SAlexander Shishkin struct stp_policy_node *policy_node, *parent_node; 259c7fd62bcSAlexander Shishkin const struct stm_protocol_driver *pdrv; 2607bd1d409SAlexander Shishkin struct stp_policy *policy; 2617bd1d409SAlexander Shishkin 2627bd1d409SAlexander Shishkin if (group->cg_item.ci_type == &stp_policy_type) { 2637bd1d409SAlexander Shishkin policy = container_of(group, struct stp_policy, group); 2647bd1d409SAlexander Shishkin } else { 2657bd1d409SAlexander Shishkin parent_node = container_of(group, struct stp_policy_node, 2667bd1d409SAlexander Shishkin group); 2677bd1d409SAlexander Shishkin policy = parent_node->policy; 2687bd1d409SAlexander Shishkin } 2697bd1d409SAlexander Shishkin 2707bd1d409SAlexander Shishkin if (!policy->stm) 2717bd1d409SAlexander Shishkin return ERR_PTR(-ENODEV); 2727bd1d409SAlexander Shishkin 273c7fd62bcSAlexander Shishkin pdrv = policy->stm->pdrv; 274c7fd62bcSAlexander Shishkin policy_node = 275c7fd62bcSAlexander Shishkin kzalloc(offsetof(struct stp_policy_node, priv[pdrv->priv_sz]), 276c7fd62bcSAlexander Shishkin GFP_KERNEL); 2777bd1d409SAlexander Shishkin if (!policy_node) 2787bd1d409SAlexander Shishkin return ERR_PTR(-ENOMEM); 2797bd1d409SAlexander Shishkin 280c7fd62bcSAlexander Shishkin if (pdrv->policy_node_init) 281c7fd62bcSAlexander Shishkin pdrv->policy_node_init((void *)policy_node->priv); 282c7fd62bcSAlexander Shishkin 283c7fd62bcSAlexander Shishkin if (policy->stm->pdrv_node_type) 284c7fd62bcSAlexander Shishkin type = policy->stm->pdrv_node_type; 285c7fd62bcSAlexander Shishkin 286c7fd62bcSAlexander Shishkin config_group_init_type_name(&policy_node->group, name, type); 2877bd1d409SAlexander Shishkin 2887bd1d409SAlexander Shishkin policy_node->policy = policy; 2897bd1d409SAlexander Shishkin 2907bd1d409SAlexander Shishkin /* default values for the attributes */ 2917bd1d409SAlexander Shishkin policy_node->first_master = policy->stm->data->sw_start; 2927bd1d409SAlexander Shishkin policy_node->last_master = policy->stm->data->sw_end; 2937bd1d409SAlexander Shishkin policy_node->first_channel = 0; 2947bd1d409SAlexander Shishkin policy_node->last_channel = policy->stm->data->sw_nchannels - 1; 2957bd1d409SAlexander Shishkin 2967bd1d409SAlexander Shishkin return &policy_node->group; 2977bd1d409SAlexander Shishkin } 2987bd1d409SAlexander Shishkin 2997bd1d409SAlexander Shishkin static void 3007bd1d409SAlexander Shishkin stp_policy_node_drop(struct config_group *group, struct config_item *item) 3017bd1d409SAlexander Shishkin { 3027bd1d409SAlexander Shishkin config_item_put(item); 3037bd1d409SAlexander Shishkin } 3047bd1d409SAlexander Shishkin 3057bd1d409SAlexander Shishkin static struct configfs_group_operations stp_policy_node_group_ops = { 3067bd1d409SAlexander Shishkin .make_group = stp_policy_node_make, 3077bd1d409SAlexander Shishkin .drop_item = stp_policy_node_drop, 3087bd1d409SAlexander Shishkin }; 3097bd1d409SAlexander Shishkin 310085006e8SBhumika Goyal static const struct config_item_type stp_policy_node_type = { 3117bd1d409SAlexander Shishkin .ct_item_ops = &stp_policy_node_item_ops, 3127bd1d409SAlexander Shishkin .ct_group_ops = &stp_policy_node_group_ops, 3137bd1d409SAlexander Shishkin .ct_attrs = stp_policy_node_attrs, 3147bd1d409SAlexander Shishkin .ct_owner = THIS_MODULE, 3157bd1d409SAlexander Shishkin }; 3167bd1d409SAlexander Shishkin 3177bd1d409SAlexander Shishkin /* 3187bd1d409SAlexander Shishkin * Root group: policies. 3197bd1d409SAlexander Shishkin */ 3209aa3d651SLinus Torvalds static ssize_t stp_policy_device_show(struct config_item *item, 3217bd1d409SAlexander Shishkin char *page) 3227bd1d409SAlexander Shishkin { 3237bd1d409SAlexander Shishkin struct stp_policy *policy = to_stp_policy(item); 3247bd1d409SAlexander Shishkin ssize_t count; 3257bd1d409SAlexander Shishkin 3267bd1d409SAlexander Shishkin count = sprintf(page, "%s\n", 3277bd1d409SAlexander Shishkin (policy && policy->stm) ? 3287bd1d409SAlexander Shishkin policy->stm->data->name : 3297bd1d409SAlexander Shishkin "<none>"); 3307bd1d409SAlexander Shishkin 3317bd1d409SAlexander Shishkin return count; 3327bd1d409SAlexander Shishkin } 3337bd1d409SAlexander Shishkin 3349aa3d651SLinus Torvalds CONFIGFS_ATTR_RO(stp_policy_, device); 3359aa3d651SLinus Torvalds 336c7fd62bcSAlexander Shishkin static ssize_t stp_policy_protocol_show(struct config_item *item, 337c7fd62bcSAlexander Shishkin char *page) 338c7fd62bcSAlexander Shishkin { 339c7fd62bcSAlexander Shishkin struct stp_policy *policy = to_stp_policy(item); 340c7fd62bcSAlexander Shishkin ssize_t count; 341c7fd62bcSAlexander Shishkin 342c7fd62bcSAlexander Shishkin count = sprintf(page, "%s\n", 343c7fd62bcSAlexander Shishkin (policy && policy->stm) ? 344c7fd62bcSAlexander Shishkin policy->stm->pdrv->name : 345c7fd62bcSAlexander Shishkin "<none>"); 346c7fd62bcSAlexander Shishkin 347c7fd62bcSAlexander Shishkin return count; 348c7fd62bcSAlexander Shishkin } 349c7fd62bcSAlexander Shishkin 350c7fd62bcSAlexander Shishkin CONFIGFS_ATTR_RO(stp_policy_, protocol); 351c7fd62bcSAlexander Shishkin 3529aa3d651SLinus Torvalds static struct configfs_attribute *stp_policy_attrs[] = { 3539aa3d651SLinus Torvalds &stp_policy_attr_device, 354c7fd62bcSAlexander Shishkin &stp_policy_attr_protocol, 3559aa3d651SLinus Torvalds NULL, 3569aa3d651SLinus Torvalds }; 3579aa3d651SLinus Torvalds 3587bd1d409SAlexander Shishkin void stp_policy_unbind(struct stp_policy *policy) 3597bd1d409SAlexander Shishkin { 3607bd1d409SAlexander Shishkin struct stm_device *stm = policy->stm; 3617bd1d409SAlexander Shishkin 3624c127fd1SAlexander Shishkin /* 3634c127fd1SAlexander Shishkin * stp_policy_release() will not call here if the policy is already 3644c127fd1SAlexander Shishkin * unbound; other users should not either, as no link exists between 3654c127fd1SAlexander Shishkin * this policy and anything else in that case 3664c127fd1SAlexander Shishkin */ 3677bd1d409SAlexander Shishkin if (WARN_ON_ONCE(!policy->stm)) 3687bd1d409SAlexander Shishkin return; 3697bd1d409SAlexander Shishkin 3704c127fd1SAlexander Shishkin lockdep_assert_held(&stm->policy_mutex); 3717bd1d409SAlexander Shishkin 3724c127fd1SAlexander Shishkin stm->policy = NULL; 3737bd1d409SAlexander Shishkin policy->stm = NULL; 3747bd1d409SAlexander Shishkin 375c7fd62bcSAlexander Shishkin stm_put_protocol(stm->pdrv); 3767bd1d409SAlexander Shishkin stm_put_device(stm); 3777bd1d409SAlexander Shishkin } 3787bd1d409SAlexander Shishkin 3797bd1d409SAlexander Shishkin static void stp_policy_release(struct config_item *item) 3807bd1d409SAlexander Shishkin { 3817bd1d409SAlexander Shishkin struct stp_policy *policy = to_stp_policy(item); 3824c127fd1SAlexander Shishkin struct stm_device *stm = policy->stm; 3837bd1d409SAlexander Shishkin 3844c127fd1SAlexander Shishkin /* a policy *can* be unbound and still exist in configfs tree */ 3854c127fd1SAlexander Shishkin if (!stm) 3864c127fd1SAlexander Shishkin return; 3874c127fd1SAlexander Shishkin 3884c127fd1SAlexander Shishkin mutex_lock(&stm->policy_mutex); 3897bd1d409SAlexander Shishkin stp_policy_unbind(policy); 3904c127fd1SAlexander Shishkin mutex_unlock(&stm->policy_mutex); 3914c127fd1SAlexander Shishkin 3927bd1d409SAlexander Shishkin kfree(policy); 3937bd1d409SAlexander Shishkin } 3947bd1d409SAlexander Shishkin 3957bd1d409SAlexander Shishkin static struct configfs_item_operations stp_policy_item_ops = { 3967bd1d409SAlexander Shishkin .release = stp_policy_release, 3977bd1d409SAlexander Shishkin }; 3987bd1d409SAlexander Shishkin 3997bd1d409SAlexander Shishkin static struct configfs_group_operations stp_policy_group_ops = { 4007bd1d409SAlexander Shishkin .make_group = stp_policy_node_make, 4017bd1d409SAlexander Shishkin }; 4027bd1d409SAlexander Shishkin 403085006e8SBhumika Goyal static const struct config_item_type stp_policy_type = { 4047bd1d409SAlexander Shishkin .ct_item_ops = &stp_policy_item_ops, 4057bd1d409SAlexander Shishkin .ct_group_ops = &stp_policy_group_ops, 4067bd1d409SAlexander Shishkin .ct_attrs = stp_policy_attrs, 4077bd1d409SAlexander Shishkin .ct_owner = THIS_MODULE, 4087bd1d409SAlexander Shishkin }; 4097bd1d409SAlexander Shishkin 4107bd1d409SAlexander Shishkin static struct config_group * 41125e3c006SAlexander Shishkin stp_policy_make(struct config_group *group, const char *name) 4127bd1d409SAlexander Shishkin { 413c7fd62bcSAlexander Shishkin const struct config_item_type *pdrv_node_type; 414c7fd62bcSAlexander Shishkin const struct stm_protocol_driver *pdrv; 415c7fd62bcSAlexander Shishkin char *devname, *proto, *p; 4167bd1d409SAlexander Shishkin struct config_group *ret; 4177bd1d409SAlexander Shishkin struct stm_device *stm; 418c7fd62bcSAlexander Shishkin int err; 4197bd1d409SAlexander Shishkin 4207bd1d409SAlexander Shishkin devname = kasprintf(GFP_KERNEL, "%s", name); 4217bd1d409SAlexander Shishkin if (!devname) 4227bd1d409SAlexander Shishkin return ERR_PTR(-ENOMEM); 4237bd1d409SAlexander Shishkin 4247bd1d409SAlexander Shishkin /* 4257bd1d409SAlexander Shishkin * node must look like <device_name>.<policy_name>, where 42659be422eSAlexander Shishkin * <device_name> is the name of an existing stm device; may 42759be422eSAlexander Shishkin * contain dots; 42859be422eSAlexander Shishkin * <policy_name> is an arbitrary string; may not contain dots 429c7fd62bcSAlexander Shishkin * <device_name>:<protocol_name>.<policy_name> 4307bd1d409SAlexander Shishkin */ 43159be422eSAlexander Shishkin p = strrchr(devname, '.'); 4327bd1d409SAlexander Shishkin if (!p) { 4337bd1d409SAlexander Shishkin kfree(devname); 4347bd1d409SAlexander Shishkin return ERR_PTR(-EINVAL); 4357bd1d409SAlexander Shishkin } 4367bd1d409SAlexander Shishkin 437fb080190SAlexander Shishkin *p = '\0'; 4387bd1d409SAlexander Shishkin 439c7fd62bcSAlexander Shishkin /* 440c7fd62bcSAlexander Shishkin * look for ":<protocol_name>": 441c7fd62bcSAlexander Shishkin * + no protocol suffix: fall back to whatever is available; 442c7fd62bcSAlexander Shishkin * + unknown protocol: fail the whole thing 443c7fd62bcSAlexander Shishkin */ 444c7fd62bcSAlexander Shishkin proto = strrchr(devname, ':'); 445c7fd62bcSAlexander Shishkin if (proto) 446c7fd62bcSAlexander Shishkin *proto++ = '\0'; 447c7fd62bcSAlexander Shishkin 4487bd1d409SAlexander Shishkin stm = stm_find_device(devname); 449c7fd62bcSAlexander Shishkin if (!stm) { 450c7fd62bcSAlexander Shishkin kfree(devname); 451c7fd62bcSAlexander Shishkin return ERR_PTR(-ENODEV); 452c7fd62bcSAlexander Shishkin } 453c7fd62bcSAlexander Shishkin 454c7fd62bcSAlexander Shishkin err = stm_lookup_protocol(proto, &pdrv, &pdrv_node_type); 4557bd1d409SAlexander Shishkin kfree(devname); 4567bd1d409SAlexander Shishkin 457c7fd62bcSAlexander Shishkin /* We don't have any protocol drivers yet */ 458c7fd62bcSAlexander Shishkin if (err != -ENOENT) { 459c7fd62bcSAlexander Shishkin stm_put_device(stm); 4607bd1d409SAlexander Shishkin return ERR_PTR(-ENODEV); 461c7fd62bcSAlexander Shishkin } 4627bd1d409SAlexander Shishkin 4637bd1d409SAlexander Shishkin mutex_lock(&stm->policy_mutex); 4647bd1d409SAlexander Shishkin if (stm->policy) { 4657bd1d409SAlexander Shishkin ret = ERR_PTR(-EBUSY); 4667bd1d409SAlexander Shishkin goto unlock_policy; 4677bd1d409SAlexander Shishkin } 4687bd1d409SAlexander Shishkin 4697bd1d409SAlexander Shishkin stm->policy = kzalloc(sizeof(*stm->policy), GFP_KERNEL); 4707bd1d409SAlexander Shishkin if (!stm->policy) { 471c7fd62bcSAlexander Shishkin mutex_unlock(&stm->policy_mutex); 472c7fd62bcSAlexander Shishkin stm_put_protocol(pdrv); 473c7fd62bcSAlexander Shishkin stm_put_device(stm); 474c7fd62bcSAlexander Shishkin return ERR_PTR(-ENOMEM); 4757bd1d409SAlexander Shishkin } 4767bd1d409SAlexander Shishkin 4777bd1d409SAlexander Shishkin config_group_init_type_name(&stm->policy->group, name, 4787bd1d409SAlexander Shishkin &stp_policy_type); 4797bd1d409SAlexander Shishkin 480c7fd62bcSAlexander Shishkin stm->pdrv = pdrv; 481c7fd62bcSAlexander Shishkin stm->pdrv_node_type = pdrv_node_type; 482c7fd62bcSAlexander Shishkin stm->policy->stm = stm; 4837bd1d409SAlexander Shishkin ret = &stm->policy->group; 4847bd1d409SAlexander Shishkin 4857bd1d409SAlexander Shishkin unlock_policy: 4867bd1d409SAlexander Shishkin mutex_unlock(&stm->policy_mutex); 4877bd1d409SAlexander Shishkin 488c7fd62bcSAlexander Shishkin if (IS_ERR(ret)) { 489c7fd62bcSAlexander Shishkin stm_put_protocol(stm->pdrv); 4907bd1d409SAlexander Shishkin stm_put_device(stm); 491c7fd62bcSAlexander Shishkin } 4927bd1d409SAlexander Shishkin 4937bd1d409SAlexander Shishkin return ret; 4947bd1d409SAlexander Shishkin } 4957bd1d409SAlexander Shishkin 49625e3c006SAlexander Shishkin static struct configfs_group_operations stp_policy_root_group_ops = { 49725e3c006SAlexander Shishkin .make_group = stp_policy_make, 4987bd1d409SAlexander Shishkin }; 4997bd1d409SAlexander Shishkin 50025e3c006SAlexander Shishkin static const struct config_item_type stp_policy_root_type = { 50125e3c006SAlexander Shishkin .ct_group_ops = &stp_policy_root_group_ops, 5027bd1d409SAlexander Shishkin .ct_owner = THIS_MODULE, 5037bd1d409SAlexander Shishkin }; 5047bd1d409SAlexander Shishkin 5057bd1d409SAlexander Shishkin static struct configfs_subsystem stp_policy_subsys = { 5067bd1d409SAlexander Shishkin .su_group = { 5077bd1d409SAlexander Shishkin .cg_item = { 5087bd1d409SAlexander Shishkin .ci_namebuf = "stp-policy", 50925e3c006SAlexander Shishkin .ci_type = &stp_policy_root_type, 5107bd1d409SAlexander Shishkin }, 5117bd1d409SAlexander Shishkin }, 5127bd1d409SAlexander Shishkin }; 5137bd1d409SAlexander Shishkin 5147bd1d409SAlexander Shishkin /* 5157bd1d409SAlexander Shishkin * Lock the policy mutex from the outside 5167bd1d409SAlexander Shishkin */ 5177bd1d409SAlexander Shishkin static struct stp_policy_node * 5187bd1d409SAlexander Shishkin __stp_policy_node_lookup(struct stp_policy *policy, char *s) 5197bd1d409SAlexander Shishkin { 520cb6102bdSAlexander Shishkin struct stp_policy_node *policy_node, *ret = NULL; 5217bd1d409SAlexander Shishkin struct list_head *head = &policy->group.cg_children; 5227bd1d409SAlexander Shishkin struct config_item *item; 5237bd1d409SAlexander Shishkin char *start, *end = s; 5247bd1d409SAlexander Shishkin 5257bd1d409SAlexander Shishkin if (list_empty(head)) 5267bd1d409SAlexander Shishkin return NULL; 5277bd1d409SAlexander Shishkin 5287bd1d409SAlexander Shishkin next: 5297bd1d409SAlexander Shishkin for (;;) { 5307bd1d409SAlexander Shishkin start = strsep(&end, "/"); 5317bd1d409SAlexander Shishkin if (!start) 5327bd1d409SAlexander Shishkin break; 5337bd1d409SAlexander Shishkin 5347bd1d409SAlexander Shishkin if (!*start) 5357bd1d409SAlexander Shishkin continue; 5367bd1d409SAlexander Shishkin 5377bd1d409SAlexander Shishkin list_for_each_entry(item, head, ci_entry) { 5387bd1d409SAlexander Shishkin policy_node = to_stp_policy_node(item); 5397bd1d409SAlexander Shishkin 5407bd1d409SAlexander Shishkin if (!strcmp(start, 5417bd1d409SAlexander Shishkin policy_node->group.cg_item.ci_name)) { 5427bd1d409SAlexander Shishkin ret = policy_node; 5437bd1d409SAlexander Shishkin 5447bd1d409SAlexander Shishkin if (!end) 5457bd1d409SAlexander Shishkin goto out; 5467bd1d409SAlexander Shishkin 5477bd1d409SAlexander Shishkin head = &policy_node->group.cg_children; 5487bd1d409SAlexander Shishkin goto next; 5497bd1d409SAlexander Shishkin } 5507bd1d409SAlexander Shishkin } 5517bd1d409SAlexander Shishkin break; 5527bd1d409SAlexander Shishkin } 5537bd1d409SAlexander Shishkin 5547bd1d409SAlexander Shishkin out: 5557bd1d409SAlexander Shishkin return ret; 5567bd1d409SAlexander Shishkin } 5577bd1d409SAlexander Shishkin 5587bd1d409SAlexander Shishkin 5597bd1d409SAlexander Shishkin struct stp_policy_node * 5607bd1d409SAlexander Shishkin stp_policy_node_lookup(struct stm_device *stm, char *s) 5617bd1d409SAlexander Shishkin { 5627bd1d409SAlexander Shishkin struct stp_policy_node *policy_node = NULL; 5637bd1d409SAlexander Shishkin 5647bd1d409SAlexander Shishkin mutex_lock(&stp_policy_subsys.su_mutex); 5657bd1d409SAlexander Shishkin 5667bd1d409SAlexander Shishkin mutex_lock(&stm->policy_mutex); 5677bd1d409SAlexander Shishkin if (stm->policy) 5687bd1d409SAlexander Shishkin policy_node = __stp_policy_node_lookup(stm->policy, s); 5697bd1d409SAlexander Shishkin mutex_unlock(&stm->policy_mutex); 5707bd1d409SAlexander Shishkin 5717bd1d409SAlexander Shishkin if (policy_node) 5727bd1d409SAlexander Shishkin config_item_get(&policy_node->group.cg_item); 573cb6102bdSAlexander Shishkin else 5747bd1d409SAlexander Shishkin mutex_unlock(&stp_policy_subsys.su_mutex); 5757bd1d409SAlexander Shishkin 5767bd1d409SAlexander Shishkin return policy_node; 5777bd1d409SAlexander Shishkin } 5787bd1d409SAlexander Shishkin 5797bd1d409SAlexander Shishkin void stp_policy_node_put(struct stp_policy_node *policy_node) 5807bd1d409SAlexander Shishkin { 581cb6102bdSAlexander Shishkin lockdep_assert_held(&stp_policy_subsys.su_mutex); 582cb6102bdSAlexander Shishkin 583cb6102bdSAlexander Shishkin mutex_unlock(&stp_policy_subsys.su_mutex); 5847bd1d409SAlexander Shishkin config_item_put(&policy_node->group.cg_item); 5857bd1d409SAlexander Shishkin } 5867bd1d409SAlexander Shishkin 5877bd1d409SAlexander Shishkin int __init stp_configfs_init(void) 5887bd1d409SAlexander Shishkin { 5897bd1d409SAlexander Shishkin config_group_init(&stp_policy_subsys.su_group); 5907bd1d409SAlexander Shishkin mutex_init(&stp_policy_subsys.su_mutex); 591e967b8bdSAlexander Shishkin return configfs_register_subsystem(&stp_policy_subsys); 5927bd1d409SAlexander Shishkin } 5937bd1d409SAlexander Shishkin 5947bd1d409SAlexander Shishkin void __exit stp_configfs_exit(void) 5957bd1d409SAlexander Shishkin { 5967bd1d409SAlexander Shishkin configfs_unregister_subsystem(&stp_policy_subsys); 5977bd1d409SAlexander Shishkin } 598