* fs/sharefs/config.c
*
* Copyright (c) 2023 Huawei Device Co., Ltd.
*/
#include <linux/configfs.h>
#include <linux/ctype.h>
#include <linux/dcache.h>
#include <linux/hashtable.h>
#include <linux/slab.h>
#include "sharefs.h"
static struct kmem_cache *sharefs_bid_entry_cachep;
struct sharefs_bid_entry {
struct hlist_node node;
struct qstr str;
int id;
};
struct sharefs_config_bitem {
struct config_item item;
struct qstr str;
};
static unsigned int make_hash(const char *name, unsigned int len)
{
unsigned long hash;
hash = init_name_hash(0);
while (len--)
hash = partial_name_hash(tolower(*name++), hash);
return end_name_hash(hash);
}
static struct qstr make_qstr(const char *name)
{
struct qstr str;
str.name = name;
str.len = strlen(name);
str.hash = make_hash(str.name, str.len);
return str;
}
static struct sharefs_bid_entry *alloc_bid_entry(const char *name, int id)
{
struct sharefs_bid_entry *bid_entry;
char *bid_entry_name;
bid_entry = kmem_cache_alloc(sharefs_bid_entry_cachep, GFP_KERNEL);
if (!bid_entry) {
bid_entry = ERR_PTR(-ENOMEM);
goto out;
}
bid_entry_name = kstrdup(name, GFP_KERNEL);
if (!bid_entry_name) {
kmem_cache_free(sharefs_bid_entry_cachep, bid_entry);
bid_entry = ERR_PTR(-ENOMEM);
goto out;
}
INIT_HLIST_NODE(&bid_entry->node);
bid_entry->str = make_qstr(bid_entry_name);
bid_entry->id = id;
out:
return bid_entry;
}
static void free_bid_entry(struct sharefs_bid_entry *bid_entry)
{
if (bid_entry == NULL)
return;
kfree(bid_entry->str.name);
kmem_cache_free(sharefs_bid_entry_cachep, bid_entry);
}
static struct sharefs_config_bitem *alloc_bitem(const char *name)
{
struct sharefs_config_bitem *bitem;
char *bitem_name;
bitem = kzalloc(sizeof(*bitem), GFP_KERNEL);
if (!bitem) {
bitem = ERR_PTR(-ENOMEM);
goto out;
}
bitem_name = kstrdup(name, GFP_KERNEL);
if (!bitem_name) {
kfree(bitem);
bitem = ERR_PTR(-ENOMEM);
goto out;
}
bitem->str = make_qstr(bitem_name);
out:
return bitem;
}
static void free_bitem(struct sharefs_config_bitem *bitem)
{
if (bitem == NULL)
return;
kfree(bitem->str.name);
kfree(bitem);
}
#define SHAREFS_BUNDLE_ATTRIBUTE(_attr_) \
\
static DEFINE_HASHTABLE(sharefs_##_attr_##_hash_table, 4); \
\
static DEFINE_MUTEX(sharefs_##_attr_##_hash_mutex); \
\
static int query_##_attr_##_hash_entry(struct qstr *str) \
{ \
int id = 0; \
struct sharefs_bid_entry *bid_entry; \
struct hlist_node *hash_node; \
\
mutex_lock(&sharefs_##_attr_##_hash_mutex); \
hash_for_each_possible_safe(sharefs_##_attr_##_hash_table, \
bid_entry, hash_node, node, str->hash) { \
if (qstr_case_eq(str, &bid_entry->str)) { \
id = bid_entry->id; \
break; \
} \
} \
mutex_unlock(&sharefs_##_attr_##_hash_mutex); \
\
return id; \
} \
\
static int insert_##_attr_##_hash_entry(struct qstr *str, int id) \
{ \
int err = 0; \
struct sharefs_bid_entry *bid_entry; \
struct hlist_node *hash_node; \
\
sharefs_info("insert name = %s", str->name); \
\
mutex_lock(&sharefs_##_attr_##_hash_mutex); \
hash_for_each_possible_safe(sharefs_##_attr_##_hash_table, \
bid_entry, hash_node, node, str->hash) { \
if (qstr_case_eq(str, &bid_entry->str)) { \
bid_entry->id = id; \
mutex_unlock(&sharefs_##_attr_##_hash_mutex); \
goto out; \
} \
} \
mutex_unlock(&sharefs_##_attr_##_hash_mutex); \
\
bid_entry = alloc_bid_entry(str->name, id); \
if (IS_ERR(bid_entry)) { \
err = PTR_ERR(bid_entry); \
goto out; \
} \
\
hash_add_rcu(sharefs_##_attr_##_hash_table, &bid_entry->node, \
bid_entry->str.hash); \
out: \
return err; \
} \
\
static void remove_##_attr_##_hash_entry(struct qstr *str) \
{ \
struct sharefs_bid_entry *bid_entry; \
struct hlist_node *hash_node; \
\
sharefs_info("remove name = %s", str->name); \
\
mutex_lock(&sharefs_##_attr_##_hash_mutex); \
hash_for_each_possible_safe(sharefs_##_attr_##_hash_table, \
bid_entry, hash_node, node, str->hash) { \
if (qstr_case_eq(str, &bid_entry->str)) { \
hash_del_rcu(&bid_entry->node); \
free_bid_entry(bid_entry); \
break; \
} \
} \
mutex_unlock(&sharefs_##_attr_##_hash_mutex); \
} \
\
static void clear_##_attr_##_hash_entry(void) \
{ \
int index; \
struct sharefs_bid_entry *bid_entry; \
struct hlist_node *hash_node; \
\
sharefs_info("clear bid entry"); \
\
mutex_lock(&sharefs_##_attr_##_hash_mutex); \
hash_for_each_safe(sharefs_##_attr_##_hash_table, index, \
hash_node, bid_entry, node) { \
hash_del_rcu(&bid_entry->node); \
kfree(bid_entry->str.name); \
kmem_cache_free(sharefs_bid_entry_cachep, bid_entry); \
} \
mutex_unlock(&sharefs_##_attr_##_hash_mutex); \
} \
\
static int sharefs_##_attr_##_get(const char *bname) \
{ \
struct qstr str; \
\
str = make_qstr(bname); \
return query_##_attr_##_hash_entry(&str); \
} \
\
static ssize_t sharefs_##_attr_##_show(struct config_item *item, \
char *page) \
{ \
int id; \
struct sharefs_config_bitem *bitem; \
\
sharefs_info("show bundle id"); \
\
bitem = container_of(item, struct sharefs_config_bitem, item); \
id = query_##_attr_##_hash_entry(&bitem->str); \
\
return scnprintf(page, PAGE_SIZE, "%u\n", id); \
} \
\
static ssize_t sharefs_##_attr_##_store(struct config_item *item, \
const char *page, size_t count) \
{ \
int id; \
int err; \
size_t size; \
struct sharefs_config_bitem *bitem; \
\
sharefs_info("store bundle id"); \
\
bitem = container_of(item, struct sharefs_config_bitem, item); \
\
if (kstrtouint(page, 10, &id)) { \
size = -EINVAL; \
goto out; \
} \
\
err = insert_##_attr_##_hash_entry(&bitem->str, id); \
if (err) { \
size = err; \
goto out; \
} \
\
size = count; \
out: \
return size; \
} \
\
static struct configfs_attribute sharefs_##_attr_##_attr = { \
.ca_name = __stringify(_attr_), \
.ca_mode = S_IRUGO | S_IWUGO, \
.ca_owner = THIS_MODULE, \
.show = sharefs_##_attr_##_show, \
.store = sharefs_##_attr_##_store, \
};
SHAREFS_BUNDLE_ATTRIBUTE(appid)
static struct configfs_attribute *sharefs_battrs[] = {
&sharefs_appid_attr,
NULL,
};
static void sharefs_config_bitem_release(struct config_item *item)
{
struct sharefs_config_bitem *bitem;
sharefs_info("release bundle item");
bitem = container_of(item, struct sharefs_config_bitem, item);
remove_appid_hash_entry(&bitem->str);
remove_appid_hash_entry(&bitem->str);
free_bitem(bitem);
}
static struct configfs_item_operations sharefs_config_bitem_ops = {
.release = sharefs_config_bitem_release,
};
static struct config_item_type sharefs_config_bitem_type = {
.ct_item_ops = &sharefs_config_bitem_ops,
.ct_attrs = sharefs_battrs,
.ct_owner = THIS_MODULE,
};
static struct config_item *sharefs_make_bitem(struct config_group *group,
const char *name)
{
struct config_item *item;
struct sharefs_config_bitem *bitem;
bitem = alloc_bitem(name);
if (IS_ERR(bitem)) {
item = ERR_PTR(-ENOMEM);
goto out;
}
config_item_init_type_name(&bitem->item, name,
&sharefs_config_bitem_type);
item = &bitem->item;
out:
return item;
}
static struct configfs_group_operations sharefs_group_ops = {
.make_item = sharefs_make_bitem,
};
static struct config_item_type sharefs_group_type = {
.ct_group_ops = &sharefs_group_ops,
.ct_owner = THIS_MODULE,
};
static struct configfs_subsystem sharefs_subsystem = {
.su_group = {
.cg_item = {
.ci_namebuf = "sharefs",
.ci_type = &sharefs_group_type,
},
},
};
int get_bid_config(const char *bname)
{
return sharefs_appid_get(bname);
}
int __init sharefs_init_configfs(void)
{
int err;
struct configfs_subsystem *subsys;
sharefs_info("init configfs");
sharefs_bid_entry_cachep = kmem_cache_create("sharefs_bid_entry_cachep",
sizeof(struct sharefs_bid_entry), 0, 0, NULL);
if (!sharefs_bid_entry_cachep) {
sharefs_err("failed to create bid entry cachep");
err = -ENOMEM;
goto out;
}
subsys = &sharefs_subsystem;
config_group_init(&subsys->su_group);
mutex_init(&subsys->su_mutex);
err = configfs_register_subsystem(subsys);
if (err)
sharefs_err("failed to register subsystem");
out:
return err;
}
void sharefs_exit_configfs(void)
{
sharefs_info("sharefs exit configfs");
configfs_unregister_subsystem(&sharefs_subsystem);
clear_appid_hash_entry();
kmem_cache_destroy(sharefs_bid_entry_cachep);
}