xref: /linux-6.15/kernel/module/sysfs.c (revision b83815af)
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 = &sect_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, &sect_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 = &notes_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, &notes_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