xref: /openbmc/linux/drivers/hwtracing/stm/policy.c (revision f57af6df)
17bd1d409SAlexander Shishkin /*
27bd1d409SAlexander Shishkin  * System Trace Module (STM) master/channel allocation policy management
37bd1d409SAlexander Shishkin  * Copyright (c) 2014, Intel Corporation.
47bd1d409SAlexander Shishkin  *
57bd1d409SAlexander Shishkin  * This program is free software; you can redistribute it and/or modify it
67bd1d409SAlexander Shishkin  * under the terms and conditions of the GNU General Public License,
77bd1d409SAlexander Shishkin  * version 2, as published by the Free Software Foundation.
87bd1d409SAlexander Shishkin  *
97bd1d409SAlexander Shishkin  * This program is distributed in the hope it will be useful, but WITHOUT
107bd1d409SAlexander Shishkin  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
117bd1d409SAlexander Shishkin  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
127bd1d409SAlexander Shishkin  * more details.
137bd1d409SAlexander Shishkin  *
147bd1d409SAlexander Shishkin  * A master/channel allocation policy allows mapping string identifiers to
157bd1d409SAlexander Shishkin  * master and channel ranges, where allocation can be done.
167bd1d409SAlexander Shishkin  */
177bd1d409SAlexander Shishkin 
187bd1d409SAlexander Shishkin #define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
197bd1d409SAlexander Shishkin 
207bd1d409SAlexander Shishkin #include <linux/types.h>
217bd1d409SAlexander Shishkin #include <linux/module.h>
227bd1d409SAlexander Shishkin #include <linux/device.h>
237bd1d409SAlexander Shishkin #include <linux/configfs.h>
247bd1d409SAlexander Shishkin #include <linux/slab.h>
257bd1d409SAlexander Shishkin #include <linux/stm.h>
267bd1d409SAlexander Shishkin #include "stm.h"
277bd1d409SAlexander Shishkin 
287bd1d409SAlexander Shishkin /*
297bd1d409SAlexander Shishkin  * STP Master/Channel allocation policy configfs layout.
307bd1d409SAlexander Shishkin  */
317bd1d409SAlexander Shishkin 
327bd1d409SAlexander Shishkin struct stp_policy {
337bd1d409SAlexander Shishkin 	struct config_group	group;
347bd1d409SAlexander Shishkin 	struct stm_device	*stm;
357bd1d409SAlexander Shishkin };
367bd1d409SAlexander Shishkin 
377bd1d409SAlexander Shishkin struct stp_policy_node {
387bd1d409SAlexander Shishkin 	struct config_group	group;
397bd1d409SAlexander Shishkin 	struct stp_policy	*policy;
407bd1d409SAlexander Shishkin 	unsigned int		first_master;
417bd1d409SAlexander Shishkin 	unsigned int		last_master;
427bd1d409SAlexander Shishkin 	unsigned int		first_channel;
437bd1d409SAlexander Shishkin 	unsigned int		last_channel;
447bd1d409SAlexander Shishkin };
457bd1d409SAlexander Shishkin 
467bd1d409SAlexander Shishkin static struct configfs_subsystem stp_policy_subsys;
477bd1d409SAlexander Shishkin 
487bd1d409SAlexander Shishkin void stp_policy_node_get_ranges(struct stp_policy_node *policy_node,
497bd1d409SAlexander Shishkin 				unsigned int *mstart, unsigned int *mend,
507bd1d409SAlexander Shishkin 				unsigned int *cstart, unsigned int *cend)
517bd1d409SAlexander Shishkin {
527bd1d409SAlexander Shishkin 	*mstart	= policy_node->first_master;
537bd1d409SAlexander Shishkin 	*mend	= policy_node->last_master;
547bd1d409SAlexander Shishkin 	*cstart	= policy_node->first_channel;
557bd1d409SAlexander Shishkin 	*cend	= policy_node->last_channel;
567bd1d409SAlexander Shishkin }
577bd1d409SAlexander Shishkin 
587bd1d409SAlexander Shishkin static inline char *stp_policy_node_name(struct stp_policy_node *policy_node)
597bd1d409SAlexander Shishkin {
607bd1d409SAlexander Shishkin 	return policy_node->group.cg_item.ci_name ? : "<none>";
617bd1d409SAlexander Shishkin }
627bd1d409SAlexander Shishkin 
637bd1d409SAlexander Shishkin static inline struct stp_policy *to_stp_policy(struct config_item *item)
647bd1d409SAlexander Shishkin {
657bd1d409SAlexander Shishkin 	return item ?
667bd1d409SAlexander Shishkin 		container_of(to_config_group(item), struct stp_policy, group) :
677bd1d409SAlexander Shishkin 		NULL;
687bd1d409SAlexander Shishkin }
697bd1d409SAlexander Shishkin 
707bd1d409SAlexander Shishkin static inline struct stp_policy_node *
717bd1d409SAlexander Shishkin to_stp_policy_node(struct config_item *item)
727bd1d409SAlexander Shishkin {
737bd1d409SAlexander Shishkin 	return item ?
747bd1d409SAlexander Shishkin 		container_of(to_config_group(item), struct stp_policy_node,
757bd1d409SAlexander Shishkin 			     group) :
767bd1d409SAlexander Shishkin 		NULL;
777bd1d409SAlexander Shishkin }
787bd1d409SAlexander Shishkin 
799aa3d651SLinus Torvalds static ssize_t
809aa3d651SLinus Torvalds stp_policy_node_masters_show(struct config_item *item, char *page)
817bd1d409SAlexander Shishkin {
829aa3d651SLinus Torvalds 	struct stp_policy_node *policy_node = to_stp_policy_node(item);
837bd1d409SAlexander Shishkin 	ssize_t count;
847bd1d409SAlexander Shishkin 
857bd1d409SAlexander Shishkin 	count = sprintf(page, "%u %u\n", policy_node->first_master,
867bd1d409SAlexander Shishkin 			policy_node->last_master);
877bd1d409SAlexander Shishkin 
887bd1d409SAlexander Shishkin 	return count;
897bd1d409SAlexander Shishkin }
907bd1d409SAlexander Shishkin 
917bd1d409SAlexander Shishkin static ssize_t
929aa3d651SLinus Torvalds stp_policy_node_masters_store(struct config_item *item, const char *page,
939aa3d651SLinus Torvalds 			      size_t count)
947bd1d409SAlexander Shishkin {
959aa3d651SLinus Torvalds 	struct stp_policy_node *policy_node = to_stp_policy_node(item);
967bd1d409SAlexander Shishkin 	unsigned int first, last;
977bd1d409SAlexander Shishkin 	struct stm_device *stm;
987bd1d409SAlexander Shishkin 	char *p = (char *)page;
997bd1d409SAlexander Shishkin 	ssize_t ret = -ENODEV;
1007bd1d409SAlexander Shishkin 
1017bd1d409SAlexander Shishkin 	if (sscanf(p, "%u %u", &first, &last) != 2)
1027bd1d409SAlexander Shishkin 		return -EINVAL;
1037bd1d409SAlexander Shishkin 
1047bd1d409SAlexander Shishkin 	mutex_lock(&stp_policy_subsys.su_mutex);
1057bd1d409SAlexander Shishkin 	stm = policy_node->policy->stm;
1067bd1d409SAlexander Shishkin 	if (!stm)
1077bd1d409SAlexander Shishkin 		goto unlock;
1087bd1d409SAlexander Shishkin 
1097bd1d409SAlexander Shishkin 	/* must be within [sw_start..sw_end], which is an inclusive range */
110f57af6dfSChunyan Zhang 	if (first > last || first < stm->data->sw_start ||
1117bd1d409SAlexander Shishkin 	    last > stm->data->sw_end) {
1127bd1d409SAlexander Shishkin 		ret = -ERANGE;
1137bd1d409SAlexander Shishkin 		goto unlock;
1147bd1d409SAlexander Shishkin 	}
1157bd1d409SAlexander Shishkin 
1167bd1d409SAlexander Shishkin 	ret = count;
1177bd1d409SAlexander Shishkin 	policy_node->first_master = first;
1187bd1d409SAlexander Shishkin 	policy_node->last_master = last;
1197bd1d409SAlexander Shishkin 
1207bd1d409SAlexander Shishkin unlock:
1217bd1d409SAlexander Shishkin 	mutex_unlock(&stp_policy_subsys.su_mutex);
1227bd1d409SAlexander Shishkin 
1237bd1d409SAlexander Shishkin 	return ret;
1247bd1d409SAlexander Shishkin }
1257bd1d409SAlexander Shishkin 
1267bd1d409SAlexander Shishkin static ssize_t
1279aa3d651SLinus Torvalds stp_policy_node_channels_show(struct config_item *item, char *page)
1287bd1d409SAlexander Shishkin {
1299aa3d651SLinus Torvalds 	struct stp_policy_node *policy_node = to_stp_policy_node(item);
1307bd1d409SAlexander Shishkin 	ssize_t count;
1317bd1d409SAlexander Shishkin 
1327bd1d409SAlexander Shishkin 	count = sprintf(page, "%u %u\n", policy_node->first_channel,
1337bd1d409SAlexander Shishkin 			policy_node->last_channel);
1347bd1d409SAlexander Shishkin 
1357bd1d409SAlexander Shishkin 	return count;
1367bd1d409SAlexander Shishkin }
1377bd1d409SAlexander Shishkin 
1387bd1d409SAlexander Shishkin static ssize_t
1399aa3d651SLinus Torvalds stp_policy_node_channels_store(struct config_item *item, const char *page,
1409aa3d651SLinus Torvalds 			       size_t count)
1417bd1d409SAlexander Shishkin {
1429aa3d651SLinus Torvalds 	struct stp_policy_node *policy_node = to_stp_policy_node(item);
1437bd1d409SAlexander Shishkin 	unsigned int first, last;
1447bd1d409SAlexander Shishkin 	struct stm_device *stm;
1457bd1d409SAlexander Shishkin 	char *p = (char *)page;
1467bd1d409SAlexander Shishkin 	ssize_t ret = -ENODEV;
1477bd1d409SAlexander Shishkin 
1487bd1d409SAlexander Shishkin 	if (sscanf(p, "%u %u", &first, &last) != 2)
1497bd1d409SAlexander Shishkin 		return -EINVAL;
1507bd1d409SAlexander Shishkin 
1517bd1d409SAlexander Shishkin 	mutex_lock(&stp_policy_subsys.su_mutex);
1527bd1d409SAlexander Shishkin 	stm = policy_node->policy->stm;
1537bd1d409SAlexander Shishkin 	if (!stm)
1547bd1d409SAlexander Shishkin 		goto unlock;
1557bd1d409SAlexander Shishkin 
1567bd1d409SAlexander Shishkin 	if (first > INT_MAX || last > INT_MAX || first > last ||
1577bd1d409SAlexander Shishkin 	    last >= stm->data->sw_nchannels) {
1587bd1d409SAlexander Shishkin 		ret = -ERANGE;
1597bd1d409SAlexander Shishkin 		goto unlock;
1607bd1d409SAlexander Shishkin 	}
1617bd1d409SAlexander Shishkin 
1627bd1d409SAlexander Shishkin 	ret = count;
1637bd1d409SAlexander Shishkin 	policy_node->first_channel = first;
1647bd1d409SAlexander Shishkin 	policy_node->last_channel = last;
1657bd1d409SAlexander Shishkin 
1667bd1d409SAlexander Shishkin unlock:
1677bd1d409SAlexander Shishkin 	mutex_unlock(&stp_policy_subsys.su_mutex);
1687bd1d409SAlexander Shishkin 
1697bd1d409SAlexander Shishkin 	return ret;
1707bd1d409SAlexander Shishkin }
1717bd1d409SAlexander Shishkin 
1727bd1d409SAlexander Shishkin static void stp_policy_node_release(struct config_item *item)
1737bd1d409SAlexander Shishkin {
1747bd1d409SAlexander Shishkin 	kfree(to_stp_policy_node(item));
1757bd1d409SAlexander Shishkin }
1767bd1d409SAlexander Shishkin 
1777bd1d409SAlexander Shishkin static struct configfs_item_operations stp_policy_node_item_ops = {
1787bd1d409SAlexander Shishkin 	.release		= stp_policy_node_release,
1797bd1d409SAlexander Shishkin };
1807bd1d409SAlexander Shishkin 
1819aa3d651SLinus Torvalds CONFIGFS_ATTR(stp_policy_node_, masters);
1829aa3d651SLinus Torvalds CONFIGFS_ATTR(stp_policy_node_, channels);
1837bd1d409SAlexander Shishkin 
1847bd1d409SAlexander Shishkin static struct configfs_attribute *stp_policy_node_attrs[] = {
1859aa3d651SLinus Torvalds 	&stp_policy_node_attr_masters,
1869aa3d651SLinus Torvalds 	&stp_policy_node_attr_channels,
1877bd1d409SAlexander Shishkin 	NULL,
1887bd1d409SAlexander Shishkin };
1897bd1d409SAlexander Shishkin 
1907bd1d409SAlexander Shishkin static struct config_item_type stp_policy_type;
1917bd1d409SAlexander Shishkin static struct config_item_type stp_policy_node_type;
1927bd1d409SAlexander Shishkin 
1937bd1d409SAlexander Shishkin static struct config_group *
1947bd1d409SAlexander Shishkin stp_policy_node_make(struct config_group *group, const char *name)
1957bd1d409SAlexander Shishkin {
1967bd1d409SAlexander Shishkin 	struct stp_policy_node *policy_node, *parent_node;
1977bd1d409SAlexander Shishkin 	struct stp_policy *policy;
1987bd1d409SAlexander Shishkin 
1997bd1d409SAlexander Shishkin 	if (group->cg_item.ci_type == &stp_policy_type) {
2007bd1d409SAlexander Shishkin 		policy = container_of(group, struct stp_policy, group);
2017bd1d409SAlexander Shishkin 	} else {
2027bd1d409SAlexander Shishkin 		parent_node = container_of(group, struct stp_policy_node,
2037bd1d409SAlexander Shishkin 					   group);
2047bd1d409SAlexander Shishkin 		policy = parent_node->policy;
2057bd1d409SAlexander Shishkin 	}
2067bd1d409SAlexander Shishkin 
2077bd1d409SAlexander Shishkin 	if (!policy->stm)
2087bd1d409SAlexander Shishkin 		return ERR_PTR(-ENODEV);
2097bd1d409SAlexander Shishkin 
2107bd1d409SAlexander Shishkin 	policy_node = kzalloc(sizeof(struct stp_policy_node), GFP_KERNEL);
2117bd1d409SAlexander Shishkin 	if (!policy_node)
2127bd1d409SAlexander Shishkin 		return ERR_PTR(-ENOMEM);
2137bd1d409SAlexander Shishkin 
2147bd1d409SAlexander Shishkin 	config_group_init_type_name(&policy_node->group, name,
2157bd1d409SAlexander Shishkin 				    &stp_policy_node_type);
2167bd1d409SAlexander Shishkin 
2177bd1d409SAlexander Shishkin 	policy_node->policy = policy;
2187bd1d409SAlexander Shishkin 
2197bd1d409SAlexander Shishkin 	/* default values for the attributes */
2207bd1d409SAlexander Shishkin 	policy_node->first_master = policy->stm->data->sw_start;
2217bd1d409SAlexander Shishkin 	policy_node->last_master = policy->stm->data->sw_end;
2227bd1d409SAlexander Shishkin 	policy_node->first_channel = 0;
2237bd1d409SAlexander Shishkin 	policy_node->last_channel = policy->stm->data->sw_nchannels - 1;
2247bd1d409SAlexander Shishkin 
2257bd1d409SAlexander Shishkin 	return &policy_node->group;
2267bd1d409SAlexander Shishkin }
2277bd1d409SAlexander Shishkin 
2287bd1d409SAlexander Shishkin static void
2297bd1d409SAlexander Shishkin stp_policy_node_drop(struct config_group *group, struct config_item *item)
2307bd1d409SAlexander Shishkin {
2317bd1d409SAlexander Shishkin 	config_item_put(item);
2327bd1d409SAlexander Shishkin }
2337bd1d409SAlexander Shishkin 
2347bd1d409SAlexander Shishkin static struct configfs_group_operations stp_policy_node_group_ops = {
2357bd1d409SAlexander Shishkin 	.make_group	= stp_policy_node_make,
2367bd1d409SAlexander Shishkin 	.drop_item	= stp_policy_node_drop,
2377bd1d409SAlexander Shishkin };
2387bd1d409SAlexander Shishkin 
2397bd1d409SAlexander Shishkin static struct config_item_type stp_policy_node_type = {
2407bd1d409SAlexander Shishkin 	.ct_item_ops	= &stp_policy_node_item_ops,
2417bd1d409SAlexander Shishkin 	.ct_group_ops	= &stp_policy_node_group_ops,
2427bd1d409SAlexander Shishkin 	.ct_attrs	= stp_policy_node_attrs,
2437bd1d409SAlexander Shishkin 	.ct_owner	= THIS_MODULE,
2447bd1d409SAlexander Shishkin };
2457bd1d409SAlexander Shishkin 
2467bd1d409SAlexander Shishkin /*
2477bd1d409SAlexander Shishkin  * Root group: policies.
2487bd1d409SAlexander Shishkin  */
2499aa3d651SLinus Torvalds static ssize_t stp_policy_device_show(struct config_item *item,
2507bd1d409SAlexander Shishkin 				      char *page)
2517bd1d409SAlexander Shishkin {
2527bd1d409SAlexander Shishkin 	struct stp_policy *policy = to_stp_policy(item);
2537bd1d409SAlexander Shishkin 	ssize_t count;
2547bd1d409SAlexander Shishkin 
2557bd1d409SAlexander Shishkin 	count = sprintf(page, "%s\n",
2567bd1d409SAlexander Shishkin 			(policy && policy->stm) ?
2577bd1d409SAlexander Shishkin 			policy->stm->data->name :
2587bd1d409SAlexander Shishkin 			"<none>");
2597bd1d409SAlexander Shishkin 
2607bd1d409SAlexander Shishkin 	return count;
2617bd1d409SAlexander Shishkin }
2627bd1d409SAlexander Shishkin 
2639aa3d651SLinus Torvalds CONFIGFS_ATTR_RO(stp_policy_, device);
2649aa3d651SLinus Torvalds 
2659aa3d651SLinus Torvalds static struct configfs_attribute *stp_policy_attrs[] = {
2669aa3d651SLinus Torvalds 	&stp_policy_attr_device,
2679aa3d651SLinus Torvalds 	NULL,
2689aa3d651SLinus Torvalds };
2699aa3d651SLinus Torvalds 
2707bd1d409SAlexander Shishkin void stp_policy_unbind(struct stp_policy *policy)
2717bd1d409SAlexander Shishkin {
2727bd1d409SAlexander Shishkin 	struct stm_device *stm = policy->stm;
2737bd1d409SAlexander Shishkin 
2744c127fd1SAlexander Shishkin 	/*
2754c127fd1SAlexander Shishkin 	 * stp_policy_release() will not call here if the policy is already
2764c127fd1SAlexander Shishkin 	 * unbound; other users should not either, as no link exists between
2774c127fd1SAlexander Shishkin 	 * this policy and anything else in that case
2784c127fd1SAlexander Shishkin 	 */
2797bd1d409SAlexander Shishkin 	if (WARN_ON_ONCE(!policy->stm))
2807bd1d409SAlexander Shishkin 		return;
2817bd1d409SAlexander Shishkin 
2824c127fd1SAlexander Shishkin 	lockdep_assert_held(&stm->policy_mutex);
2837bd1d409SAlexander Shishkin 
2844c127fd1SAlexander Shishkin 	stm->policy = NULL;
2857bd1d409SAlexander Shishkin 	policy->stm = NULL;
2867bd1d409SAlexander Shishkin 
2877bd1d409SAlexander Shishkin 	stm_put_device(stm);
2887bd1d409SAlexander Shishkin }
2897bd1d409SAlexander Shishkin 
2907bd1d409SAlexander Shishkin static void stp_policy_release(struct config_item *item)
2917bd1d409SAlexander Shishkin {
2927bd1d409SAlexander Shishkin 	struct stp_policy *policy = to_stp_policy(item);
2934c127fd1SAlexander Shishkin 	struct stm_device *stm = policy->stm;
2947bd1d409SAlexander Shishkin 
2954c127fd1SAlexander Shishkin 	/* a policy *can* be unbound and still exist in configfs tree */
2964c127fd1SAlexander Shishkin 	if (!stm)
2974c127fd1SAlexander Shishkin 		return;
2984c127fd1SAlexander Shishkin 
2994c127fd1SAlexander Shishkin 	mutex_lock(&stm->policy_mutex);
3007bd1d409SAlexander Shishkin 	stp_policy_unbind(policy);
3014c127fd1SAlexander Shishkin 	mutex_unlock(&stm->policy_mutex);
3024c127fd1SAlexander Shishkin 
3037bd1d409SAlexander Shishkin 	kfree(policy);
3047bd1d409SAlexander Shishkin }
3057bd1d409SAlexander Shishkin 
3067bd1d409SAlexander Shishkin static struct configfs_item_operations stp_policy_item_ops = {
3077bd1d409SAlexander Shishkin 	.release		= stp_policy_release,
3087bd1d409SAlexander Shishkin };
3097bd1d409SAlexander Shishkin 
3107bd1d409SAlexander Shishkin static struct configfs_group_operations stp_policy_group_ops = {
3117bd1d409SAlexander Shishkin 	.make_group	= stp_policy_node_make,
3127bd1d409SAlexander Shishkin };
3137bd1d409SAlexander Shishkin 
3147bd1d409SAlexander Shishkin static struct config_item_type stp_policy_type = {
3157bd1d409SAlexander Shishkin 	.ct_item_ops	= &stp_policy_item_ops,
3167bd1d409SAlexander Shishkin 	.ct_group_ops	= &stp_policy_group_ops,
3177bd1d409SAlexander Shishkin 	.ct_attrs	= stp_policy_attrs,
3187bd1d409SAlexander Shishkin 	.ct_owner	= THIS_MODULE,
3197bd1d409SAlexander Shishkin };
3207bd1d409SAlexander Shishkin 
3217bd1d409SAlexander Shishkin static struct config_group *
3227bd1d409SAlexander Shishkin stp_policies_make(struct config_group *group, const char *name)
3237bd1d409SAlexander Shishkin {
3247bd1d409SAlexander Shishkin 	struct config_group *ret;
3257bd1d409SAlexander Shishkin 	struct stm_device *stm;
3267bd1d409SAlexander Shishkin 	char *devname, *p;
3277bd1d409SAlexander Shishkin 
3287bd1d409SAlexander Shishkin 	devname = kasprintf(GFP_KERNEL, "%s", name);
3297bd1d409SAlexander Shishkin 	if (!devname)
3307bd1d409SAlexander Shishkin 		return ERR_PTR(-ENOMEM);
3317bd1d409SAlexander Shishkin 
3327bd1d409SAlexander Shishkin 	/*
3337bd1d409SAlexander Shishkin 	 * node must look like <device_name>.<policy_name>, where
33459be422eSAlexander Shishkin 	 * <device_name> is the name of an existing stm device; may
33559be422eSAlexander Shishkin 	 *               contain dots;
33659be422eSAlexander Shishkin 	 * <policy_name> is an arbitrary string; may not contain dots
3377bd1d409SAlexander Shishkin 	 */
33859be422eSAlexander Shishkin 	p = strrchr(devname, '.');
3397bd1d409SAlexander Shishkin 	if (!p) {
3407bd1d409SAlexander Shishkin 		kfree(devname);
3417bd1d409SAlexander Shishkin 		return ERR_PTR(-EINVAL);
3427bd1d409SAlexander Shishkin 	}
3437bd1d409SAlexander Shishkin 
3447bd1d409SAlexander Shishkin 	*p++ = '\0';
3457bd1d409SAlexander Shishkin 
3467bd1d409SAlexander Shishkin 	stm = stm_find_device(devname);
3477bd1d409SAlexander Shishkin 	kfree(devname);
3487bd1d409SAlexander Shishkin 
3497bd1d409SAlexander Shishkin 	if (!stm)
3507bd1d409SAlexander Shishkin 		return ERR_PTR(-ENODEV);
3517bd1d409SAlexander Shishkin 
3527bd1d409SAlexander Shishkin 	mutex_lock(&stm->policy_mutex);
3537bd1d409SAlexander Shishkin 	if (stm->policy) {
3547bd1d409SAlexander Shishkin 		ret = ERR_PTR(-EBUSY);
3557bd1d409SAlexander Shishkin 		goto unlock_policy;
3567bd1d409SAlexander Shishkin 	}
3577bd1d409SAlexander Shishkin 
3587bd1d409SAlexander Shishkin 	stm->policy = kzalloc(sizeof(*stm->policy), GFP_KERNEL);
3597bd1d409SAlexander Shishkin 	if (!stm->policy) {
3607bd1d409SAlexander Shishkin 		ret = ERR_PTR(-ENOMEM);
3617bd1d409SAlexander Shishkin 		goto unlock_policy;
3627bd1d409SAlexander Shishkin 	}
3637bd1d409SAlexander Shishkin 
3647bd1d409SAlexander Shishkin 	config_group_init_type_name(&stm->policy->group, name,
3657bd1d409SAlexander Shishkin 				    &stp_policy_type);
3667bd1d409SAlexander Shishkin 	stm->policy->stm = stm;
3677bd1d409SAlexander Shishkin 
3687bd1d409SAlexander Shishkin 	ret = &stm->policy->group;
3697bd1d409SAlexander Shishkin 
3707bd1d409SAlexander Shishkin unlock_policy:
3717bd1d409SAlexander Shishkin 	mutex_unlock(&stm->policy_mutex);
3727bd1d409SAlexander Shishkin 
3737bd1d409SAlexander Shishkin 	if (IS_ERR(ret))
3747bd1d409SAlexander Shishkin 		stm_put_device(stm);
3757bd1d409SAlexander Shishkin 
3767bd1d409SAlexander Shishkin 	return ret;
3777bd1d409SAlexander Shishkin }
3787bd1d409SAlexander Shishkin 
3797bd1d409SAlexander Shishkin static struct configfs_group_operations stp_policies_group_ops = {
3807bd1d409SAlexander Shishkin 	.make_group	= stp_policies_make,
3817bd1d409SAlexander Shishkin };
3827bd1d409SAlexander Shishkin 
3837bd1d409SAlexander Shishkin static struct config_item_type stp_policies_type = {
3847bd1d409SAlexander Shishkin 	.ct_group_ops	= &stp_policies_group_ops,
3857bd1d409SAlexander Shishkin 	.ct_owner	= THIS_MODULE,
3867bd1d409SAlexander Shishkin };
3877bd1d409SAlexander Shishkin 
3887bd1d409SAlexander Shishkin static struct configfs_subsystem stp_policy_subsys = {
3897bd1d409SAlexander Shishkin 	.su_group = {
3907bd1d409SAlexander Shishkin 		.cg_item = {
3917bd1d409SAlexander Shishkin 			.ci_namebuf	= "stp-policy",
3927bd1d409SAlexander Shishkin 			.ci_type	= &stp_policies_type,
3937bd1d409SAlexander Shishkin 		},
3947bd1d409SAlexander Shishkin 	},
3957bd1d409SAlexander Shishkin };
3967bd1d409SAlexander Shishkin 
3977bd1d409SAlexander Shishkin /*
3987bd1d409SAlexander Shishkin  * Lock the policy mutex from the outside
3997bd1d409SAlexander Shishkin  */
4007bd1d409SAlexander Shishkin static struct stp_policy_node *
4017bd1d409SAlexander Shishkin __stp_policy_node_lookup(struct stp_policy *policy, char *s)
4027bd1d409SAlexander Shishkin {
4037bd1d409SAlexander Shishkin 	struct stp_policy_node *policy_node, *ret;
4047bd1d409SAlexander Shishkin 	struct list_head *head = &policy->group.cg_children;
4057bd1d409SAlexander Shishkin 	struct config_item *item;
4067bd1d409SAlexander Shishkin 	char *start, *end = s;
4077bd1d409SAlexander Shishkin 
4087bd1d409SAlexander Shishkin 	if (list_empty(head))
4097bd1d409SAlexander Shishkin 		return NULL;
4107bd1d409SAlexander Shishkin 
4117bd1d409SAlexander Shishkin 	/* return the first entry if everything else fails */
4127bd1d409SAlexander Shishkin 	item = list_entry(head->next, struct config_item, ci_entry);
4137bd1d409SAlexander Shishkin 	ret = to_stp_policy_node(item);
4147bd1d409SAlexander Shishkin 
4157bd1d409SAlexander Shishkin next:
4167bd1d409SAlexander Shishkin 	for (;;) {
4177bd1d409SAlexander Shishkin 		start = strsep(&end, "/");
4187bd1d409SAlexander Shishkin 		if (!start)
4197bd1d409SAlexander Shishkin 			break;
4207bd1d409SAlexander Shishkin 
4217bd1d409SAlexander Shishkin 		if (!*start)
4227bd1d409SAlexander Shishkin 			continue;
4237bd1d409SAlexander Shishkin 
4247bd1d409SAlexander Shishkin 		list_for_each_entry(item, head, ci_entry) {
4257bd1d409SAlexander Shishkin 			policy_node = to_stp_policy_node(item);
4267bd1d409SAlexander Shishkin 
4277bd1d409SAlexander Shishkin 			if (!strcmp(start,
4287bd1d409SAlexander Shishkin 				    policy_node->group.cg_item.ci_name)) {
4297bd1d409SAlexander Shishkin 				ret = policy_node;
4307bd1d409SAlexander Shishkin 
4317bd1d409SAlexander Shishkin 				if (!end)
4327bd1d409SAlexander Shishkin 					goto out;
4337bd1d409SAlexander Shishkin 
4347bd1d409SAlexander Shishkin 				head = &policy_node->group.cg_children;
4357bd1d409SAlexander Shishkin 				goto next;
4367bd1d409SAlexander Shishkin 			}
4377bd1d409SAlexander Shishkin 		}
4387bd1d409SAlexander Shishkin 		break;
4397bd1d409SAlexander Shishkin 	}
4407bd1d409SAlexander Shishkin 
4417bd1d409SAlexander Shishkin out:
4427bd1d409SAlexander Shishkin 	return ret;
4437bd1d409SAlexander Shishkin }
4447bd1d409SAlexander Shishkin 
4457bd1d409SAlexander Shishkin 
4467bd1d409SAlexander Shishkin struct stp_policy_node *
4477bd1d409SAlexander Shishkin stp_policy_node_lookup(struct stm_device *stm, char *s)
4487bd1d409SAlexander Shishkin {
4497bd1d409SAlexander Shishkin 	struct stp_policy_node *policy_node = NULL;
4507bd1d409SAlexander Shishkin 
4517bd1d409SAlexander Shishkin 	mutex_lock(&stp_policy_subsys.su_mutex);
4527bd1d409SAlexander Shishkin 
4537bd1d409SAlexander Shishkin 	mutex_lock(&stm->policy_mutex);
4547bd1d409SAlexander Shishkin 	if (stm->policy)
4557bd1d409SAlexander Shishkin 		policy_node = __stp_policy_node_lookup(stm->policy, s);
4567bd1d409SAlexander Shishkin 	mutex_unlock(&stm->policy_mutex);
4577bd1d409SAlexander Shishkin 
4587bd1d409SAlexander Shishkin 	if (policy_node)
4597bd1d409SAlexander Shishkin 		config_item_get(&policy_node->group.cg_item);
4607bd1d409SAlexander Shishkin 	mutex_unlock(&stp_policy_subsys.su_mutex);
4617bd1d409SAlexander Shishkin 
4627bd1d409SAlexander Shishkin 	return policy_node;
4637bd1d409SAlexander Shishkin }
4647bd1d409SAlexander Shishkin 
4657bd1d409SAlexander Shishkin void stp_policy_node_put(struct stp_policy_node *policy_node)
4667bd1d409SAlexander Shishkin {
4677bd1d409SAlexander Shishkin 	config_item_put(&policy_node->group.cg_item);
4687bd1d409SAlexander Shishkin }
4697bd1d409SAlexander Shishkin 
4707bd1d409SAlexander Shishkin int __init stp_configfs_init(void)
4717bd1d409SAlexander Shishkin {
4727bd1d409SAlexander Shishkin 	int err;
4737bd1d409SAlexander Shishkin 
4747bd1d409SAlexander Shishkin 	config_group_init(&stp_policy_subsys.su_group);
4757bd1d409SAlexander Shishkin 	mutex_init(&stp_policy_subsys.su_mutex);
4767bd1d409SAlexander Shishkin 	err = configfs_register_subsystem(&stp_policy_subsys);
4777bd1d409SAlexander Shishkin 
4787bd1d409SAlexander Shishkin 	return err;
4797bd1d409SAlexander Shishkin }
4807bd1d409SAlexander Shishkin 
4817bd1d409SAlexander Shishkin void __exit stp_configfs_exit(void)
4827bd1d409SAlexander Shishkin {
4837bd1d409SAlexander Shishkin 	configfs_unregister_subsystem(&stp_policy_subsys);
4847bd1d409SAlexander Shishkin }
485