xref: /openbmc/linux/drivers/hwtracing/stm/policy.c (revision 9aa3d651)
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 */
1107bd1d409SAlexander Shishkin 	if (first > INT_MAX || last > INT_MAX || first > last ||
1117bd1d409SAlexander Shishkin 	    first < stm->data->sw_start ||
1127bd1d409SAlexander Shishkin 	    last > stm->data->sw_end) {
1137bd1d409SAlexander Shishkin 		ret = -ERANGE;
1147bd1d409SAlexander Shishkin 		goto unlock;
1157bd1d409SAlexander Shishkin 	}
1167bd1d409SAlexander Shishkin 
1177bd1d409SAlexander Shishkin 	ret = count;
1187bd1d409SAlexander Shishkin 	policy_node->first_master = first;
1197bd1d409SAlexander Shishkin 	policy_node->last_master = last;
1207bd1d409SAlexander Shishkin 
1217bd1d409SAlexander Shishkin unlock:
1227bd1d409SAlexander Shishkin 	mutex_unlock(&stp_policy_subsys.su_mutex);
1237bd1d409SAlexander Shishkin 
1247bd1d409SAlexander Shishkin 	return ret;
1257bd1d409SAlexander Shishkin }
1267bd1d409SAlexander Shishkin 
1277bd1d409SAlexander Shishkin static ssize_t
1289aa3d651SLinus Torvalds stp_policy_node_channels_show(struct config_item *item, char *page)
1297bd1d409SAlexander Shishkin {
1309aa3d651SLinus Torvalds 	struct stp_policy_node *policy_node = to_stp_policy_node(item);
1317bd1d409SAlexander Shishkin 	ssize_t count;
1327bd1d409SAlexander Shishkin 
1337bd1d409SAlexander Shishkin 	count = sprintf(page, "%u %u\n", policy_node->first_channel,
1347bd1d409SAlexander Shishkin 			policy_node->last_channel);
1357bd1d409SAlexander Shishkin 
1367bd1d409SAlexander Shishkin 	return count;
1377bd1d409SAlexander Shishkin }
1387bd1d409SAlexander Shishkin 
1397bd1d409SAlexander Shishkin static ssize_t
1409aa3d651SLinus Torvalds stp_policy_node_channels_store(struct config_item *item, const char *page,
1419aa3d651SLinus Torvalds 			       size_t count)
1427bd1d409SAlexander Shishkin {
1439aa3d651SLinus Torvalds 	struct stp_policy_node *policy_node = to_stp_policy_node(item);
1447bd1d409SAlexander Shishkin 	unsigned int first, last;
1457bd1d409SAlexander Shishkin 	struct stm_device *stm;
1467bd1d409SAlexander Shishkin 	char *p = (char *)page;
1477bd1d409SAlexander Shishkin 	ssize_t ret = -ENODEV;
1487bd1d409SAlexander Shishkin 
1497bd1d409SAlexander Shishkin 	if (sscanf(p, "%u %u", &first, &last) != 2)
1507bd1d409SAlexander Shishkin 		return -EINVAL;
1517bd1d409SAlexander Shishkin 
1527bd1d409SAlexander Shishkin 	mutex_lock(&stp_policy_subsys.su_mutex);
1537bd1d409SAlexander Shishkin 	stm = policy_node->policy->stm;
1547bd1d409SAlexander Shishkin 	if (!stm)
1557bd1d409SAlexander Shishkin 		goto unlock;
1567bd1d409SAlexander Shishkin 
1577bd1d409SAlexander Shishkin 	if (first > INT_MAX || last > INT_MAX || first > last ||
1587bd1d409SAlexander Shishkin 	    last >= stm->data->sw_nchannels) {
1597bd1d409SAlexander Shishkin 		ret = -ERANGE;
1607bd1d409SAlexander Shishkin 		goto unlock;
1617bd1d409SAlexander Shishkin 	}
1627bd1d409SAlexander Shishkin 
1637bd1d409SAlexander Shishkin 	ret = count;
1647bd1d409SAlexander Shishkin 	policy_node->first_channel = first;
1657bd1d409SAlexander Shishkin 	policy_node->last_channel = last;
1667bd1d409SAlexander Shishkin 
1677bd1d409SAlexander Shishkin unlock:
1687bd1d409SAlexander Shishkin 	mutex_unlock(&stp_policy_subsys.su_mutex);
1697bd1d409SAlexander Shishkin 
1707bd1d409SAlexander Shishkin 	return ret;
1717bd1d409SAlexander Shishkin }
1727bd1d409SAlexander Shishkin 
1737bd1d409SAlexander Shishkin static void stp_policy_node_release(struct config_item *item)
1747bd1d409SAlexander Shishkin {
1757bd1d409SAlexander Shishkin 	kfree(to_stp_policy_node(item));
1767bd1d409SAlexander Shishkin }
1777bd1d409SAlexander Shishkin 
1787bd1d409SAlexander Shishkin static struct configfs_item_operations stp_policy_node_item_ops = {
1797bd1d409SAlexander Shishkin 	.release		= stp_policy_node_release,
1807bd1d409SAlexander Shishkin };
1817bd1d409SAlexander Shishkin 
1829aa3d651SLinus Torvalds CONFIGFS_ATTR(stp_policy_node_, masters);
1839aa3d651SLinus Torvalds CONFIGFS_ATTR(stp_policy_node_, channels);
1847bd1d409SAlexander Shishkin 
1857bd1d409SAlexander Shishkin static struct configfs_attribute *stp_policy_node_attrs[] = {
1869aa3d651SLinus Torvalds 	&stp_policy_node_attr_masters,
1879aa3d651SLinus Torvalds 	&stp_policy_node_attr_channels,
1887bd1d409SAlexander Shishkin 	NULL,
1897bd1d409SAlexander Shishkin };
1907bd1d409SAlexander Shishkin 
1917bd1d409SAlexander Shishkin static struct config_item_type stp_policy_type;
1927bd1d409SAlexander Shishkin static struct config_item_type stp_policy_node_type;
1937bd1d409SAlexander Shishkin 
1947bd1d409SAlexander Shishkin static struct config_group *
1957bd1d409SAlexander Shishkin stp_policy_node_make(struct config_group *group, const char *name)
1967bd1d409SAlexander Shishkin {
1977bd1d409SAlexander Shishkin 	struct stp_policy_node *policy_node, *parent_node;
1987bd1d409SAlexander Shishkin 	struct stp_policy *policy;
1997bd1d409SAlexander Shishkin 
2007bd1d409SAlexander Shishkin 	if (group->cg_item.ci_type == &stp_policy_type) {
2017bd1d409SAlexander Shishkin 		policy = container_of(group, struct stp_policy, group);
2027bd1d409SAlexander Shishkin 	} else {
2037bd1d409SAlexander Shishkin 		parent_node = container_of(group, struct stp_policy_node,
2047bd1d409SAlexander Shishkin 					   group);
2057bd1d409SAlexander Shishkin 		policy = parent_node->policy;
2067bd1d409SAlexander Shishkin 	}
2077bd1d409SAlexander Shishkin 
2087bd1d409SAlexander Shishkin 	if (!policy->stm)
2097bd1d409SAlexander Shishkin 		return ERR_PTR(-ENODEV);
2107bd1d409SAlexander Shishkin 
2117bd1d409SAlexander Shishkin 	policy_node = kzalloc(sizeof(struct stp_policy_node), GFP_KERNEL);
2127bd1d409SAlexander Shishkin 	if (!policy_node)
2137bd1d409SAlexander Shishkin 		return ERR_PTR(-ENOMEM);
2147bd1d409SAlexander Shishkin 
2157bd1d409SAlexander Shishkin 	config_group_init_type_name(&policy_node->group, name,
2167bd1d409SAlexander Shishkin 				    &stp_policy_node_type);
2177bd1d409SAlexander Shishkin 
2187bd1d409SAlexander Shishkin 	policy_node->policy = policy;
2197bd1d409SAlexander Shishkin 
2207bd1d409SAlexander Shishkin 	/* default values for the attributes */
2217bd1d409SAlexander Shishkin 	policy_node->first_master = policy->stm->data->sw_start;
2227bd1d409SAlexander Shishkin 	policy_node->last_master = policy->stm->data->sw_end;
2237bd1d409SAlexander Shishkin 	policy_node->first_channel = 0;
2247bd1d409SAlexander Shishkin 	policy_node->last_channel = policy->stm->data->sw_nchannels - 1;
2257bd1d409SAlexander Shishkin 
2267bd1d409SAlexander Shishkin 	return &policy_node->group;
2277bd1d409SAlexander Shishkin }
2287bd1d409SAlexander Shishkin 
2297bd1d409SAlexander Shishkin static void
2307bd1d409SAlexander Shishkin stp_policy_node_drop(struct config_group *group, struct config_item *item)
2317bd1d409SAlexander Shishkin {
2327bd1d409SAlexander Shishkin 	config_item_put(item);
2337bd1d409SAlexander Shishkin }
2347bd1d409SAlexander Shishkin 
2357bd1d409SAlexander Shishkin static struct configfs_group_operations stp_policy_node_group_ops = {
2367bd1d409SAlexander Shishkin 	.make_group	= stp_policy_node_make,
2377bd1d409SAlexander Shishkin 	.drop_item	= stp_policy_node_drop,
2387bd1d409SAlexander Shishkin };
2397bd1d409SAlexander Shishkin 
2407bd1d409SAlexander Shishkin static struct config_item_type stp_policy_node_type = {
2417bd1d409SAlexander Shishkin 	.ct_item_ops	= &stp_policy_node_item_ops,
2427bd1d409SAlexander Shishkin 	.ct_group_ops	= &stp_policy_node_group_ops,
2437bd1d409SAlexander Shishkin 	.ct_attrs	= stp_policy_node_attrs,
2447bd1d409SAlexander Shishkin 	.ct_owner	= THIS_MODULE,
2457bd1d409SAlexander Shishkin };
2467bd1d409SAlexander Shishkin 
2477bd1d409SAlexander Shishkin /*
2487bd1d409SAlexander Shishkin  * Root group: policies.
2497bd1d409SAlexander Shishkin  */
2509aa3d651SLinus Torvalds static ssize_t stp_policy_device_show(struct config_item *item,
2517bd1d409SAlexander Shishkin 				      char *page)
2527bd1d409SAlexander Shishkin {
2537bd1d409SAlexander Shishkin 	struct stp_policy *policy = to_stp_policy(item);
2547bd1d409SAlexander Shishkin 	ssize_t count;
2557bd1d409SAlexander Shishkin 
2567bd1d409SAlexander Shishkin 	count = sprintf(page, "%s\n",
2577bd1d409SAlexander Shishkin 			(policy && policy->stm) ?
2587bd1d409SAlexander Shishkin 			policy->stm->data->name :
2597bd1d409SAlexander Shishkin 			"<none>");
2607bd1d409SAlexander Shishkin 
2617bd1d409SAlexander Shishkin 	return count;
2627bd1d409SAlexander Shishkin }
2637bd1d409SAlexander Shishkin 
2649aa3d651SLinus Torvalds CONFIGFS_ATTR_RO(stp_policy_, device);
2659aa3d651SLinus Torvalds 
2669aa3d651SLinus Torvalds static struct configfs_attribute *stp_policy_attrs[] = {
2679aa3d651SLinus Torvalds 	&stp_policy_attr_device,
2689aa3d651SLinus Torvalds 	NULL,
2699aa3d651SLinus Torvalds };
2709aa3d651SLinus Torvalds 
2717bd1d409SAlexander Shishkin void stp_policy_unbind(struct stp_policy *policy)
2727bd1d409SAlexander Shishkin {
2737bd1d409SAlexander Shishkin 	struct stm_device *stm = policy->stm;
2747bd1d409SAlexander Shishkin 
2757bd1d409SAlexander Shishkin 	if (WARN_ON_ONCE(!policy->stm))
2767bd1d409SAlexander Shishkin 		return;
2777bd1d409SAlexander Shishkin 
2787bd1d409SAlexander Shishkin 	mutex_lock(&stm->policy_mutex);
2797bd1d409SAlexander Shishkin 	stm->policy = NULL;
2807bd1d409SAlexander Shishkin 	mutex_unlock(&stm->policy_mutex);
2817bd1d409SAlexander Shishkin 
2827bd1d409SAlexander Shishkin 	policy->stm = NULL;
2837bd1d409SAlexander Shishkin 
2847bd1d409SAlexander Shishkin 	stm_put_device(stm);
2857bd1d409SAlexander Shishkin }
2867bd1d409SAlexander Shishkin 
2877bd1d409SAlexander Shishkin static void stp_policy_release(struct config_item *item)
2887bd1d409SAlexander Shishkin {
2897bd1d409SAlexander Shishkin 	struct stp_policy *policy = to_stp_policy(item);
2907bd1d409SAlexander Shishkin 
2917bd1d409SAlexander Shishkin 	stp_policy_unbind(policy);
2927bd1d409SAlexander Shishkin 	kfree(policy);
2937bd1d409SAlexander Shishkin }
2947bd1d409SAlexander Shishkin 
2957bd1d409SAlexander Shishkin static struct configfs_item_operations stp_policy_item_ops = {
2967bd1d409SAlexander Shishkin 	.release		= stp_policy_release,
2977bd1d409SAlexander Shishkin };
2987bd1d409SAlexander Shishkin 
2997bd1d409SAlexander Shishkin static struct configfs_group_operations stp_policy_group_ops = {
3007bd1d409SAlexander Shishkin 	.make_group	= stp_policy_node_make,
3017bd1d409SAlexander Shishkin };
3027bd1d409SAlexander Shishkin 
3037bd1d409SAlexander Shishkin static struct config_item_type stp_policy_type = {
3047bd1d409SAlexander Shishkin 	.ct_item_ops	= &stp_policy_item_ops,
3057bd1d409SAlexander Shishkin 	.ct_group_ops	= &stp_policy_group_ops,
3067bd1d409SAlexander Shishkin 	.ct_attrs	= stp_policy_attrs,
3077bd1d409SAlexander Shishkin 	.ct_owner	= THIS_MODULE,
3087bd1d409SAlexander Shishkin };
3097bd1d409SAlexander Shishkin 
3107bd1d409SAlexander Shishkin static struct config_group *
3117bd1d409SAlexander Shishkin stp_policies_make(struct config_group *group, const char *name)
3127bd1d409SAlexander Shishkin {
3137bd1d409SAlexander Shishkin 	struct config_group *ret;
3147bd1d409SAlexander Shishkin 	struct stm_device *stm;
3157bd1d409SAlexander Shishkin 	char *devname, *p;
3167bd1d409SAlexander Shishkin 
3177bd1d409SAlexander Shishkin 	devname = kasprintf(GFP_KERNEL, "%s", name);
3187bd1d409SAlexander Shishkin 	if (!devname)
3197bd1d409SAlexander Shishkin 		return ERR_PTR(-ENOMEM);
3207bd1d409SAlexander Shishkin 
3217bd1d409SAlexander Shishkin 	/*
3227bd1d409SAlexander Shishkin 	 * node must look like <device_name>.<policy_name>, where
3237bd1d409SAlexander Shishkin 	 * <device_name> is the name of an existing stm device and
3247bd1d409SAlexander Shishkin 	 * <policy_name> is an arbitrary string
3257bd1d409SAlexander Shishkin 	 */
3267bd1d409SAlexander Shishkin 	p = strchr(devname, '.');
3277bd1d409SAlexander Shishkin 	if (!p) {
3287bd1d409SAlexander Shishkin 		kfree(devname);
3297bd1d409SAlexander Shishkin 		return ERR_PTR(-EINVAL);
3307bd1d409SAlexander Shishkin 	}
3317bd1d409SAlexander Shishkin 
3327bd1d409SAlexander Shishkin 	*p++ = '\0';
3337bd1d409SAlexander Shishkin 
3347bd1d409SAlexander Shishkin 	stm = stm_find_device(devname);
3357bd1d409SAlexander Shishkin 	kfree(devname);
3367bd1d409SAlexander Shishkin 
3377bd1d409SAlexander Shishkin 	if (!stm)
3387bd1d409SAlexander Shishkin 		return ERR_PTR(-ENODEV);
3397bd1d409SAlexander Shishkin 
3407bd1d409SAlexander Shishkin 	mutex_lock(&stm->policy_mutex);
3417bd1d409SAlexander Shishkin 	if (stm->policy) {
3427bd1d409SAlexander Shishkin 		ret = ERR_PTR(-EBUSY);
3437bd1d409SAlexander Shishkin 		goto unlock_policy;
3447bd1d409SAlexander Shishkin 	}
3457bd1d409SAlexander Shishkin 
3467bd1d409SAlexander Shishkin 	stm->policy = kzalloc(sizeof(*stm->policy), GFP_KERNEL);
3477bd1d409SAlexander Shishkin 	if (!stm->policy) {
3487bd1d409SAlexander Shishkin 		ret = ERR_PTR(-ENOMEM);
3497bd1d409SAlexander Shishkin 		goto unlock_policy;
3507bd1d409SAlexander Shishkin 	}
3517bd1d409SAlexander Shishkin 
3527bd1d409SAlexander Shishkin 	config_group_init_type_name(&stm->policy->group, name,
3537bd1d409SAlexander Shishkin 				    &stp_policy_type);
3547bd1d409SAlexander Shishkin 	stm->policy->stm = stm;
3557bd1d409SAlexander Shishkin 
3567bd1d409SAlexander Shishkin 	ret = &stm->policy->group;
3577bd1d409SAlexander Shishkin 
3587bd1d409SAlexander Shishkin unlock_policy:
3597bd1d409SAlexander Shishkin 	mutex_unlock(&stm->policy_mutex);
3607bd1d409SAlexander Shishkin 
3617bd1d409SAlexander Shishkin 	if (IS_ERR(ret))
3627bd1d409SAlexander Shishkin 		stm_put_device(stm);
3637bd1d409SAlexander Shishkin 
3647bd1d409SAlexander Shishkin 	return ret;
3657bd1d409SAlexander Shishkin }
3667bd1d409SAlexander Shishkin 
3677bd1d409SAlexander Shishkin static struct configfs_group_operations stp_policies_group_ops = {
3687bd1d409SAlexander Shishkin 	.make_group	= stp_policies_make,
3697bd1d409SAlexander Shishkin };
3707bd1d409SAlexander Shishkin 
3717bd1d409SAlexander Shishkin static struct config_item_type stp_policies_type = {
3727bd1d409SAlexander Shishkin 	.ct_group_ops	= &stp_policies_group_ops,
3737bd1d409SAlexander Shishkin 	.ct_owner	= THIS_MODULE,
3747bd1d409SAlexander Shishkin };
3757bd1d409SAlexander Shishkin 
3767bd1d409SAlexander Shishkin static struct configfs_subsystem stp_policy_subsys = {
3777bd1d409SAlexander Shishkin 	.su_group = {
3787bd1d409SAlexander Shishkin 		.cg_item = {
3797bd1d409SAlexander Shishkin 			.ci_namebuf	= "stp-policy",
3807bd1d409SAlexander Shishkin 			.ci_type	= &stp_policies_type,
3817bd1d409SAlexander Shishkin 		},
3827bd1d409SAlexander Shishkin 	},
3837bd1d409SAlexander Shishkin };
3847bd1d409SAlexander Shishkin 
3857bd1d409SAlexander Shishkin /*
3867bd1d409SAlexander Shishkin  * Lock the policy mutex from the outside
3877bd1d409SAlexander Shishkin  */
3887bd1d409SAlexander Shishkin static struct stp_policy_node *
3897bd1d409SAlexander Shishkin __stp_policy_node_lookup(struct stp_policy *policy, char *s)
3907bd1d409SAlexander Shishkin {
3917bd1d409SAlexander Shishkin 	struct stp_policy_node *policy_node, *ret;
3927bd1d409SAlexander Shishkin 	struct list_head *head = &policy->group.cg_children;
3937bd1d409SAlexander Shishkin 	struct config_item *item;
3947bd1d409SAlexander Shishkin 	char *start, *end = s;
3957bd1d409SAlexander Shishkin 
3967bd1d409SAlexander Shishkin 	if (list_empty(head))
3977bd1d409SAlexander Shishkin 		return NULL;
3987bd1d409SAlexander Shishkin 
3997bd1d409SAlexander Shishkin 	/* return the first entry if everything else fails */
4007bd1d409SAlexander Shishkin 	item = list_entry(head->next, struct config_item, ci_entry);
4017bd1d409SAlexander Shishkin 	ret = to_stp_policy_node(item);
4027bd1d409SAlexander Shishkin 
4037bd1d409SAlexander Shishkin next:
4047bd1d409SAlexander Shishkin 	for (;;) {
4057bd1d409SAlexander Shishkin 		start = strsep(&end, "/");
4067bd1d409SAlexander Shishkin 		if (!start)
4077bd1d409SAlexander Shishkin 			break;
4087bd1d409SAlexander Shishkin 
4097bd1d409SAlexander Shishkin 		if (!*start)
4107bd1d409SAlexander Shishkin 			continue;
4117bd1d409SAlexander Shishkin 
4127bd1d409SAlexander Shishkin 		list_for_each_entry(item, head, ci_entry) {
4137bd1d409SAlexander Shishkin 			policy_node = to_stp_policy_node(item);
4147bd1d409SAlexander Shishkin 
4157bd1d409SAlexander Shishkin 			if (!strcmp(start,
4167bd1d409SAlexander Shishkin 				    policy_node->group.cg_item.ci_name)) {
4177bd1d409SAlexander Shishkin 				ret = policy_node;
4187bd1d409SAlexander Shishkin 
4197bd1d409SAlexander Shishkin 				if (!end)
4207bd1d409SAlexander Shishkin 					goto out;
4217bd1d409SAlexander Shishkin 
4227bd1d409SAlexander Shishkin 				head = &policy_node->group.cg_children;
4237bd1d409SAlexander Shishkin 				goto next;
4247bd1d409SAlexander Shishkin 			}
4257bd1d409SAlexander Shishkin 		}
4267bd1d409SAlexander Shishkin 		break;
4277bd1d409SAlexander Shishkin 	}
4287bd1d409SAlexander Shishkin 
4297bd1d409SAlexander Shishkin out:
4307bd1d409SAlexander Shishkin 	return ret;
4317bd1d409SAlexander Shishkin }
4327bd1d409SAlexander Shishkin 
4337bd1d409SAlexander Shishkin 
4347bd1d409SAlexander Shishkin struct stp_policy_node *
4357bd1d409SAlexander Shishkin stp_policy_node_lookup(struct stm_device *stm, char *s)
4367bd1d409SAlexander Shishkin {
4377bd1d409SAlexander Shishkin 	struct stp_policy_node *policy_node = NULL;
4387bd1d409SAlexander Shishkin 
4397bd1d409SAlexander Shishkin 	mutex_lock(&stp_policy_subsys.su_mutex);
4407bd1d409SAlexander Shishkin 
4417bd1d409SAlexander Shishkin 	mutex_lock(&stm->policy_mutex);
4427bd1d409SAlexander Shishkin 	if (stm->policy)
4437bd1d409SAlexander Shishkin 		policy_node = __stp_policy_node_lookup(stm->policy, s);
4447bd1d409SAlexander Shishkin 	mutex_unlock(&stm->policy_mutex);
4457bd1d409SAlexander Shishkin 
4467bd1d409SAlexander Shishkin 	if (policy_node)
4477bd1d409SAlexander Shishkin 		config_item_get(&policy_node->group.cg_item);
4487bd1d409SAlexander Shishkin 	mutex_unlock(&stp_policy_subsys.su_mutex);
4497bd1d409SAlexander Shishkin 
4507bd1d409SAlexander Shishkin 	return policy_node;
4517bd1d409SAlexander Shishkin }
4527bd1d409SAlexander Shishkin 
4537bd1d409SAlexander Shishkin void stp_policy_node_put(struct stp_policy_node *policy_node)
4547bd1d409SAlexander Shishkin {
4557bd1d409SAlexander Shishkin 	config_item_put(&policy_node->group.cg_item);
4567bd1d409SAlexander Shishkin }
4577bd1d409SAlexander Shishkin 
4587bd1d409SAlexander Shishkin int __init stp_configfs_init(void)
4597bd1d409SAlexander Shishkin {
4607bd1d409SAlexander Shishkin 	int err;
4617bd1d409SAlexander Shishkin 
4627bd1d409SAlexander Shishkin 	config_group_init(&stp_policy_subsys.su_group);
4637bd1d409SAlexander Shishkin 	mutex_init(&stp_policy_subsys.su_mutex);
4647bd1d409SAlexander Shishkin 	err = configfs_register_subsystem(&stp_policy_subsys);
4657bd1d409SAlexander Shishkin 
4667bd1d409SAlexander Shishkin 	return err;
4677bd1d409SAlexander Shishkin }
4687bd1d409SAlexander Shishkin 
4697bd1d409SAlexander Shishkin void __exit stp_configfs_exit(void)
4707bd1d409SAlexander Shishkin {
4717bd1d409SAlexander Shishkin 	configfs_unregister_subsystem(&stp_policy_subsys);
4727bd1d409SAlexander Shishkin }
473