Re: [PATCH v8 11/13] module: Move sysfs support into a separate file

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




Le 22/02/2022 à 15:13, Aaron Tomlin a écrit :
> No functional change.
> 
> This patch migrates module sysfs support out of core code into
> kernel/module/sysfs.c. In addition simple code refactoring to
> make this possible.
> 
> Signed-off-by: Aaron Tomlin <atomlin@xxxxxxxxxx>

Reviewed-by: Christophe Leroy <christophe.leroy@xxxxxxxxxx>

> ---
>   kernel/module/Makefile   |   1 +
>   kernel/module/internal.h |  21 ++
>   kernel/module/main.c     | 469 +--------------------------------------
>   kernel/module/sysfs.c    | 436 ++++++++++++++++++++++++++++++++++++
>   4 files changed, 461 insertions(+), 466 deletions(-)
>   create mode 100644 kernel/module/sysfs.c
> 
> diff --git a/kernel/module/Makefile b/kernel/module/Makefile
> index 94296c98a67f..cf8dcdc6b55f 100644
> --- a/kernel/module/Makefile
> +++ b/kernel/module/Makefile
> @@ -16,3 +16,4 @@ obj-$(CONFIG_STRICT_MODULE_RWX) += strict_rwx.o
>   obj-$(CONFIG_DEBUG_KMEMLEAK) += debug_kmemleak.o
>   obj-$(CONFIG_KALLSYMS) += kallsyms.o
>   obj-$(CONFIG_PROC_FS) += procfs.o
> +obj-$(CONFIG_SYSFS) += sysfs.o
> diff --git a/kernel/module/internal.h b/kernel/module/internal.h
> index 6af40c2d145f..62d749ef695e 100644
> --- a/kernel/module/internal.h
> +++ b/kernel/module/internal.h
> @@ -34,6 +34,9 @@
>   extern struct mutex module_mutex;
>   extern struct list_head modules;
>   
> +extern struct module_attribute *modinfo_attrs[];
> +extern size_t modinfo_attrs_count;
> +
>   /* Provided by the linker */
>   extern const struct kernel_symbol __start___ksymtab[];
>   extern const struct kernel_symbol __stop___ksymtab[];
> @@ -204,3 +207,21 @@ static inline void init_build_id(struct module *mod, const struct load_info *inf
>   static inline void layout_symtab(struct module *mod, struct load_info *info) { }
>   static inline void add_kallsyms(struct module *mod, const struct load_info *info) { }
>   #endif /* CONFIG_KALLSYMS */
> +
> +#ifdef CONFIG_SYSFS
> +int mod_sysfs_setup(struct module *mod, const struct load_info *info,
> +		    struct kernel_param *kparam, unsigned int num_params);
> +void mod_sysfs_teardown(struct module *mod);
> +void init_param_lock(struct module *mod);
> +#else /* !CONFIG_SYSFS */
> +static inline int mod_sysfs_setup(struct module *mod,
> +			   	  const struct load_info *info,
> +			   	  struct kernel_param *kparam,
> +			   	  unsigned int num_params)
> +{
> +	return 0;
> +}
> +
> +static inline void mod_sysfs_teardown(struct module *mod) { }
> +static inline void init_param_lock(struct module *mod) { }
> +#endif /* CONFIG_SYSFS */
> diff --git a/kernel/module/main.c b/kernel/module/main.c
> index 44b6fd1acc44..b8a59b5c3e3a 100644
> --- a/kernel/module/main.c
> +++ b/kernel/module/main.c
> @@ -14,9 +14,7 @@
>   #include <linux/init.h>
>   #include <linux/kallsyms.h>
>   #include <linux/buildid.h>
> -#include <linux/file.h>
>   #include <linux/fs.h>
> -#include <linux/sysfs.h>
>   #include <linux/kernel.h>
>   #include <linux/kernel_read_file.h>
>   #include <linux/slab.h>
> @@ -989,7 +987,7 @@ static ssize_t show_taint(struct module_attribute *mattr,
>   static struct module_attribute modinfo_taint =
>   	__ATTR(taint, 0444, show_taint, NULL);
>   
> -static struct module_attribute *modinfo_attrs[] = {
> +struct module_attribute *modinfo_attrs[] = {
>   	&module_uevent,
>   	&modinfo_version,
>   	&modinfo_srcversion,
> @@ -1003,6 +1001,8 @@ static struct module_attribute *modinfo_attrs[] = {
>   	NULL,
>   };
>   
> +size_t modinfo_attrs_count = ARRAY_SIZE(modinfo_attrs);
> +
>   static const char vermagic[] = VERMAGIC_STRING;
>   
>   static int try_to_force_load(struct module *mod, const char *reason)
> @@ -1253,469 +1253,6 @@ resolve_symbol_wait(struct module *mod,
>   	return ksym;
>   }
>   
> -/*
> - * /sys/module/foo/sections stuff
> - * J. Corbet <corbet@xxxxxxx>
> - */
> -#ifdef CONFIG_SYSFS
> -
> -#ifdef CONFIG_KALLSYMS
> -struct module_sect_attr {
> -	struct bin_attribute battr;
> -	unsigned long address;
> -};
> -
> -struct module_sect_attrs {
> -	struct attribute_group grp;
> -	unsigned int nsections;
> -	struct module_sect_attr attrs[];
> -};
> -
> -#define MODULE_SECT_READ_SIZE (3 /* "0x", "\n" */ + (BITS_PER_LONG / 4))
> -static ssize_t module_sect_read(struct file *file, struct kobject *kobj,
> -				struct bin_attribute *battr,
> -				char *buf, loff_t pos, size_t count)
> -{
> -	struct module_sect_attr *sattr =
> -		container_of(battr, struct module_sect_attr, battr);
> -	char bounce[MODULE_SECT_READ_SIZE + 1];
> -	size_t wrote;
> -
> -	if (pos != 0)
> -		return -EINVAL;
> -
> -	/*
> -	 * Since we're a binary read handler, we must account for the
> -	 * trailing NUL byte that sprintf will write: if "buf" is
> -	 * too small to hold the NUL, or the NUL is exactly the last
> -	 * byte, the read will look like it got truncated by one byte.
> -	 * Since there is no way to ask sprintf nicely to not write
> -	 * the NUL, we have to use a bounce buffer.
> -	 */
> -	wrote = scnprintf(bounce, sizeof(bounce), "0x%px\n",
> -			 kallsyms_show_value(file->f_cred)
> -				? (void *)sattr->address : NULL);
> -	count = min(count, wrote);
> -	memcpy(buf, bounce, count);
> -
> -	return count;
> -}
> -
> -static void free_sect_attrs(struct module_sect_attrs *sect_attrs)
> -{
> -	unsigned int section;
> -
> -	for (section = 0; section < sect_attrs->nsections; section++)
> -		kfree(sect_attrs->attrs[section].battr.attr.name);
> -	kfree(sect_attrs);
> -}
> -
> -static void add_sect_attrs(struct module *mod, const struct load_info *info)
> -{
> -	unsigned int nloaded = 0, i, size[2];
> -	struct module_sect_attrs *sect_attrs;
> -	struct module_sect_attr *sattr;
> -	struct bin_attribute **gattr;
> -
> -	/* Count loaded sections and allocate structures */
> -	for (i = 0; i < info->hdr->e_shnum; i++)
> -		if (!sect_empty(&info->sechdrs[i]))
> -			nloaded++;
> -	size[0] = ALIGN(struct_size(sect_attrs, attrs, nloaded),
> -			sizeof(sect_attrs->grp.bin_attrs[0]));
> -	size[1] = (nloaded + 1) * sizeof(sect_attrs->grp.bin_attrs[0]);
> -	sect_attrs = kzalloc(size[0] + size[1], GFP_KERNEL);
> -	if (sect_attrs == NULL)
> -		return;
> -
> -	/* Setup section attributes. */
> -	sect_attrs->grp.name = "sections";
> -	sect_attrs->grp.bin_attrs = (void *)sect_attrs + size[0];
> -
> -	sect_attrs->nsections = 0;
> -	sattr = &sect_attrs->attrs[0];
> -	gattr = &sect_attrs->grp.bin_attrs[0];
> -	for (i = 0; i < info->hdr->e_shnum; i++) {
> -		Elf_Shdr *sec = &info->sechdrs[i];
> -		if (sect_empty(sec))
> -			continue;
> -		sysfs_bin_attr_init(&sattr->battr);
> -		sattr->address = sec->sh_addr;
> -		sattr->battr.attr.name =
> -			kstrdup(info->secstrings + sec->sh_name, GFP_KERNEL);
> -		if (sattr->battr.attr.name == NULL)
> -			goto out;
> -		sect_attrs->nsections++;
> -		sattr->battr.read = module_sect_read;
> -		sattr->battr.size = MODULE_SECT_READ_SIZE;
> -		sattr->battr.attr.mode = 0400;
> -		*(gattr++) = &(sattr++)->battr;
> -	}
> -	*gattr = NULL;
> -
> -	if (sysfs_create_group(&mod->mkobj.kobj, &sect_attrs->grp))
> -		goto out;
> -
> -	mod->sect_attrs = sect_attrs;
> -	return;
> -  out:
> -	free_sect_attrs(sect_attrs);
> -}
> -
> -static void remove_sect_attrs(struct module *mod)
> -{
> -	if (mod->sect_attrs) {
> -		sysfs_remove_group(&mod->mkobj.kobj,
> -				   &mod->sect_attrs->grp);
> -		/*
> -		 * We are positive that no one is using any sect attrs
> -		 * at this point.  Deallocate immediately.
> -		 */
> -		free_sect_attrs(mod->sect_attrs);
> -		mod->sect_attrs = NULL;
> -	}
> -}
> -
> -/*
> - * /sys/module/foo/notes/.section.name gives contents of SHT_NOTE sections.
> - */
> -
> -struct module_notes_attrs {
> -	struct kobject *dir;
> -	unsigned int notes;
> -	struct bin_attribute attrs[];
> -};
> -
> -static ssize_t module_notes_read(struct file *filp, struct kobject *kobj,
> -				 struct bin_attribute *bin_attr,
> -				 char *buf, loff_t pos, size_t count)
> -{
> -	/*
> -	 * The caller checked the pos and count against our size.
> -	 */
> -	memcpy(buf, bin_attr->private + pos, count);
> -	return count;
> -}
> -
> -static void free_notes_attrs(struct module_notes_attrs *notes_attrs,
> -			     unsigned int i)
> -{
> -	if (notes_attrs->dir) {
> -		while (i-- > 0)
> -			sysfs_remove_bin_file(notes_attrs->dir,
> -					      &notes_attrs->attrs[i]);
> -		kobject_put(notes_attrs->dir);
> -	}
> -	kfree(notes_attrs);
> -}
> -
> -static void add_notes_attrs(struct module *mod, const struct load_info *info)
> -{
> -	unsigned int notes, loaded, i;
> -	struct module_notes_attrs *notes_attrs;
> -	struct bin_attribute *nattr;
> -
> -	/* failed to create section attributes, so can't create notes */
> -	if (!mod->sect_attrs)
> -		return;
> -
> -	/* Count notes sections and allocate structures.  */
> -	notes = 0;
> -	for (i = 0; i < info->hdr->e_shnum; i++)
> -		if (!sect_empty(&info->sechdrs[i]) &&
> -		    (info->sechdrs[i].sh_type == SHT_NOTE))
> -			++notes;
> -
> -	if (notes == 0)
> -		return;
> -
> -	notes_attrs = kzalloc(struct_size(notes_attrs, attrs, notes),
> -			      GFP_KERNEL);
> -	if (notes_attrs == NULL)
> -		return;
> -
> -	notes_attrs->notes = notes;
> -	nattr = &notes_attrs->attrs[0];
> -	for (loaded = i = 0; i < info->hdr->e_shnum; ++i) {
> -		if (sect_empty(&info->sechdrs[i]))
> -			continue;
> -		if (info->sechdrs[i].sh_type == SHT_NOTE) {
> -			sysfs_bin_attr_init(nattr);
> -			nattr->attr.name = mod->sect_attrs->attrs[loaded].battr.attr.name;
> -			nattr->attr.mode = S_IRUGO;
> -			nattr->size = info->sechdrs[i].sh_size;
> -			nattr->private = (void *) info->sechdrs[i].sh_addr;
> -			nattr->read = module_notes_read;
> -			++nattr;
> -		}
> -		++loaded;
> -	}
> -
> -	notes_attrs->dir = kobject_create_and_add("notes", &mod->mkobj.kobj);
> -	if (!notes_attrs->dir)
> -		goto out;
> -
> -	for (i = 0; i < notes; ++i)
> -		if (sysfs_create_bin_file(notes_attrs->dir,
> -					  &notes_attrs->attrs[i]))
> -			goto out;
> -
> -	mod->notes_attrs = notes_attrs;
> -	return;
> -
> -  out:
> -	free_notes_attrs(notes_attrs, i);
> -}
> -
> -static void remove_notes_attrs(struct module *mod)
> -{
> -	if (mod->notes_attrs)
> -		free_notes_attrs(mod->notes_attrs, mod->notes_attrs->notes);
> -}
> -
> -#else
> -
> -static inline void add_sect_attrs(struct module *mod,
> -				  const struct load_info *info)
> -{
> -}
> -
> -static inline void remove_sect_attrs(struct module *mod)
> -{
> -}
> -
> -static inline void add_notes_attrs(struct module *mod,
> -				   const struct load_info *info)
> -{
> -}
> -
> -static inline void remove_notes_attrs(struct module *mod)
> -{
> -}
> -#endif /* CONFIG_KALLSYMS */
> -
> -static void del_usage_links(struct module *mod)
> -{
> -#ifdef CONFIG_MODULE_UNLOAD
> -	struct module_use *use;
> -
> -	mutex_lock(&module_mutex);
> -	list_for_each_entry(use, &mod->target_list, target_list)
> -		sysfs_remove_link(use->target->holders_dir, mod->name);
> -	mutex_unlock(&module_mutex);
> -#endif
> -}
> -
> -static int add_usage_links(struct module *mod)
> -{
> -	int ret = 0;
> -#ifdef CONFIG_MODULE_UNLOAD
> -	struct module_use *use;
> -
> -	mutex_lock(&module_mutex);
> -	list_for_each_entry(use, &mod->target_list, target_list) {
> -		ret = sysfs_create_link(use->target->holders_dir,
> -					&mod->mkobj.kobj, mod->name);
> -		if (ret)
> -			break;
> -	}
> -	mutex_unlock(&module_mutex);
> -	if (ret)
> -		del_usage_links(mod);
> -#endif
> -	return ret;
> -}
> -
> -static void module_remove_modinfo_attrs(struct module *mod, int end);
> -
> -static int module_add_modinfo_attrs(struct module *mod)
> -{
> -	struct module_attribute *attr;
> -	struct module_attribute *temp_attr;
> -	int error = 0;
> -	int i;
> -
> -	mod->modinfo_attrs = kzalloc((sizeof(struct module_attribute) *
> -					(ARRAY_SIZE(modinfo_attrs) + 1)),
> -					GFP_KERNEL);
> -	if (!mod->modinfo_attrs)
> -		return -ENOMEM;
> -
> -	temp_attr = mod->modinfo_attrs;
> -	for (i = 0; (attr = modinfo_attrs[i]); i++) {
> -		if (!attr->test || attr->test(mod)) {
> -			memcpy(temp_attr, attr, sizeof(*temp_attr));
> -			sysfs_attr_init(&temp_attr->attr);
> -			error = sysfs_create_file(&mod->mkobj.kobj,
> -					&temp_attr->attr);
> -			if (error)
> -				goto error_out;
> -			++temp_attr;
> -		}
> -	}
> -
> -	return 0;
> -
> -error_out:
> -	if (i > 0)
> -		module_remove_modinfo_attrs(mod, --i);
> -	else
> -		kfree(mod->modinfo_attrs);
> -	return error;
> -}
> -
> -static void module_remove_modinfo_attrs(struct module *mod, int end)
> -{
> -	struct module_attribute *attr;
> -	int i;
> -
> -	for (i = 0; (attr = &mod->modinfo_attrs[i]); i++) {
> -		if (end >= 0 && i > end)
> -			break;
> -		/* pick a field to test for end of list */
> -		if (!attr->attr.name)
> -			break;
> -		sysfs_remove_file(&mod->mkobj.kobj, &attr->attr);
> -		if (attr->free)
> -			attr->free(mod);
> -	}
> -	kfree(mod->modinfo_attrs);
> -}
> -
> -static void mod_kobject_put(struct module *mod)
> -{
> -	DECLARE_COMPLETION_ONSTACK(c);
> -	mod->mkobj.kobj_completion = &c;
> -	kobject_put(&mod->mkobj.kobj);
> -	wait_for_completion(&c);
> -}
> -
> -static int mod_sysfs_init(struct module *mod)
> -{
> -	int err;
> -	struct kobject *kobj;
> -
> -	if (!module_sysfs_initialized) {
> -		pr_err("%s: module sysfs not initialized\n", mod->name);
> -		err = -EINVAL;
> -		goto out;
> -	}
> -
> -	kobj = kset_find_obj(module_kset, mod->name);
> -	if (kobj) {
> -		pr_err("%s: module is already loaded\n", mod->name);
> -		kobject_put(kobj);
> -		err = -EINVAL;
> -		goto out;
> -	}
> -
> -	mod->mkobj.mod = mod;
> -
> -	memset(&mod->mkobj.kobj, 0, sizeof(mod->mkobj.kobj));
> -	mod->mkobj.kobj.kset = module_kset;
> -	err = kobject_init_and_add(&mod->mkobj.kobj, &module_ktype, NULL,
> -				   "%s", mod->name);
> -	if (err)
> -		mod_kobject_put(mod);
> -
> -out:
> -	return err;
> -}
> -
> -static int mod_sysfs_setup(struct module *mod,
> -			   const struct load_info *info,
> -			   struct kernel_param *kparam,
> -			   unsigned int num_params)
> -{
> -	int err;
> -
> -	err = mod_sysfs_init(mod);
> -	if (err)
> -		goto out;
> -
> -	mod->holders_dir = kobject_create_and_add("holders", &mod->mkobj.kobj);
> -	if (!mod->holders_dir) {
> -		err = -ENOMEM;
> -		goto out_unreg;
> -	}
> -
> -	err = module_param_sysfs_setup(mod, kparam, num_params);
> -	if (err)
> -		goto out_unreg_holders;
> -
> -	err = module_add_modinfo_attrs(mod);
> -	if (err)
> -		goto out_unreg_param;
> -
> -	err = add_usage_links(mod);
> -	if (err)
> -		goto out_unreg_modinfo_attrs;
> -
> -	add_sect_attrs(mod, info);
> -	add_notes_attrs(mod, info);
> -
> -	return 0;
> -
> -out_unreg_modinfo_attrs:
> -	module_remove_modinfo_attrs(mod, -1);
> -out_unreg_param:
> -	module_param_sysfs_remove(mod);
> -out_unreg_holders:
> -	kobject_put(mod->holders_dir);
> -out_unreg:
> -	mod_kobject_put(mod);
> -out:
> -	return err;
> -}
> -
> -static void mod_sysfs_fini(struct module *mod)
> -{
> -	remove_notes_attrs(mod);
> -	remove_sect_attrs(mod);
> -	mod_kobject_put(mod);
> -}
> -
> -static void init_param_lock(struct module *mod)
> -{
> -	mutex_init(&mod->param_lock);
> -}
> -#else /* !CONFIG_SYSFS */
> -
> -static int mod_sysfs_setup(struct module *mod,
> -			   const struct load_info *info,
> -			   struct kernel_param *kparam,
> -			   unsigned int num_params)
> -{
> -	return 0;
> -}
> -
> -static void mod_sysfs_fini(struct module *mod)
> -{
> -}
> -
> -static void module_remove_modinfo_attrs(struct module *mod, int end)
> -{
> -}
> -
> -static void del_usage_links(struct module *mod)
> -{
> -}
> -
> -static void init_param_lock(struct module *mod)
> -{
> -}
> -#endif /* CONFIG_SYSFS */
> -
> -static void mod_sysfs_teardown(struct module *mod)
> -{
> -	del_usage_links(mod);
> -	module_remove_modinfo_attrs(mod, -1);
> -	module_param_sysfs_remove(mod);
> -	kobject_put(mod->mkobj.drivers_dir);
> -	kobject_put(mod->holders_dir);
> -	mod_sysfs_fini(mod);
> -}
> -
>   /*
>    * LKM RO/NX protection: protect module's text/ro-data
>    * from modification and any data from execution.
> diff --git a/kernel/module/sysfs.c b/kernel/module/sysfs.c
> new file mode 100644
> index 000000000000..ce68f821dcd1
> --- /dev/null
> +++ b/kernel/module/sysfs.c
> @@ -0,0 +1,436 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Module sysfs support
> + *
> + * Copyright (C) 2008 Rusty Russell
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/fs.h>
> +#include <linux/sysfs.h>
> +#include <linux/slab.h>
> +#include <linux/kallsyms.h>
> +#include <linux/mutex.h>
> +#include "internal.h"
> +
> +/*
> + * /sys/module/foo/sections stuff
> + * J. Corbet <corbet@xxxxxxx>
> + */
> +#ifdef CONFIG_KALLSYMS
> +struct module_sect_attr {
> +	struct bin_attribute battr;
> +	unsigned long address;
> +};
> +
> +struct module_sect_attrs {
> +	struct attribute_group grp;
> +	unsigned int nsections;
> +	struct module_sect_attr attrs[];
> +};
> +
> +#define MODULE_SECT_READ_SIZE (3 /* "0x", "\n" */ + (BITS_PER_LONG / 4))
> +static ssize_t module_sect_read(struct file *file, struct kobject *kobj,
> +				struct bin_attribute *battr,
> +				char *buf, loff_t pos, size_t count)
> +{
> +	struct module_sect_attr *sattr =
> +		container_of(battr, struct module_sect_attr, battr);
> +	char bounce[MODULE_SECT_READ_SIZE + 1];
> +	size_t wrote;
> +
> +	if (pos != 0)
> +		return -EINVAL;
> +
> +	/*
> +	 * Since we're a binary read handler, we must account for the
> +	 * trailing NUL byte that sprintf will write: if "buf" is
> +	 * too small to hold the NUL, or the NUL is exactly the last
> +	 * byte, the read will look like it got truncated by one byte.
> +	 * Since there is no way to ask sprintf nicely to not write
> +	 * the NUL, we have to use a bounce buffer.
> +	 */
> +	wrote = scnprintf(bounce, sizeof(bounce), "0x%px\n",
> +			  kallsyms_show_value(file->f_cred)
> +				? (void *)sattr->address : NULL);
> +	count = min(count, wrote);
> +	memcpy(buf, bounce, count);
> +
> +	return count;
> +}
> +
> +static void free_sect_attrs(struct module_sect_attrs *sect_attrs)
> +{
> +	unsigned int section;
> +
> +	for (section = 0; section < sect_attrs->nsections; section++)
> +		kfree(sect_attrs->attrs[section].battr.attr.name);
> +	kfree(sect_attrs);
> +}
> +
> +static void add_sect_attrs(struct module *mod, const struct load_info *info)
> +{
> +	unsigned int nloaded = 0, i, size[2];
> +	struct module_sect_attrs *sect_attrs;
> +	struct module_sect_attr *sattr;
> +	struct bin_attribute **gattr;
> +
> +	/* Count loaded sections and allocate structures */
> +	for (i = 0; i < info->hdr->e_shnum; i++)
> +		if (!sect_empty(&info->sechdrs[i]))
> +			nloaded++;
> +	size[0] = ALIGN(struct_size(sect_attrs, attrs, nloaded),
> +			sizeof(sect_attrs->grp.bin_attrs[0]));
> +	size[1] = (nloaded + 1) * sizeof(sect_attrs->grp.bin_attrs[0]);
> +	sect_attrs = kzalloc(size[0] + size[1], GFP_KERNEL);
> +	if (!sect_attrs)
> +		return;
> +
> +	/* Setup section attributes. */
> +	sect_attrs->grp.name = "sections";
> +	sect_attrs->grp.bin_attrs = (void *)sect_attrs + size[0];
> +
> +	sect_attrs->nsections = 0;
> +	sattr = &sect_attrs->attrs[0];
> +	gattr = &sect_attrs->grp.bin_attrs[0];
> +	for (i = 0; i < info->hdr->e_shnum; i++) {
> +		Elf_Shdr *sec = &info->sechdrs[i];
> +
> +		if (sect_empty(sec))
> +			continue;
> +		sysfs_bin_attr_init(&sattr->battr);
> +		sattr->address = sec->sh_addr;
> +		sattr->battr.attr.name =
> +			kstrdup(info->secstrings + sec->sh_name, GFP_KERNEL);
> +		if (!sattr->battr.attr.name)
> +			goto out;
> +		sect_attrs->nsections++;
> +		sattr->battr.read = module_sect_read;
> +		sattr->battr.size = MODULE_SECT_READ_SIZE;
> +		sattr->battr.attr.mode = 0400;
> +		*(gattr++) = &(sattr++)->battr;
> +	}
> +	*gattr = NULL;
> +
> +	if (sysfs_create_group(&mod->mkobj.kobj, &sect_attrs->grp))
> +		goto out;
> +
> +	mod->sect_attrs = sect_attrs;
> +	return;
> +out:
> +	free_sect_attrs(sect_attrs);
> +}
> +
> +static void remove_sect_attrs(struct module *mod)
> +{
> +	if (mod->sect_attrs) {
> +		sysfs_remove_group(&mod->mkobj.kobj,
> +				   &mod->sect_attrs->grp);
> +		/*
> +		 * We are positive that no one is using any sect attrs
> +		 * at this point.  Deallocate immediately.
> +		 */
> +		free_sect_attrs(mod->sect_attrs);
> +		mod->sect_attrs = NULL;
> +	}
> +}
> +
> +/*
> + * /sys/module/foo/notes/.section.name gives contents of SHT_NOTE sections.
> + */
> +
> +struct module_notes_attrs {
> +	struct kobject *dir;
> +	unsigned int notes;
> +	struct bin_attribute attrs[];
> +};
> +
> +static ssize_t module_notes_read(struct file *filp, struct kobject *kobj,
> +				 struct bin_attribute *bin_attr,
> +				 char *buf, loff_t pos, size_t count)
> +{
> +	/*
> +	 * The caller checked the pos and count against our size.
> +	 */
> +	memcpy(buf, bin_attr->private + pos, count);
> +	return count;
> +}
> +
> +static void free_notes_attrs(struct module_notes_attrs *notes_attrs,
> +			     unsigned int i)
> +{
> +	if (notes_attrs->dir) {
> +		while (i-- > 0)
> +			sysfs_remove_bin_file(notes_attrs->dir,
> +					      &notes_attrs->attrs[i]);
> +		kobject_put(notes_attrs->dir);
> +	}
> +	kfree(notes_attrs);
> +}
> +
> +static void add_notes_attrs(struct module *mod, const struct load_info *info)
> +{
> +	unsigned int notes, loaded, i;
> +	struct module_notes_attrs *notes_attrs;
> +	struct bin_attribute *nattr;
> +
> +	/* failed to create section attributes, so can't create notes */
> +	if (!mod->sect_attrs)
> +		return;
> +
> +	/* Count notes sections and allocate structures.  */
> +	notes = 0;
> +	for (i = 0; i < info->hdr->e_shnum; i++)
> +		if (!sect_empty(&info->sechdrs[i]) &&
> +		    info->sechdrs[i].sh_type == SHT_NOTE)
> +			++notes;
> +
> +	if (notes == 0)
> +		return;
> +
> +	notes_attrs = kzalloc(struct_size(notes_attrs, attrs, notes),
> +			      GFP_KERNEL);
> +	if (!notes_attrs)
> +		return;
> +
> +	notes_attrs->notes = notes;
> +	nattr = &notes_attrs->attrs[0];
> +	for (loaded = i = 0; i < info->hdr->e_shnum; ++i) {
> +		if (sect_empty(&info->sechdrs[i]))
> +			continue;
> +		if (info->sechdrs[i].sh_type == SHT_NOTE) {
> +			sysfs_bin_attr_init(nattr);
> +			nattr->attr.name = mod->sect_attrs->attrs[loaded].battr.attr.name;
> +			nattr->attr.mode = 0444;
> +			nattr->size = info->sechdrs[i].sh_size;
> +			nattr->private = (void *)info->sechdrs[i].sh_addr;
> +			nattr->read = module_notes_read;
> +			++nattr;
> +		}
> +		++loaded;
> +	}
> +
> +	notes_attrs->dir = kobject_create_and_add("notes", &mod->mkobj.kobj);
> +	if (!notes_attrs->dir)
> +		goto out;
> +
> +	for (i = 0; i < notes; ++i)
> +		if (sysfs_create_bin_file(notes_attrs->dir,
> +					  &notes_attrs->attrs[i]))
> +			goto out;
> +
> +	mod->notes_attrs = notes_attrs;
> +	return;
> +
> +out:
> +	free_notes_attrs(notes_attrs, i);
> +}
> +
> +static void remove_notes_attrs(struct module *mod)
> +{
> +	if (mod->notes_attrs)
> +		free_notes_attrs(mod->notes_attrs, mod->notes_attrs->notes);
> +}
> +
> +#else /* !CONFIG_KALLSYMS */
> +static inline void add_sect_attrs(struct module *mod, const struct load_info *info) { }
> +static inline void remove_sect_attrs(struct module *mod) { }
> +static inline void add_notes_attrs(struct module *mod, const struct load_info *info) { }
> +static inline void remove_notes_attrs(struct module *mod) { }
> +#endif /* CONFIG_KALLSYMS */
> +
> +static void del_usage_links(struct module *mod)
> +{
> +#ifdef CONFIG_MODULE_UNLOAD
> +	struct module_use *use;
> +
> +	mutex_lock(&module_mutex);
> +	list_for_each_entry(use, &mod->target_list, target_list)
> +		sysfs_remove_link(use->target->holders_dir, mod->name);
> +	mutex_unlock(&module_mutex);
> +#endif
> +}
> +
> +static int add_usage_links(struct module *mod)
> +{
> +	int ret = 0;
> +#ifdef CONFIG_MODULE_UNLOAD
> +	struct module_use *use;
> +
> +	mutex_lock(&module_mutex);
> +	list_for_each_entry(use, &mod->target_list, target_list) {
> +		ret = sysfs_create_link(use->target->holders_dir,
> +					&mod->mkobj.kobj, mod->name);
> +		if (ret)
> +			break;
> +	}
> +	mutex_unlock(&module_mutex);
> +	if (ret)
> +		del_usage_links(mod);
> +#endif
> +	return ret;
> +}
> +
> +static void module_remove_modinfo_attrs(struct module *mod, int end)
> +{
> +	struct module_attribute *attr;
> +	int i;
> +
> +	for (i = 0; (attr = &mod->modinfo_attrs[i]); i++) {
> +		if (end >= 0 && i > end)
> +			break;
> +		/* pick a field to test for end of list */
> +		if (!attr->attr.name)
> +			break;
> +		sysfs_remove_file(&mod->mkobj.kobj, &attr->attr);
> +		if (attr->free)
> +			attr->free(mod);
> +	}
> +	kfree(mod->modinfo_attrs);
> +}
> +
> +static int module_add_modinfo_attrs(struct module *mod)
> +{
> +	struct module_attribute *attr;
> +	struct module_attribute *temp_attr;
> +	int error = 0;
> +	int i;
> +
> +	mod->modinfo_attrs = kzalloc((sizeof(struct module_attribute) *
> +					(modinfo_attrs_count + 1)),
> +					GFP_KERNEL);
> +	if (!mod->modinfo_attrs)
> +		return -ENOMEM;
> +
> +	temp_attr = mod->modinfo_attrs;
> +	for (i = 0; (attr = modinfo_attrs[i]); i++) {
> +		if (!attr->test || attr->test(mod)) {
> +			memcpy(temp_attr, attr, sizeof(*temp_attr));
> +			sysfs_attr_init(&temp_attr->attr);
> +			error = sysfs_create_file(&mod->mkobj.kobj,
> +						  &temp_attr->attr);
> +			if (error)
> +				goto error_out;
> +			++temp_attr;
> +		}
> +	}
> +
> +	return 0;
> +
> +error_out:
> +	if (i > 0)
> +		module_remove_modinfo_attrs(mod, --i);
> +	else
> +		kfree(mod->modinfo_attrs);
> +	return error;
> +}
> +
> +static void mod_kobject_put(struct module *mod)
> +{
> +	DECLARE_COMPLETION_ONSTACK(c);
> +
> +	mod->mkobj.kobj_completion = &c;
> +	kobject_put(&mod->mkobj.kobj);
> +	wait_for_completion(&c);
> +}
> +
> +static int mod_sysfs_init(struct module *mod)
> +{
> +	int err;
> +	struct kobject *kobj;
> +
> +	if (!module_sysfs_initialized) {
> +		pr_err("%s: module sysfs not initialized\n", mod->name);
> +		err = -EINVAL;
> +		goto out;
> +	}
> +
> +	kobj = kset_find_obj(module_kset, mod->name);
> +	if (kobj) {
> +		pr_err("%s: module is already loaded\n", mod->name);
> +		kobject_put(kobj);
> +		err = -EINVAL;
> +		goto out;
> +	}
> +
> +	mod->mkobj.mod = mod;
> +
> +	memset(&mod->mkobj.kobj, 0, sizeof(mod->mkobj.kobj));
> +	mod->mkobj.kobj.kset = module_kset;
> +	err = kobject_init_and_add(&mod->mkobj.kobj, &module_ktype, NULL,
> +				   "%s", mod->name);
> +	if (err)
> +		mod_kobject_put(mod);
> +
> +out:
> +	return err;
> +}
> +
> +int mod_sysfs_setup(struct module *mod,
> +		    const struct load_info *info,
> +			   struct kernel_param *kparam,
> +			   unsigned int num_params)
> +{
> +	int err;
> +
> +	err = mod_sysfs_init(mod);
> +	if (err)
> +		goto out;
> +
> +	mod->holders_dir = kobject_create_and_add("holders", &mod->mkobj.kobj);
> +	if (!mod->holders_dir) {
> +		err = -ENOMEM;
> +		goto out_unreg;
> +	}
> +
> +	err = module_param_sysfs_setup(mod, kparam, num_params);
> +	if (err)
> +		goto out_unreg_holders;
> +
> +	err = module_add_modinfo_attrs(mod);
> +	if (err)
> +		goto out_unreg_param;
> +
> +	err = add_usage_links(mod);
> +	if (err)
> +		goto out_unreg_modinfo_attrs;
> +
> +	add_sect_attrs(mod, info);
> +	add_notes_attrs(mod, info);
> +
> +	return 0;
> +
> +out_unreg_modinfo_attrs:
> +	module_remove_modinfo_attrs(mod, -1);
> +out_unreg_param:
> +	module_param_sysfs_remove(mod);
> +out_unreg_holders:
> +	kobject_put(mod->holders_dir);
> +out_unreg:
> +	mod_kobject_put(mod);
> +out:
> +	return err;
> +}
> +
> +static void mod_sysfs_fini(struct module *mod)
> +{
> +	remove_notes_attrs(mod);
> +	remove_sect_attrs(mod);
> +	mod_kobject_put(mod);
> +}
> +
> +void mod_sysfs_teardown(struct module *mod)
> +{
> +	del_usage_links(mod);
> +	module_remove_modinfo_attrs(mod, -1);
> +	module_param_sysfs_remove(mod);
> +	kobject_put(mod->mkobj.drivers_dir);
> +	kobject_put(mod->holders_dir);
> +	mod_sysfs_fini(mod);
> +}
> +
> +void init_param_lock(struct module *mod)
> +{
> +	mutex_init(&mod->param_lock);
> +}




[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux