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}