brintos

brintos / linux-shallow public Read only

0
0
Text · 12.6 KiB · 42103c3 Raw
571 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * System Trace Module (STM) master/channel allocation policy management4 * Copyright (c) 2014, Intel Corporation.5 *6 * A master/channel allocation policy allows mapping string identifiers to7 * master and channel ranges, where allocation can be done.8 */9 10#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt11 12#include <linux/types.h>13#include <linux/module.h>14#include <linux/device.h>15#include <linux/configfs.h>16#include <linux/slab.h>17#include <linux/stm.h>18#include "stm.h"19 20/*21 * STP Master/Channel allocation policy configfs layout.22 */23 24struct stp_policy {25	struct config_group	group;26	struct stm_device	*stm;27};28 29struct stp_policy_node {30	struct config_group	group;31	struct stp_policy	*policy;32	unsigned int		first_master;33	unsigned int		last_master;34	unsigned int		first_channel;35	unsigned int		last_channel;36	/* this is the one that's exposed to the attributes */37	unsigned char		priv[];38};39 40void *stp_policy_node_priv(struct stp_policy_node *pn)41{42	if (!pn)43		return NULL;44 45	return pn->priv;46}47 48static struct configfs_subsystem stp_policy_subsys;49 50void stp_policy_node_get_ranges(struct stp_policy_node *policy_node,51				unsigned int *mstart, unsigned int *mend,52				unsigned int *cstart, unsigned int *cend)53{54	*mstart	= policy_node->first_master;55	*mend	= policy_node->last_master;56	*cstart	= policy_node->first_channel;57	*cend	= policy_node->last_channel;58}59 60static inline struct stp_policy *to_stp_policy(struct config_item *item)61{62	return item ?63		container_of(to_config_group(item), struct stp_policy, group) :64		NULL;65}66 67static inline struct stp_policy_node *68to_stp_policy_node(struct config_item *item)69{70	return item ?71		container_of(to_config_group(item), struct stp_policy_node,72			     group) :73		NULL;74}75 76void *to_pdrv_policy_node(struct config_item *item)77{78	struct stp_policy_node *node = to_stp_policy_node(item);79 80	return stp_policy_node_priv(node);81}82EXPORT_SYMBOL_GPL(to_pdrv_policy_node);83 84static ssize_t85stp_policy_node_masters_show(struct config_item *item, char *page)86{87	struct stp_policy_node *policy_node = to_stp_policy_node(item);88	ssize_t count;89 90	count = sprintf(page, "%u %u\n", policy_node->first_master,91			policy_node->last_master);92 93	return count;94}95 96static ssize_t97stp_policy_node_masters_store(struct config_item *item, const char *page,98			      size_t count)99{100	struct stp_policy_node *policy_node = to_stp_policy_node(item);101	unsigned int first, last;102	struct stm_device *stm;103	char *p = (char *)page;104	ssize_t ret = -ENODEV;105 106	if (sscanf(p, "%u %u", &first, &last) != 2)107		return -EINVAL;108 109	mutex_lock(&stp_policy_subsys.su_mutex);110	stm = policy_node->policy->stm;111	if (!stm)112		goto unlock;113 114	/* must be within [sw_start..sw_end], which is an inclusive range */115	if (first > last || first < stm->data->sw_start ||116	    last > stm->data->sw_end) {117		ret = -ERANGE;118		goto unlock;119	}120 121	ret = count;122	policy_node->first_master = first;123	policy_node->last_master = last;124 125unlock:126	mutex_unlock(&stp_policy_subsys.su_mutex);127 128	return ret;129}130 131static ssize_t132stp_policy_node_channels_show(struct config_item *item, char *page)133{134	struct stp_policy_node *policy_node = to_stp_policy_node(item);135	ssize_t count;136 137	count = sprintf(page, "%u %u\n", policy_node->first_channel,138			policy_node->last_channel);139 140	return count;141}142 143static ssize_t144stp_policy_node_channels_store(struct config_item *item, const char *page,145			       size_t count)146{147	struct stp_policy_node *policy_node = to_stp_policy_node(item);148	unsigned int first, last;149	struct stm_device *stm;150	char *p = (char *)page;151	ssize_t ret = -ENODEV;152 153	if (sscanf(p, "%u %u", &first, &last) != 2)154		return -EINVAL;155 156	mutex_lock(&stp_policy_subsys.su_mutex);157	stm = policy_node->policy->stm;158	if (!stm)159		goto unlock;160 161	if (first > INT_MAX || last > INT_MAX || first > last ||162	    last >= stm->data->sw_nchannels) {163		ret = -ERANGE;164		goto unlock;165	}166 167	ret = count;168	policy_node->first_channel = first;169	policy_node->last_channel = last;170 171unlock:172	mutex_unlock(&stp_policy_subsys.su_mutex);173 174	return ret;175}176 177static void stp_policy_node_release(struct config_item *item)178{179	struct stp_policy_node *node = to_stp_policy_node(item);180 181	kfree(node);182}183 184static struct configfs_item_operations stp_policy_node_item_ops = {185	.release		= stp_policy_node_release,186};187 188CONFIGFS_ATTR(stp_policy_node_, masters);189CONFIGFS_ATTR(stp_policy_node_, channels);190 191static struct configfs_attribute *stp_policy_node_attrs[] = {192	&stp_policy_node_attr_masters,193	&stp_policy_node_attr_channels,194	NULL,195};196 197static const struct config_item_type stp_policy_type;198static const struct config_item_type stp_policy_node_type;199 200const struct config_item_type *201get_policy_node_type(struct configfs_attribute **attrs)202{203	struct config_item_type *type;204	struct configfs_attribute **merged;205 206	type = kmemdup(&stp_policy_node_type, sizeof(stp_policy_node_type),207		       GFP_KERNEL);208	if (!type)209		return NULL;210 211	merged = memcat_p(stp_policy_node_attrs, attrs);212	if (!merged) {213		kfree(type);214		return NULL;215	}216 217	type->ct_attrs = merged;218 219	return type;220}221 222static struct config_group *223stp_policy_node_make(struct config_group *group, const char *name)224{225	const struct config_item_type *type = &stp_policy_node_type;226	struct stp_policy_node *policy_node, *parent_node;227	const struct stm_protocol_driver *pdrv;228	struct stp_policy *policy;229 230	if (group->cg_item.ci_type == &stp_policy_type) {231		policy = container_of(group, struct stp_policy, group);232	} else {233		parent_node = container_of(group, struct stp_policy_node,234					   group);235		policy = parent_node->policy;236	}237 238	if (!policy->stm)239		return ERR_PTR(-ENODEV);240 241	pdrv = policy->stm->pdrv;242	policy_node =243		kzalloc(offsetof(struct stp_policy_node, priv[pdrv->priv_sz]),244			GFP_KERNEL);245	if (!policy_node)246		return ERR_PTR(-ENOMEM);247 248	if (pdrv->policy_node_init)249		pdrv->policy_node_init((void *)policy_node->priv);250 251	if (policy->stm->pdrv_node_type)252		type = policy->stm->pdrv_node_type;253 254	config_group_init_type_name(&policy_node->group, name, type);255 256	policy_node->policy = policy;257 258	/* default values for the attributes */259	policy_node->first_master = policy->stm->data->sw_start;260	policy_node->last_master = policy->stm->data->sw_end;261	policy_node->first_channel = 0;262	policy_node->last_channel = policy->stm->data->sw_nchannels - 1;263 264	return &policy_node->group;265}266 267static void268stp_policy_node_drop(struct config_group *group, struct config_item *item)269{270	config_item_put(item);271}272 273static struct configfs_group_operations stp_policy_node_group_ops = {274	.make_group	= stp_policy_node_make,275	.drop_item	= stp_policy_node_drop,276};277 278static const struct config_item_type stp_policy_node_type = {279	.ct_item_ops	= &stp_policy_node_item_ops,280	.ct_group_ops	= &stp_policy_node_group_ops,281	.ct_attrs	= stp_policy_node_attrs,282	.ct_owner	= THIS_MODULE,283};284 285/*286 * Root group: policies.287 */288static ssize_t stp_policy_device_show(struct config_item *item,289				      char *page)290{291	struct stp_policy *policy = to_stp_policy(item);292	ssize_t count;293 294	count = sprintf(page, "%s\n",295			(policy && policy->stm) ?296			policy->stm->data->name :297			"<none>");298 299	return count;300}301 302CONFIGFS_ATTR_RO(stp_policy_, device);303 304static ssize_t stp_policy_protocol_show(struct config_item *item,305					char *page)306{307	struct stp_policy *policy = to_stp_policy(item);308	ssize_t count;309 310	count = sprintf(page, "%s\n",311			(policy && policy->stm) ?312			policy->stm->pdrv->name :313			"<none>");314 315	return count;316}317 318CONFIGFS_ATTR_RO(stp_policy_, protocol);319 320static struct configfs_attribute *stp_policy_attrs[] = {321	&stp_policy_attr_device,322	&stp_policy_attr_protocol,323	NULL,324};325 326void stp_policy_unbind(struct stp_policy *policy)327{328	struct stm_device *stm = policy->stm;329 330	/*331	 * stp_policy_release() will not call here if the policy is already332	 * unbound; other users should not either, as no link exists between333	 * this policy and anything else in that case334	 */335	if (WARN_ON_ONCE(!policy->stm))336		return;337 338	lockdep_assert_held(&stm->policy_mutex);339 340	stm->policy = NULL;341	policy->stm = NULL;342 343	/*344	 * Drop the reference on the protocol driver and lose the link.345	 */346	stm_put_protocol(stm->pdrv);347	stm->pdrv = NULL;348	stm_put_device(stm);349}350 351static void stp_policy_release(struct config_item *item)352{353	struct stp_policy *policy = to_stp_policy(item);354	struct stm_device *stm = policy->stm;355 356	/* a policy *can* be unbound and still exist in configfs tree */357	if (!stm)358		return;359 360	mutex_lock(&stm->policy_mutex);361	stp_policy_unbind(policy);362	mutex_unlock(&stm->policy_mutex);363 364	kfree(policy);365}366 367static struct configfs_item_operations stp_policy_item_ops = {368	.release		= stp_policy_release,369};370 371static struct configfs_group_operations stp_policy_group_ops = {372	.make_group	= stp_policy_node_make,373};374 375static const struct config_item_type stp_policy_type = {376	.ct_item_ops	= &stp_policy_item_ops,377	.ct_group_ops	= &stp_policy_group_ops,378	.ct_attrs	= stp_policy_attrs,379	.ct_owner	= THIS_MODULE,380};381 382static struct config_group *383stp_policy_make(struct config_group *group, const char *name)384{385	const struct config_item_type *pdrv_node_type;386	const struct stm_protocol_driver *pdrv;387	char *devname, *proto, *p;388	struct config_group *ret;389	struct stm_device *stm;390	int err;391 392	devname = kasprintf(GFP_KERNEL, "%s", name);393	if (!devname)394		return ERR_PTR(-ENOMEM);395 396	/*397	 * node must look like <device_name>.<policy_name>, where398	 * <device_name> is the name of an existing stm device; may399	 *               contain dots;400	 * <policy_name> is an arbitrary string; may not contain dots401	 * <device_name>:<protocol_name>.<policy_name>402	 */403	p = strrchr(devname, '.');404	if (!p) {405		kfree(devname);406		return ERR_PTR(-EINVAL);407	}408 409	*p = '\0';410 411	/*412	 * look for ":<protocol_name>":413	 *  + no protocol suffix: fall back to whatever is available;414	 *  + unknown protocol: fail the whole thing415	 */416	proto = strrchr(devname, ':');417	if (proto)418		*proto++ = '\0';419 420	stm = stm_find_device(devname);421	if (!stm) {422		kfree(devname);423		return ERR_PTR(-ENODEV);424	}425 426	err = stm_lookup_protocol(proto, &pdrv, &pdrv_node_type);427	kfree(devname);428 429	if (err) {430		stm_put_device(stm);431		return ERR_PTR(-ENODEV);432	}433 434	mutex_lock(&stm->policy_mutex);435	if (stm->policy) {436		ret = ERR_PTR(-EBUSY);437		goto unlock_policy;438	}439 440	stm->policy = kzalloc(sizeof(*stm->policy), GFP_KERNEL);441	if (!stm->policy) {442		ret = ERR_PTR(-ENOMEM);443		goto unlock_policy;444	}445 446	config_group_init_type_name(&stm->policy->group, name,447				    &stp_policy_type);448 449	stm->pdrv = pdrv;450	stm->pdrv_node_type = pdrv_node_type;451	stm->policy->stm = stm;452	ret = &stm->policy->group;453 454unlock_policy:455	mutex_unlock(&stm->policy_mutex);456 457	if (IS_ERR(ret)) {458		/*459		 * pdrv and stm->pdrv at this point can be quite different,460		 * and only one of them needs to be 'put'461		 */462		stm_put_protocol(pdrv);463		stm_put_device(stm);464	}465 466	return ret;467}468 469static struct configfs_group_operations stp_policy_root_group_ops = {470	.make_group	= stp_policy_make,471};472 473static const struct config_item_type stp_policy_root_type = {474	.ct_group_ops	= &stp_policy_root_group_ops,475	.ct_owner	= THIS_MODULE,476};477 478static struct configfs_subsystem stp_policy_subsys = {479	.su_group = {480		.cg_item = {481			.ci_namebuf	= "stp-policy",482			.ci_type	= &stp_policy_root_type,483		},484	},485};486 487/*488 * Lock the policy mutex from the outside489 */490static struct stp_policy_node *491__stp_policy_node_lookup(struct stp_policy *policy, char *s)492{493	struct stp_policy_node *policy_node, *ret = NULL;494	struct list_head *head = &policy->group.cg_children;495	struct config_item *item;496	char *start, *end = s;497 498	if (list_empty(head))499		return NULL;500 501next:502	for (;;) {503		start = strsep(&end, "/");504		if (!start)505			break;506 507		if (!*start)508			continue;509 510		list_for_each_entry(item, head, ci_entry) {511			policy_node = to_stp_policy_node(item);512 513			if (!strcmp(start,514				    policy_node->group.cg_item.ci_name)) {515				ret = policy_node;516 517				if (!end)518					goto out;519 520				head = &policy_node->group.cg_children;521				goto next;522			}523		}524		break;525	}526 527out:528	return ret;529}530 531 532struct stp_policy_node *533stp_policy_node_lookup(struct stm_device *stm, char *s)534{535	struct stp_policy_node *policy_node = NULL;536 537	mutex_lock(&stp_policy_subsys.su_mutex);538 539	mutex_lock(&stm->policy_mutex);540	if (stm->policy)541		policy_node = __stp_policy_node_lookup(stm->policy, s);542	mutex_unlock(&stm->policy_mutex);543 544	if (policy_node)545		config_item_get(&policy_node->group.cg_item);546	else547		mutex_unlock(&stp_policy_subsys.su_mutex);548 549	return policy_node;550}551 552void stp_policy_node_put(struct stp_policy_node *policy_node)553{554	lockdep_assert_held(&stp_policy_subsys.su_mutex);555 556	mutex_unlock(&stp_policy_subsys.su_mutex);557	config_item_put(&policy_node->group.cg_item);558}559 560int __init stp_configfs_init(void)561{562	config_group_init(&stp_policy_subsys.su_group);563	mutex_init(&stp_policy_subsys.su_mutex);564	return configfs_register_subsystem(&stp_policy_subsys);565}566 567void __exit stp_configfs_exit(void)568{569	configfs_unregister_subsystem(&stp_policy_subsys);570}571