/* SPDX-License-Identifier: GPL-2.0 */ /* * fs/sharefs/config.c * * Copyright (c) 2023 Huawei Device Co., Ltd. */ #include #include #include #include #include #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); }