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