144c09535SAaron Tomlin // SPDX-License-Identifier: GPL-2.0-or-later
244c09535SAaron Tomlin /*
344c09535SAaron Tomlin * Module sysfs support
444c09535SAaron Tomlin *
544c09535SAaron Tomlin * Copyright (C) 2008 Rusty Russell
644c09535SAaron Tomlin */
744c09535SAaron Tomlin
844c09535SAaron Tomlin #include <linux/module.h>
944c09535SAaron Tomlin #include <linux/kernel.h>
1044c09535SAaron Tomlin #include <linux/fs.h>
1144c09535SAaron Tomlin #include <linux/sysfs.h>
1244c09535SAaron Tomlin #include <linux/slab.h>
1344c09535SAaron Tomlin #include <linux/kallsyms.h>
1444c09535SAaron Tomlin #include <linux/mutex.h>
1544c09535SAaron Tomlin #include "internal.h"
1644c09535SAaron Tomlin
1744c09535SAaron Tomlin /*
1844c09535SAaron Tomlin * /sys/module/foo/sections stuff
1944c09535SAaron Tomlin * J. Corbet <[email protected]>
2044c09535SAaron Tomlin */
2144c09535SAaron Tomlin #ifdef CONFIG_KALLSYMS
2244c09535SAaron Tomlin struct module_sect_attrs {
2344c09535SAaron Tomlin struct attribute_group grp;
2434f5ec0fSThomas Weißschuh struct bin_attribute attrs[];
2544c09535SAaron Tomlin };
2644c09535SAaron Tomlin
2744c09535SAaron Tomlin #define MODULE_SECT_READ_SIZE (3 /* "0x", "\n" */ + (BITS_PER_LONG / 4))
module_sect_read(struct file * file,struct kobject * kobj,const struct bin_attribute * battr,char * buf,loff_t pos,size_t count)2844c09535SAaron Tomlin static ssize_t module_sect_read(struct file *file, struct kobject *kobj,
29*b83815afSThomas Weißschuh const struct bin_attribute *battr,
3044c09535SAaron Tomlin char *buf, loff_t pos, size_t count)
3144c09535SAaron Tomlin {
3244c09535SAaron Tomlin char bounce[MODULE_SECT_READ_SIZE + 1];
3344c09535SAaron Tomlin size_t wrote;
3444c09535SAaron Tomlin
3544c09535SAaron Tomlin if (pos != 0)
3644c09535SAaron Tomlin return -EINVAL;
3744c09535SAaron Tomlin
3844c09535SAaron Tomlin /*
3944c09535SAaron Tomlin * Since we're a binary read handler, we must account for the
4044c09535SAaron Tomlin * trailing NUL byte that sprintf will write: if "buf" is
4144c09535SAaron Tomlin * too small to hold the NUL, or the NUL is exactly the last
4244c09535SAaron Tomlin * byte, the read will look like it got truncated by one byte.
4344c09535SAaron Tomlin * Since there is no way to ask sprintf nicely to not write
4444c09535SAaron Tomlin * the NUL, we have to use a bounce buffer.
4544c09535SAaron Tomlin */
4644c09535SAaron Tomlin wrote = scnprintf(bounce, sizeof(bounce), "0x%px\n",
4744c09535SAaron Tomlin kallsyms_show_value(file->f_cred)
484b2c11e4SThomas Weißschuh ? battr->private : NULL);
4944c09535SAaron Tomlin count = min(count, wrote);
5044c09535SAaron Tomlin memcpy(buf, bounce, count);
5144c09535SAaron Tomlin
5244c09535SAaron Tomlin return count;
5344c09535SAaron Tomlin }
5444c09535SAaron Tomlin
free_sect_attrs(struct module_sect_attrs * sect_attrs)5544c09535SAaron Tomlin static void free_sect_attrs(struct module_sect_attrs *sect_attrs)
5644c09535SAaron Tomlin {
57*b83815afSThomas Weißschuh const struct bin_attribute *const *bin_attr;
5844c09535SAaron Tomlin
59*b83815afSThomas Weißschuh for (bin_attr = sect_attrs->grp.bin_attrs_new; *bin_attr; bin_attr++)
60d8959b94SThomas Weißschuh kfree((*bin_attr)->attr.name);
61*b83815afSThomas Weißschuh kfree(sect_attrs->grp.bin_attrs_new);
6244c09535SAaron Tomlin kfree(sect_attrs);
6344c09535SAaron Tomlin }
6444c09535SAaron Tomlin
add_sect_attrs(struct module * mod,const struct load_info * info)65ce47f7cbSChunhui Li static int add_sect_attrs(struct module *mod, const struct load_info *info)
6644c09535SAaron Tomlin {
6744c09535SAaron Tomlin struct module_sect_attrs *sect_attrs;
68*b83815afSThomas Weißschuh const struct bin_attribute **gattr;
6934f5ec0fSThomas Weißschuh struct bin_attribute *sattr;
70f47c0bebSThomas Weißschuh unsigned int nloaded = 0, i;
71ce47f7cbSChunhui Li int ret;
7244c09535SAaron Tomlin
7344c09535SAaron Tomlin /* Count loaded sections and allocate structures */
7444c09535SAaron Tomlin for (i = 0; i < info->hdr->e_shnum; i++)
7544c09535SAaron Tomlin if (!sect_empty(&info->sechdrs[i]))
7644c09535SAaron Tomlin nloaded++;
77f47c0bebSThomas Weißschuh sect_attrs = kzalloc(struct_size(sect_attrs, attrs, nloaded), GFP_KERNEL);
7844c09535SAaron Tomlin if (!sect_attrs)
79ce47f7cbSChunhui Li return -ENOMEM;
8044c09535SAaron Tomlin
81f47c0bebSThomas Weißschuh gattr = kcalloc(nloaded + 1, sizeof(*gattr), GFP_KERNEL);
82f47c0bebSThomas Weißschuh if (!gattr) {
83f47c0bebSThomas Weißschuh kfree(sect_attrs);
84f47c0bebSThomas Weißschuh return -ENOMEM;
85f47c0bebSThomas Weißschuh }
86f47c0bebSThomas Weißschuh
8744c09535SAaron Tomlin /* Setup section attributes. */
8844c09535SAaron Tomlin sect_attrs->grp.name = "sections";
89*b83815afSThomas Weißschuh sect_attrs->grp.bin_attrs_new = gattr;
9044c09535SAaron Tomlin
9144c09535SAaron Tomlin sattr = §_attrs->attrs[0];
9244c09535SAaron Tomlin for (i = 0; i < info->hdr->e_shnum; i++) {
9344c09535SAaron Tomlin Elf_Shdr *sec = &info->sechdrs[i];
9444c09535SAaron Tomlin
9544c09535SAaron Tomlin if (sect_empty(sec))
9644c09535SAaron Tomlin continue;
9734f5ec0fSThomas Weißschuh sysfs_bin_attr_init(sattr);
9834f5ec0fSThomas Weißschuh sattr->attr.name =
9944c09535SAaron Tomlin kstrdup(info->secstrings + sec->sh_name, GFP_KERNEL);
10034f5ec0fSThomas Weißschuh if (!sattr->attr.name) {
101ce47f7cbSChunhui Li ret = -ENOMEM;
10244c09535SAaron Tomlin goto out;
103ce47f7cbSChunhui Li }
104*b83815afSThomas Weißschuh sattr->read_new = module_sect_read;
10534f5ec0fSThomas Weißschuh sattr->private = (void *)sec->sh_addr;
10634f5ec0fSThomas Weißschuh sattr->size = MODULE_SECT_READ_SIZE;
10734f5ec0fSThomas Weißschuh sattr->attr.mode = 0400;
10834f5ec0fSThomas Weißschuh *(gattr++) = sattr++;
10944c09535SAaron Tomlin }
11044c09535SAaron Tomlin
111ce47f7cbSChunhui Li ret = sysfs_create_group(&mod->mkobj.kobj, §_attrs->grp);
112ce47f7cbSChunhui Li if (ret)
11344c09535SAaron Tomlin goto out;
11444c09535SAaron Tomlin
11544c09535SAaron Tomlin mod->sect_attrs = sect_attrs;
116ce47f7cbSChunhui Li return 0;
11744c09535SAaron Tomlin out:
11844c09535SAaron Tomlin free_sect_attrs(sect_attrs);
119ce47f7cbSChunhui Li return ret;
12044c09535SAaron Tomlin }
12144c09535SAaron Tomlin
remove_sect_attrs(struct module * mod)12244c09535SAaron Tomlin static void remove_sect_attrs(struct module *mod)
12344c09535SAaron Tomlin {
12444c09535SAaron Tomlin if (mod->sect_attrs) {
12544c09535SAaron Tomlin sysfs_remove_group(&mod->mkobj.kobj,
12644c09535SAaron Tomlin &mod->sect_attrs->grp);
12744c09535SAaron Tomlin /*
12844c09535SAaron Tomlin * We are positive that no one is using any sect attrs
12944c09535SAaron Tomlin * at this point. Deallocate immediately.
13044c09535SAaron Tomlin */
13144c09535SAaron Tomlin free_sect_attrs(mod->sect_attrs);
13244c09535SAaron Tomlin mod->sect_attrs = NULL;
13344c09535SAaron Tomlin }
13444c09535SAaron Tomlin }
13544c09535SAaron Tomlin
13644c09535SAaron Tomlin /*
13744c09535SAaron Tomlin * /sys/module/foo/notes/.section.name gives contents of SHT_NOTE sections.
13844c09535SAaron Tomlin */
13944c09535SAaron Tomlin
14044c09535SAaron Tomlin struct module_notes_attrs {
1414723f16dSThomas Weißschuh struct attribute_group grp;
1424723f16dSThomas Weißschuh struct bin_attribute attrs[];
14344c09535SAaron Tomlin };
14444c09535SAaron Tomlin
free_notes_attrs(struct module_notes_attrs * notes_attrs)1454723f16dSThomas Weißschuh static void free_notes_attrs(struct module_notes_attrs *notes_attrs)
14644c09535SAaron Tomlin {
147*b83815afSThomas Weißschuh kfree(notes_attrs->grp.bin_attrs_new);
14844c09535SAaron Tomlin kfree(notes_attrs);
14944c09535SAaron Tomlin }
15044c09535SAaron Tomlin
add_notes_attrs(struct module * mod,const struct load_info * info)151ce47f7cbSChunhui Li static int add_notes_attrs(struct module *mod, const struct load_info *info)
15244c09535SAaron Tomlin {
15344c09535SAaron Tomlin unsigned int notes, loaded, i;
15444c09535SAaron Tomlin struct module_notes_attrs *notes_attrs;
155*b83815afSThomas Weißschuh const struct bin_attribute **gattr;
15644c09535SAaron Tomlin struct bin_attribute *nattr;
157ce47f7cbSChunhui Li int ret;
15844c09535SAaron Tomlin
15944c09535SAaron Tomlin /* Count notes sections and allocate structures. */
16044c09535SAaron Tomlin notes = 0;
16144c09535SAaron Tomlin for (i = 0; i < info->hdr->e_shnum; i++)
16244c09535SAaron Tomlin if (!sect_empty(&info->sechdrs[i]) &&
16344c09535SAaron Tomlin info->sechdrs[i].sh_type == SHT_NOTE)
16444c09535SAaron Tomlin ++notes;
16544c09535SAaron Tomlin
16644c09535SAaron Tomlin if (notes == 0)
167ce47f7cbSChunhui Li return 0;
16844c09535SAaron Tomlin
16944c09535SAaron Tomlin notes_attrs = kzalloc(struct_size(notes_attrs, attrs, notes),
17044c09535SAaron Tomlin GFP_KERNEL);
17144c09535SAaron Tomlin if (!notes_attrs)
172ce47f7cbSChunhui Li return -ENOMEM;
17344c09535SAaron Tomlin
1744723f16dSThomas Weißschuh gattr = kcalloc(notes + 1, sizeof(*gattr), GFP_KERNEL);
1754723f16dSThomas Weißschuh if (!gattr) {
1764723f16dSThomas Weißschuh kfree(notes_attrs);
1774723f16dSThomas Weißschuh return -ENOMEM;
1784723f16dSThomas Weißschuh }
1794723f16dSThomas Weißschuh
1804723f16dSThomas Weißschuh notes_attrs->grp.name = "notes";
181*b83815afSThomas Weißschuh notes_attrs->grp.bin_attrs_new = gattr;
1824723f16dSThomas Weißschuh
18344c09535SAaron Tomlin nattr = ¬es_attrs->attrs[0];
18444c09535SAaron Tomlin for (loaded = i = 0; i < info->hdr->e_shnum; ++i) {
18544c09535SAaron Tomlin if (sect_empty(&info->sechdrs[i]))
18644c09535SAaron Tomlin continue;
18744c09535SAaron Tomlin if (info->sechdrs[i].sh_type == SHT_NOTE) {
18844c09535SAaron Tomlin sysfs_bin_attr_init(nattr);
18934f5ec0fSThomas Weißschuh nattr->attr.name = mod->sect_attrs->attrs[loaded].attr.name;
19044c09535SAaron Tomlin nattr->attr.mode = 0444;
19144c09535SAaron Tomlin nattr->size = info->sechdrs[i].sh_size;
19244c09535SAaron Tomlin nattr->private = (void *)info->sechdrs[i].sh_addr;
19366bc1a17SLukas Wunner nattr->read_new = sysfs_bin_attr_simple_read;
1944723f16dSThomas Weißschuh *(gattr++) = nattr++;
19544c09535SAaron Tomlin }
19644c09535SAaron Tomlin ++loaded;
19744c09535SAaron Tomlin }
19844c09535SAaron Tomlin
1994723f16dSThomas Weißschuh ret = sysfs_create_group(&mod->mkobj.kobj, ¬es_attrs->grp);
200ce47f7cbSChunhui Li if (ret)
20144c09535SAaron Tomlin goto out;
20244c09535SAaron Tomlin
20344c09535SAaron Tomlin mod->notes_attrs = notes_attrs;
204ce47f7cbSChunhui Li return 0;
20544c09535SAaron Tomlin
20644c09535SAaron Tomlin out:
2074723f16dSThomas Weißschuh free_notes_attrs(notes_attrs);
208ce47f7cbSChunhui Li return ret;
20944c09535SAaron Tomlin }
21044c09535SAaron Tomlin
remove_notes_attrs(struct module * mod)21144c09535SAaron Tomlin static void remove_notes_attrs(struct module *mod)
21244c09535SAaron Tomlin {
2134723f16dSThomas Weißschuh if (mod->notes_attrs) {
2144723f16dSThomas Weißschuh sysfs_remove_group(&mod->mkobj.kobj,
2154723f16dSThomas Weißschuh &mod->notes_attrs->grp);
2164723f16dSThomas Weißschuh /*
2174723f16dSThomas Weißschuh * We are positive that no one is using any notes attrs
2184723f16dSThomas Weißschuh * at this point. Deallocate immediately.
2194723f16dSThomas Weißschuh */
2204723f16dSThomas Weißschuh free_notes_attrs(mod->notes_attrs);
2214723f16dSThomas Weißschuh mod->notes_attrs = NULL;
2224723f16dSThomas Weißschuh }
22344c09535SAaron Tomlin }
22444c09535SAaron Tomlin
22544c09535SAaron Tomlin #else /* !CONFIG_KALLSYMS */
add_sect_attrs(struct module * mod,const struct load_info * info)226ce47f7cbSChunhui Li static inline int add_sect_attrs(struct module *mod, const struct load_info *info)
227ce47f7cbSChunhui Li {
228ce47f7cbSChunhui Li return 0;
229ce47f7cbSChunhui Li }
remove_sect_attrs(struct module * mod)23044c09535SAaron Tomlin static inline void remove_sect_attrs(struct module *mod) { }
add_notes_attrs(struct module * mod,const struct load_info * info)231ce47f7cbSChunhui Li static inline int add_notes_attrs(struct module *mod, const struct load_info *info)
232ce47f7cbSChunhui Li {
233ce47f7cbSChunhui Li return 0;
234ce47f7cbSChunhui Li }
remove_notes_attrs(struct module * mod)23544c09535SAaron Tomlin static inline void remove_notes_attrs(struct module *mod) { }
23644c09535SAaron Tomlin #endif /* CONFIG_KALLSYMS */
23744c09535SAaron Tomlin
del_usage_links(struct module * mod)23844c09535SAaron Tomlin static void del_usage_links(struct module *mod)
23944c09535SAaron Tomlin {
24044c09535SAaron Tomlin #ifdef CONFIG_MODULE_UNLOAD
24144c09535SAaron Tomlin struct module_use *use;
24244c09535SAaron Tomlin
24344c09535SAaron Tomlin mutex_lock(&module_mutex);
24444c09535SAaron Tomlin list_for_each_entry(use, &mod->target_list, target_list)
24544c09535SAaron Tomlin sysfs_remove_link(use->target->holders_dir, mod->name);
24644c09535SAaron Tomlin mutex_unlock(&module_mutex);
24744c09535SAaron Tomlin #endif
24844c09535SAaron Tomlin }
24944c09535SAaron Tomlin
add_usage_links(struct module * mod)25044c09535SAaron Tomlin static int add_usage_links(struct module *mod)
25144c09535SAaron Tomlin {
25244c09535SAaron Tomlin int ret = 0;
25344c09535SAaron Tomlin #ifdef CONFIG_MODULE_UNLOAD
25444c09535SAaron Tomlin struct module_use *use;
25544c09535SAaron Tomlin
25644c09535SAaron Tomlin mutex_lock(&module_mutex);
25744c09535SAaron Tomlin list_for_each_entry(use, &mod->target_list, target_list) {
25844c09535SAaron Tomlin ret = sysfs_create_link(use->target->holders_dir,
25944c09535SAaron Tomlin &mod->mkobj.kobj, mod->name);
26044c09535SAaron Tomlin if (ret)
26144c09535SAaron Tomlin break;
26244c09535SAaron Tomlin }
26344c09535SAaron Tomlin mutex_unlock(&module_mutex);
26444c09535SAaron Tomlin if (ret)
26544c09535SAaron Tomlin del_usage_links(mod);
26644c09535SAaron Tomlin #endif
26744c09535SAaron Tomlin return ret;
26844c09535SAaron Tomlin }
26944c09535SAaron Tomlin
module_remove_modinfo_attrs(struct module * mod,int end)27044c09535SAaron Tomlin static void module_remove_modinfo_attrs(struct module *mod, int end)
27144c09535SAaron Tomlin {
272f3227ffdSThomas Weißschuh const struct module_attribute *attr;
27344c09535SAaron Tomlin int i;
27444c09535SAaron Tomlin
27544c09535SAaron Tomlin for (i = 0; (attr = &mod->modinfo_attrs[i]); i++) {
27644c09535SAaron Tomlin if (end >= 0 && i > end)
27744c09535SAaron Tomlin break;
27844c09535SAaron Tomlin /* pick a field to test for end of list */
27944c09535SAaron Tomlin if (!attr->attr.name)
28044c09535SAaron Tomlin break;
28144c09535SAaron Tomlin sysfs_remove_file(&mod->mkobj.kobj, &attr->attr);
28244c09535SAaron Tomlin if (attr->free)
28344c09535SAaron Tomlin attr->free(mod);
28444c09535SAaron Tomlin }
28544c09535SAaron Tomlin kfree(mod->modinfo_attrs);
28644c09535SAaron Tomlin }
28744c09535SAaron Tomlin
module_add_modinfo_attrs(struct module * mod)28844c09535SAaron Tomlin static int module_add_modinfo_attrs(struct module *mod)
28944c09535SAaron Tomlin {
290f3227ffdSThomas Weißschuh const struct module_attribute *attr;
29144c09535SAaron Tomlin struct module_attribute *temp_attr;
29244c09535SAaron Tomlin int error = 0;
29344c09535SAaron Tomlin int i;
29444c09535SAaron Tomlin
29544c09535SAaron Tomlin mod->modinfo_attrs = kzalloc((sizeof(struct module_attribute) *
29644c09535SAaron Tomlin (modinfo_attrs_count + 1)),
29744c09535SAaron Tomlin GFP_KERNEL);
29844c09535SAaron Tomlin if (!mod->modinfo_attrs)
29944c09535SAaron Tomlin return -ENOMEM;
30044c09535SAaron Tomlin
30144c09535SAaron Tomlin temp_attr = mod->modinfo_attrs;
30244c09535SAaron Tomlin for (i = 0; (attr = modinfo_attrs[i]); i++) {
30344c09535SAaron Tomlin if (!attr->test || attr->test(mod)) {
30444c09535SAaron Tomlin memcpy(temp_attr, attr, sizeof(*temp_attr));
30544c09535SAaron Tomlin sysfs_attr_init(&temp_attr->attr);
30644c09535SAaron Tomlin error = sysfs_create_file(&mod->mkobj.kobj,
30744c09535SAaron Tomlin &temp_attr->attr);
30844c09535SAaron Tomlin if (error)
30944c09535SAaron Tomlin goto error_out;
31044c09535SAaron Tomlin ++temp_attr;
31144c09535SAaron Tomlin }
31244c09535SAaron Tomlin }
31344c09535SAaron Tomlin
31444c09535SAaron Tomlin return 0;
31544c09535SAaron Tomlin
31644c09535SAaron Tomlin error_out:
31744c09535SAaron Tomlin if (i > 0)
31844c09535SAaron Tomlin module_remove_modinfo_attrs(mod, --i);
31944c09535SAaron Tomlin else
32044c09535SAaron Tomlin kfree(mod->modinfo_attrs);
32144c09535SAaron Tomlin return error;
32244c09535SAaron Tomlin }
32344c09535SAaron Tomlin
mod_kobject_put(struct module * mod)32444c09535SAaron Tomlin static void mod_kobject_put(struct module *mod)
32544c09535SAaron Tomlin {
32644c09535SAaron Tomlin DECLARE_COMPLETION_ONSTACK(c);
32744c09535SAaron Tomlin
32844c09535SAaron Tomlin mod->mkobj.kobj_completion = &c;
32944c09535SAaron Tomlin kobject_put(&mod->mkobj.kobj);
33044c09535SAaron Tomlin wait_for_completion(&c);
33144c09535SAaron Tomlin }
33244c09535SAaron Tomlin
mod_sysfs_init(struct module * mod)33344c09535SAaron Tomlin static int mod_sysfs_init(struct module *mod)
33444c09535SAaron Tomlin {
33544c09535SAaron Tomlin int err;
33644c09535SAaron Tomlin struct kobject *kobj;
33744c09535SAaron Tomlin
3383cd60866SRasmus Villemoes if (!module_kset) {
33944c09535SAaron Tomlin pr_err("%s: module sysfs not initialized\n", mod->name);
34044c09535SAaron Tomlin err = -EINVAL;
34144c09535SAaron Tomlin goto out;
34244c09535SAaron Tomlin }
34344c09535SAaron Tomlin
34444c09535SAaron Tomlin kobj = kset_find_obj(module_kset, mod->name);
34544c09535SAaron Tomlin if (kobj) {
34644c09535SAaron Tomlin pr_err("%s: module is already loaded\n", mod->name);
34744c09535SAaron Tomlin kobject_put(kobj);
34844c09535SAaron Tomlin err = -EINVAL;
34944c09535SAaron Tomlin goto out;
35044c09535SAaron Tomlin }
35144c09535SAaron Tomlin
35244c09535SAaron Tomlin mod->mkobj.mod = mod;
35344c09535SAaron Tomlin
35444c09535SAaron Tomlin memset(&mod->mkobj.kobj, 0, sizeof(mod->mkobj.kobj));
35544c09535SAaron Tomlin mod->mkobj.kobj.kset = module_kset;
35644c09535SAaron Tomlin err = kobject_init_and_add(&mod->mkobj.kobj, &module_ktype, NULL,
35744c09535SAaron Tomlin "%s", mod->name);
35844c09535SAaron Tomlin if (err)
35944c09535SAaron Tomlin mod_kobject_put(mod);
36044c09535SAaron Tomlin
36144c09535SAaron Tomlin out:
36244c09535SAaron Tomlin return err;
36344c09535SAaron Tomlin }
36444c09535SAaron Tomlin
mod_sysfs_setup(struct module * mod,const struct load_info * info,struct kernel_param * kparam,unsigned int num_params)36544c09535SAaron Tomlin int mod_sysfs_setup(struct module *mod,
36644c09535SAaron Tomlin const struct load_info *info,
36744c09535SAaron Tomlin struct kernel_param *kparam,
36844c09535SAaron Tomlin unsigned int num_params)
36944c09535SAaron Tomlin {
37044c09535SAaron Tomlin int err;
37144c09535SAaron Tomlin
37244c09535SAaron Tomlin err = mod_sysfs_init(mod);
37344c09535SAaron Tomlin if (err)
37444c09535SAaron Tomlin goto out;
37544c09535SAaron Tomlin
37644c09535SAaron Tomlin mod->holders_dir = kobject_create_and_add("holders", &mod->mkobj.kobj);
37744c09535SAaron Tomlin if (!mod->holders_dir) {
37844c09535SAaron Tomlin err = -ENOMEM;
37944c09535SAaron Tomlin goto out_unreg;
38044c09535SAaron Tomlin }
38144c09535SAaron Tomlin
38244c09535SAaron Tomlin err = module_param_sysfs_setup(mod, kparam, num_params);
38344c09535SAaron Tomlin if (err)
38444c09535SAaron Tomlin goto out_unreg_holders;
38544c09535SAaron Tomlin
38644c09535SAaron Tomlin err = module_add_modinfo_attrs(mod);
38744c09535SAaron Tomlin if (err)
38844c09535SAaron Tomlin goto out_unreg_param;
38944c09535SAaron Tomlin
39044c09535SAaron Tomlin err = add_usage_links(mod);
39144c09535SAaron Tomlin if (err)
39244c09535SAaron Tomlin goto out_unreg_modinfo_attrs;
39344c09535SAaron Tomlin
394ce47f7cbSChunhui Li err = add_sect_attrs(mod, info);
395ce47f7cbSChunhui Li if (err)
396ce47f7cbSChunhui Li goto out_del_usage_links;
397ce47f7cbSChunhui Li
398ce47f7cbSChunhui Li err = add_notes_attrs(mod, info);
399ce47f7cbSChunhui Li if (err)
400ce47f7cbSChunhui Li goto out_unreg_sect_attrs;
40144c09535SAaron Tomlin
40244c09535SAaron Tomlin return 0;
40344c09535SAaron Tomlin
404ce47f7cbSChunhui Li out_unreg_sect_attrs:
405ce47f7cbSChunhui Li remove_sect_attrs(mod);
406ce47f7cbSChunhui Li out_del_usage_links:
407ce47f7cbSChunhui Li del_usage_links(mod);
40844c09535SAaron Tomlin out_unreg_modinfo_attrs:
40944c09535SAaron Tomlin module_remove_modinfo_attrs(mod, -1);
41044c09535SAaron Tomlin out_unreg_param:
41144c09535SAaron Tomlin module_param_sysfs_remove(mod);
41244c09535SAaron Tomlin out_unreg_holders:
41344c09535SAaron Tomlin kobject_put(mod->holders_dir);
41444c09535SAaron Tomlin out_unreg:
41544c09535SAaron Tomlin mod_kobject_put(mod);
41644c09535SAaron Tomlin out:
41744c09535SAaron Tomlin return err;
41844c09535SAaron Tomlin }
41944c09535SAaron Tomlin
mod_sysfs_fini(struct module * mod)42044c09535SAaron Tomlin static void mod_sysfs_fini(struct module *mod)
42144c09535SAaron Tomlin {
42244c09535SAaron Tomlin remove_notes_attrs(mod);
42344c09535SAaron Tomlin remove_sect_attrs(mod);
42444c09535SAaron Tomlin mod_kobject_put(mod);
42544c09535SAaron Tomlin }
42644c09535SAaron Tomlin
mod_sysfs_teardown(struct module * mod)42744c09535SAaron Tomlin void mod_sysfs_teardown(struct module *mod)
42844c09535SAaron Tomlin {
42944c09535SAaron Tomlin del_usage_links(mod);
43044c09535SAaron Tomlin module_remove_modinfo_attrs(mod, -1);
43144c09535SAaron Tomlin module_param_sysfs_remove(mod);
43244c09535SAaron Tomlin kobject_put(mod->mkobj.drivers_dir);
43344c09535SAaron Tomlin kobject_put(mod->holders_dir);
43444c09535SAaron Tomlin mod_sysfs_fini(mod);
43544c09535SAaron Tomlin }
43644c09535SAaron Tomlin
init_param_lock(struct module * mod)43744c09535SAaron Tomlin void init_param_lock(struct module *mod)
43844c09535SAaron Tomlin {
43944c09535SAaron Tomlin mutex_init(&mod->param_lock);
44044c09535SAaron Tomlin }
441