xref: /kernel/linux/linux-5.10/fs/sharefs/config.c (revision 8c2ecf20)
1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * fs/sharefs/config.c
4 *
5 * Copyright (c) 2023 Huawei Device Co., Ltd.
6 */
7
8#include <linux/configfs.h>
9#include <linux/ctype.h>
10#include <linux/dcache.h>
11#include <linux/hashtable.h>
12#include <linux/slab.h>
13#include "sharefs.h"
14
15static struct kmem_cache *sharefs_bid_entry_cachep;
16
17struct sharefs_bid_entry {
18	struct hlist_node node;
19	struct qstr str;
20	int id;
21};
22
23struct sharefs_config_bitem {
24	struct config_item item;
25	struct qstr str;
26};
27
28static unsigned int make_hash(const char *name, unsigned int len)
29{
30	unsigned long hash;
31
32	hash = init_name_hash(0);
33	while (len--)
34		hash = partial_name_hash(tolower(*name++), hash);
35
36	return end_name_hash(hash);
37}
38
39static struct qstr make_qstr(const char *name)
40{
41	struct qstr str;
42	str.name = name;
43	str.len = strlen(name);
44	str.hash = make_hash(str.name, str.len);
45
46	return str;
47}
48
49static struct sharefs_bid_entry *alloc_bid_entry(const char *name, int id)
50{
51	struct sharefs_bid_entry *bid_entry;
52	char *bid_entry_name;
53
54	bid_entry = kmem_cache_alloc(sharefs_bid_entry_cachep, GFP_KERNEL);
55	if (!bid_entry) {
56		bid_entry = ERR_PTR(-ENOMEM);
57		goto out;
58	}
59
60	bid_entry_name = kstrdup(name, GFP_KERNEL);
61	if (!bid_entry_name) {
62		kmem_cache_free(sharefs_bid_entry_cachep, bid_entry);
63		bid_entry = ERR_PTR(-ENOMEM);
64		goto out;
65	}
66
67	INIT_HLIST_NODE(&bid_entry->node);
68	bid_entry->str = make_qstr(bid_entry_name);
69	bid_entry->id = id;
70out:
71	return bid_entry;
72}
73
74static void free_bid_entry(struct sharefs_bid_entry *bid_entry)
75{
76	if (bid_entry == NULL)
77		return;
78
79	kfree(bid_entry->str.name);
80	kmem_cache_free(sharefs_bid_entry_cachep, bid_entry);
81}
82
83static struct sharefs_config_bitem *alloc_bitem(const char *name)
84{
85	struct sharefs_config_bitem *bitem;
86	char *bitem_name;
87
88	bitem = kzalloc(sizeof(*bitem), GFP_KERNEL);
89	if (!bitem) {
90		bitem = ERR_PTR(-ENOMEM);
91		goto out;
92	}
93
94	bitem_name = kstrdup(name, GFP_KERNEL);
95	if (!bitem_name) {
96		kfree(bitem);
97		bitem = ERR_PTR(-ENOMEM);
98		goto out;
99	}
100
101	bitem->str = make_qstr(bitem_name);
102out:
103	return bitem;
104}
105
106static void free_bitem(struct sharefs_config_bitem *bitem)
107{
108	if (bitem == NULL)
109		return;
110
111	kfree(bitem->str.name);
112	kfree(bitem);
113}
114
115#define SHAREFS_BUNDLE_ATTRIBUTE(_attr_)					\
116									\
117static DEFINE_HASHTABLE(sharefs_##_attr_##_hash_table, 4);		\
118									\
119static DEFINE_MUTEX(sharefs_##_attr_##_hash_mutex);			\
120									\
121static int query_##_attr_##_hash_entry(struct qstr *str)		\
122{									\
123	int id = 0;							\
124	struct sharefs_bid_entry *bid_entry;				\
125	struct hlist_node *hash_node;					\
126									\
127	mutex_lock(&sharefs_##_attr_##_hash_mutex);			\
128	hash_for_each_possible_safe(sharefs_##_attr_##_hash_table,	\
129		bid_entry, hash_node, node, str->hash) {		\
130		if (qstr_case_eq(str, &bid_entry->str)) {		\
131			id = bid_entry->id;				\
132			break;						\
133		}							\
134	}								\
135	mutex_unlock(&sharefs_##_attr_##_hash_mutex);			\
136									\
137	return id;							\
138}									\
139									\
140static int insert_##_attr_##_hash_entry(struct qstr *str, int id)	\
141{									\
142	int err = 0;							\
143	struct sharefs_bid_entry *bid_entry;				\
144	struct hlist_node *hash_node;					\
145									\
146	sharefs_info("insert name = %s", str->name);			\
147									\
148	mutex_lock(&sharefs_##_attr_##_hash_mutex);			\
149	hash_for_each_possible_safe(sharefs_##_attr_##_hash_table,	\
150		bid_entry, hash_node, node, str->hash) {		\
151		if (qstr_case_eq(str, &bid_entry->str)) {		\
152			bid_entry->id = id;				\
153			mutex_unlock(&sharefs_##_attr_##_hash_mutex);	\
154			goto out;					\
155		}							\
156	}								\
157	mutex_unlock(&sharefs_##_attr_##_hash_mutex);			\
158									\
159	bid_entry = alloc_bid_entry(str->name, id);			\
160	if (IS_ERR(bid_entry)) {					\
161		err = PTR_ERR(bid_entry);				\
162		goto out;						\
163	} 								\
164									\
165	hash_add_rcu(sharefs_##_attr_##_hash_table, &bid_entry->node,	\
166		 bid_entry->str.hash);					\
167out:									\
168	return err;							\
169}									\
170									\
171static void remove_##_attr_##_hash_entry(struct qstr *str)		\
172{									\
173	struct sharefs_bid_entry *bid_entry;				\
174	struct hlist_node *hash_node;					\
175									\
176	sharefs_info("remove name = %s", str->name);			\
177									\
178	mutex_lock(&sharefs_##_attr_##_hash_mutex);			\
179	hash_for_each_possible_safe(sharefs_##_attr_##_hash_table,	\
180		bid_entry, hash_node, node, str->hash) {		\
181		if (qstr_case_eq(str, &bid_entry->str)) {		\
182			hash_del_rcu(&bid_entry->node);			\
183			free_bid_entry(bid_entry);			\
184			break;						\
185		}							\
186	}								\
187	mutex_unlock(&sharefs_##_attr_##_hash_mutex);			\
188}									\
189									\
190static void clear_##_attr_##_hash_entry(void)				\
191{									\
192	int index;							\
193	struct sharefs_bid_entry *bid_entry;				\
194	struct hlist_node *hash_node;					\
195									\
196	sharefs_info("clear bid entry");					\
197									\
198	mutex_lock(&sharefs_##_attr_##_hash_mutex);			\
199	hash_for_each_safe(sharefs_##_attr_##_hash_table, index,		\
200		hash_node, bid_entry, node) {				\
201		hash_del_rcu(&bid_entry->node);				\
202		kfree(bid_entry->str.name);				\
203		kmem_cache_free(sharefs_bid_entry_cachep, bid_entry);	\
204	}								\
205	mutex_unlock(&sharefs_##_attr_##_hash_mutex);			\
206}									\
207									\
208static int sharefs_##_attr_##_get(const char *bname)			\
209{									\
210	struct qstr str;						\
211									\
212	str = make_qstr(bname);						\
213	return query_##_attr_##_hash_entry(&str);			\
214}									\
215									\
216static ssize_t sharefs_##_attr_##_show(struct config_item *item,		\
217	char *page)							\
218{									\
219	int id;								\
220	struct sharefs_config_bitem *bitem;				\
221									\
222	sharefs_info("show bundle id");					\
223									\
224	bitem = container_of(item, struct sharefs_config_bitem, item);	\
225	id = query_##_attr_##_hash_entry(&bitem->str);			\
226									\
227	return scnprintf(page, PAGE_SIZE, "%u\n", id);			\
228}									\
229									\
230static ssize_t sharefs_##_attr_##_store(struct config_item *item,	\
231	const char *page, size_t count)					\
232{									\
233	int id;								\
234	int err;							\
235	size_t size;							\
236	struct sharefs_config_bitem *bitem;				\
237									\
238	sharefs_info("store bundle id");					\
239									\
240	bitem = container_of(item, struct sharefs_config_bitem, item);	\
241									\
242	if (kstrtouint(page, 10, &id)) {				\
243		size = -EINVAL;						\
244		goto out; 						\
245	}								\
246									\
247	err = insert_##_attr_##_hash_entry(&bitem->str, id);		\
248	if (err) {							\
249		size = err;						\
250		goto out;						\
251	}								\
252									\
253	size = count;							\
254out:									\
255	return size;							\
256}									\
257									\
258static struct configfs_attribute sharefs_##_attr_##_attr = {		\
259	.ca_name	= __stringify(_attr_),				\
260	.ca_mode	= S_IRUGO | S_IWUGO,				\
261	.ca_owner	= THIS_MODULE,					\
262	.show		= sharefs_##_attr_##_show,			\
263	.store		= sharefs_##_attr_##_store,			\
264};
265
266SHAREFS_BUNDLE_ATTRIBUTE(appid)
267
268static struct configfs_attribute *sharefs_battrs[] = {
269	&sharefs_appid_attr,
270	NULL,
271};
272
273static void sharefs_config_bitem_release(struct config_item *item)
274{
275	struct sharefs_config_bitem *bitem;
276
277	sharefs_info("release bundle item");
278
279	bitem = container_of(item, struct sharefs_config_bitem, item);
280	remove_appid_hash_entry(&bitem->str);
281	remove_appid_hash_entry(&bitem->str);
282	free_bitem(bitem);
283}
284
285static struct configfs_item_operations sharefs_config_bitem_ops = {
286	.release = sharefs_config_bitem_release,
287};
288
289static struct config_item_type sharefs_config_bitem_type = {
290	.ct_item_ops	= &sharefs_config_bitem_ops,
291	.ct_attrs	= sharefs_battrs,
292	.ct_owner	= THIS_MODULE,
293};
294
295static struct config_item *sharefs_make_bitem(struct config_group *group,
296					      const char *name)
297{
298	struct config_item *item;
299	struct sharefs_config_bitem *bitem;
300
301	bitem = alloc_bitem(name);
302	if (IS_ERR(bitem)) {
303		item = ERR_PTR(-ENOMEM);
304		goto out;
305	}
306
307	config_item_init_type_name(&bitem->item, name,
308		&sharefs_config_bitem_type);
309	item = &bitem->item;
310out:
311	return item;
312}
313
314static struct configfs_group_operations sharefs_group_ops = {
315	.make_item = sharefs_make_bitem,
316};
317
318static struct config_item_type sharefs_group_type = {
319	.ct_group_ops     = &sharefs_group_ops,
320	.ct_owner         = THIS_MODULE,
321};
322
323static struct configfs_subsystem sharefs_subsystem = {
324	.su_group = {
325		.cg_item = {
326			.ci_namebuf = "sharefs",
327			.ci_type = &sharefs_group_type,
328		},
329	},
330};
331
332int get_bid_config(const char *bname)
333{
334	return sharefs_appid_get(bname);
335}
336
337int __init sharefs_init_configfs(void)
338{
339	int err;
340	struct configfs_subsystem *subsys;
341
342	sharefs_info("init configfs");
343
344	sharefs_bid_entry_cachep = kmem_cache_create("sharefs_bid_entry_cachep",
345		sizeof(struct sharefs_bid_entry), 0, 0, NULL);
346	if (!sharefs_bid_entry_cachep) {
347		sharefs_err("failed to create bid entry cachep");
348		err = -ENOMEM;
349		goto out;
350	}
351
352	subsys = &sharefs_subsystem;
353	config_group_init(&subsys->su_group);
354	mutex_init(&subsys->su_mutex);
355
356	err = configfs_register_subsystem(subsys);
357	if (err)
358		sharefs_err("failed to register subsystem");
359
360out:
361	return err;
362}
363
364void sharefs_exit_configfs(void)
365{
366	sharefs_info("sharefs exit configfs");
367
368	configfs_unregister_subsystem(&sharefs_subsystem);
369	clear_appid_hash_entry();
370
371	kmem_cache_destroy(sharefs_bid_entry_cachep);
372}