list_for_each_entry(i, &modules, list) { struct module_use *use; list_for_each_entry(use, &i->modules_which_use_me, list) { if (use->module_which_uses == mod) { DEBUGP("%s unusing %s\n", mod->name, i->name); module_put(i); list_del(&use->list); kfree(use); /* There can be at most one match. */ break; } } }} #ifdef CONFIG_MODULE_FORCE_UNLOADstatic inline int try_force_unload(unsigned int flags){ int ret = (flags & O_TRUNC); if (ret) add_taint(TAINT_FORCED_RMMOD); return ret;}#elsestatic inline int try_force_unload(unsigned int flags){ return 0;}#endif /* CONFIG_MODULE_FORCE_UNLOAD */ struct stopref{ struct module *mod; int flags; int *forced;}; /* Whole machine is stopped with interrupts off when this runs. */static int __try_stop_module(void *_sref){ struct stopref *sref = _sref; /* If it's not unused, quit unless we are told to block. */ if ((sref->flags & O_NONBLOCK) && module_refcount(sref->mod) != 0) { if (!(*sref->forced = try_force_unload(sref->flags))) return -EWOULDBLOCK; } /* Mark it as dying. */ sref->mod->state = MODULE_STATE_GOING; return 0;} static int try_stop_module(struct module *mod, int flags, int *forced){ struct stopref sref = { mod, flags, forced }; return stop_machine_run(__try_stop_module, &sref, NR_CPUS);} unsigned int module_refcount(struct module *mod){ unsigned int i, total = 0; for (i = 0; i < NR_CPUS; i++) total += local_read(&mod->ref[i].count); return total;}EXPORT_SYMBOL(module_refcount); /* This exists whether we can unload or not */static void free_module(struct module *mod); static void wait_for_zero_refcount(struct module *mod){ /* Since we might sleep for some time, drop the semaphore first */ mutex_unlock(&module_mutex); for (;;) { DEBUGP("Looking at refcount...\n"); set_current_state(TASK_UNINTERRUPTIBLE); if (module_refcount(mod) == 0) break; schedule(); } current->state = TASK_RUNNING; mutex_lock(&module_mutex);} asmlinkage longsys_delete_module(const char __user *name_user, unsigned int flags){ struct module *mod; char name[MODULE_NAME_LEN]; int ret, forced = 0; if (!capable(CAP_SYS_MODULE)) return -EPERM; if (strncpy_from_user(name, name_user, MODULE_NAME_LEN-1) < 0) return -EFAULT; name[MODULE_NAME_LEN-1] = '\0'; if (mutex_lock_interruptible(&module_mutex) != 0) return -EINTR; mod = find_module(name); if (!mod) { ret = -ENOENT; goto out; } if (!list_empty(&mod->modules_which_use_me)) { /* Other modules depend on us: get rid of them first. */ ret = -EWOULDBLOCK; goto out; } /* Doing init or already dying? */ if (mod->state != MODULE_STATE_LIVE) { /* FIXME: if (force), slam module count and wake up waiter --RR */ DEBUGP("%s already dying\n", mod->name); ret = -EBUSY; goto out; } /* If it has an init func, it must have an exit func to unload */ if ((mod->init != NULL && mod->exit == NULL) || mod->unsafe) { forced = try_force_unload(flags); if (!forced) { /* This module can't be removed */ ret = -EBUSY; goto out; } } /* Set this up before setting mod->state */ mod->waiter = current; /* Stop the machine so refcounts can't move and disable module. */ ret = try_stop_module(mod, flags, &forced); if (ret != 0) goto out; /* Never wait if forced. */ if (!forced && module_refcount(mod) != 0) wait_for_zero_refcount(mod); /* Final destruction now noone is using it. */ if (mod->exit != NULL) { mutex_unlock(&module_mutex); mod->exit(); mutex_lock(&module_mutex); } free_module(mod); out: mutex_unlock(&module_mutex); return ret;} static void print_unload_info(struct seq_file *m, struct module *mod){ struct module_use *use; int printed_something = 0; seq_printf(m, " %u ", module_refcount(mod)); /* Always include a trailing , so userspace can differentiate between this and the old multi-field proc format. */ list_for_each_entry(use, &mod->modules_which_use_me, list) { printed_something = 1; seq_printf(m, "%s,", use->module_which_uses->name); } if (mod->unsafe) { printed_something = 1; seq_printf(m, "[unsafe],"); } if (mod->init != NULL && mod->exit == NULL) { printed_something = 1; seq_printf(m, "[permanent],"); } if (!printed_something) seq_printf(m, "-");} void __symbol_put(const char *symbol){ struct module *owner; unsigned long flags; const unsigned long *crc; spin_lock_irqsave(&modlist_lock, flags); if (!__find_symbol(symbol, &owner, &crc, 1)) BUG(); module_put(owner); spin_unlock_irqrestore(&modlist_lock, flags);}EXPORT_SYMBOL(__symbol_put); void symbol_put_addr(void *addr){ struct module *modaddr; if (core_kernel_text((unsigned long)addr)) return; if (!(modaddr = module_text_address((unsigned long)addr))) BUG(); module_put(modaddr);}EXPORT_SYMBOL_GPL(symbol_put_addr); static ssize_t show_refcnt(struct module_attribute *mattr, struct module *mod, char *buffer){ /* sysfs holds a reference */ return sprintf(buffer, "%u\n", module_refcount(mod)-1);} static struct module_attribute refcnt = { .attr = { .name = "refcnt", .mode = 0444, .owner = THIS_MODULE }, .show = show_refcnt,}; #else /* !CONFIG_MODULE_UNLOAD */static void print_unload_info(struct seq_file *m, struct module *mod){ /* We don't know the usage count, or what modules are using. */ seq_printf(m, " - -");} static inline void module_unload_free(struct module *mod){} static inline int use_module(struct module *a, struct module *b){ return strong_try_module_get(b);} static inline void module_unload_init(struct module *mod){}#endif /* CONFIG_MODULE_UNLOAD */ static struct module_attribute *modinfo_attrs[] = { &modinfo_version, &modinfo_srcversion,#ifdef CONFIG_MODULE_UNLOAD &refcnt,#endif NULL,}; static const char vermagic[] = VERMAGIC_STRING; #ifdef CONFIG_MODVERSIONSstatic int check_version(Elf_Shdr *sechdrs, unsigned int versindex, const char *symname, struct module *mod, const unsigned long *crc){ unsigned int i, num_versions; struct modversion_info *versions; /* Exporting module didn't supply crcs? OK, we're already tainted. */ if (!crc) return 1; versions = (void *) sechdrs[versindex].sh_addr; num_versions = sechdrs[versindex].sh_size / sizeof(struct modversion_info); for (i = 0; i < num_versions; i++) { if (strcmp(versions[i].name, symname) != 0) continue; if (versions[i].crc == *crc) return 1; printk("%s: disagrees about version of symbol %s\n", mod->name, symname); DEBUGP("Found checksum %lX vs module %lX\n", *crc, versions[i].crc); return 0; } /* Not in module's version table. OK, but that taints the kernel. */ if (!(tainted & TAINT_FORCED_MODULE)) { printk("%s: no version for \"%s\" found: kernel tainted.\n", mod->name, symname); add_taint(TAINT_FORCED_MODULE); } return 1;} static inline int check_modstruct_version(Elf_Shdr *sechdrs, unsigned int versindex, struct module *mod){ const unsigned long *crc; struct module *owner; if (!__find_symbol("struct_module", &owner, &crc, 1)) BUG(); return check_version(sechdrs, versindex, "struct_module", mod, crc);} /* First part is kernel version, which we ignore. */static inline int same_magic(const char *amagic, const char *bmagic){ amagic += strcspn(amagic, " "); bmagic += strcspn(bmagic, " "); return strcmp(amagic, bmagic) == 0;}#elsestatic inline int check_version(Elf_Shdr *sechdrs, unsigned int versindex, const char *symname, struct module *mod, const unsigned long *crc){ return 1;} static inline int check_modstruct_version(Elf_Shdr *sechdrs, unsigned int versindex, struct module *mod){ return 1;} static inline int same_magic(const char *amagic, const char *bmagic){ return strcmp(amagic, bmagic) == 0;}#endif /* CONFIG_MODVERSIONS */ /* Resolve a symbol for this module. I.e. if we find one, record usage. Must be holding module_mutex. */static unsigned long resolve_symbol(Elf_Shdr *sechdrs, unsigned int versindex, const char *name, struct module *mod){ struct module *owner; unsigned long ret; const unsigned long *crc; ret = __find_symbol(name, &owner, &crc, mod->license_gplok); if (ret) { /* use_module can fail due to OOM, or module unloading */ if (!check_version(sechdrs, versindex, name, mod, crc) || !use_module(mod, owner)) ret = 0; } return ret;} /* * /sys/module/foo/sections stuff * J. Corbet <corbet@lwn.net> */#ifdef CONFIG_KALLSYMSstatic ssize_t module_sect_show(struct module_attribute *mattr, struct module *mod, char *buf){ struct module_sect_attr *sattr = container_of(mattr, struct module_sect_attr, mattr); return sprintf(buf, "0x%lx\n", sattr->address);} static void add_sect_attrs(struct module *mod, unsigned int nsect, char *secstrings, Elf_Shdr *sechdrs){ unsigned int nloaded = 0, i, size[2]; struct module_sect_attrs *sect_attrs; struct module_sect_attr *sattr; struct attribute **gattr; /* Count loaded sections and allocate structures */ for (i = 0; i < nsect; i++) if (sechdrs[i].sh_flags & SHF_ALLOC) nloaded++; size[0] = ALIGN(sizeof(*sect_attrs) + nloaded * sizeof(sect_attrs->attrs[0]), sizeof(sect_attrs->grp.attrs[0])); size[1] = (nloaded + 1) * sizeof(sect_attrs->grp.attrs[0]); if (! (sect_attrs = kmalloc(size[0] + size[1], GFP_KERNEL))) return; /* Setup section attributes. */ sect_attrs->grp.name = "sections"; sect_attrs->grp.attrs = (void *)sect_attrs + size[0]; sattr = §_attrs->attrs[0]; gattr = §_attrs->grp.attrs[0]; for (i = 0; i < nsect; i++) { if (! (sechdrs[i].sh_flags & SHF_ALLOC)) continue; sattr->address = sechdrs[i].sh_addr; strlcpy(sattr->name, secstrings + sechdrs[i].sh_name, MODULE_SECT_NAME_LEN); sattr->mattr.show = module_sect_show; sattr->mattr.store = NULL; sattr->mattr.attr.name = sattr->name; sattr->mattr.attr.owner = mod; sattr->mattr.attr.mode = S_IRUGO; *(gattr++) = &(sattr++)->mattr.attr; } *gattr = NULL; if (sysfs_create_group(&mod->mkobj.kobj, §_attrs->grp)) goto out; mod->sect_attrs = sect_attrs; return; out: kfree(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. */ kfree(mod->sect_attrs); mod->sect_attrs = NULL; }} #elsestatic inline void add_sect_attrs(struct module *mod, unsigned int nsect, char *sectstrings, Elf_Shdr *sechdrs){} static inline void remove_sect_attrs(struct module *mod){}#endif /* CONFIG_KALLSYMS */ 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]) && !error; i++) { if (!attr->test || (attr->test && attr->test(mod))) { memcpy(temp_attr, attr, sizeof(*temp_attr)); temp_attr->attr.owner = mod; error = sysfs_create_file(&mod->mkobj.kobj,&temp_attr->attr); ++temp_attr; } } return error;} static void module_remove_modinfo_attrs(struct module *mod){ struct module_attribute *attr; int i; for (i = 0; (attr = &mod->modinfo_attrs[i]); i++) { /* 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 mod_sysfs_setup(struct module *mod, struct kernel_param *kparam, unsigned int num_params){ int err; Lines 501–1000 of 2126. Previous lines Load more
list_for_each_entry(i, &modules, list) { struct module_use *use; list_for_each_entry(use, &i->modules_which_use_me, list) { if (use->module_which_uses == mod) { DEBUGP("%s unusing %s\n", mod->name, i->name); module_put(i); list_del(&use->list); kfree(use); /* There can be at most one match. */ break; } } } } #ifdef CONFIG_MODULE_FORCE_UNLOAD static inline int try_force_unload(unsigned int flags) { int ret = (flags & O_TRUNC); if (ret) add_taint(TAINT_FORCED_RMMOD); return ret; } #else static inline int try_force_unload(unsigned int flags) { return 0; } #endif /* CONFIG_MODULE_FORCE_UNLOAD */ struct stopref { struct module *mod; int flags; int *forced; }; /* Whole machine is stopped with interrupts off when this runs. */ static int __try_stop_module(void *_sref) { struct stopref *sref = _sref; /* If it's not unused, quit unless we are told to block. */ if ((sref->flags & O_NONBLOCK) && module_refcount(sref->mod) != 0) { if (!(*sref->forced = try_force_unload(sref->flags))) return -EWOULDBLOCK; } /* Mark it as dying. */ sref->mod->state = MODULE_STATE_GOING; return 0; } static int try_stop_module(struct module *mod, int flags, int *forced) { struct stopref sref = { mod, flags, forced }; return stop_machine_run(__try_stop_module, &sref, NR_CPUS); } unsigned int module_refcount(struct module *mod) { unsigned int i, total = 0; for (i = 0; i < NR_CPUS; i++) total += local_read(&mod->ref[i].count); return total; } EXPORT_SYMBOL(module_refcount); /* This exists whether we can unload or not */ static void free_module(struct module *mod); static void wait_for_zero_refcount(struct module *mod) { /* Since we might sleep for some time, drop the semaphore first */ mutex_unlock(&module_mutex); for (;;) { DEBUGP("Looking at refcount...\n"); set_current_state(TASK_UNINTERRUPTIBLE); if (module_refcount(mod) == 0) break; schedule(); } current->state = TASK_RUNNING; mutex_lock(&module_mutex); } asmlinkage long sys_delete_module(const char __user *name_user, unsigned int flags) { struct module *mod; char name[MODULE_NAME_LEN]; int ret, forced = 0; if (!capable(CAP_SYS_MODULE)) return -EPERM; if (strncpy_from_user(name, name_user, MODULE_NAME_LEN-1) < 0) return -EFAULT; name[MODULE_NAME_LEN-1] = '\0'; if (mutex_lock_interruptible(&module_mutex) != 0) return -EINTR; mod = find_module(name); if (!mod) { ret = -ENOENT; goto out; } if (!list_empty(&mod->modules_which_use_me)) { /* Other modules depend on us: get rid of them first. */ ret = -EWOULDBLOCK; goto out; } /* Doing init or already dying? */ if (mod->state != MODULE_STATE_LIVE) { /* FIXME: if (force), slam module count and wake up waiter --RR */ DEBUGP("%s already dying\n", mod->name); ret = -EBUSY; goto out; } /* If it has an init func, it must have an exit func to unload */ if ((mod->init != NULL && mod->exit == NULL) || mod->unsafe) { forced = try_force_unload(flags); if (!forced) { /* This module can't be removed */ ret = -EBUSY; goto out; } } /* Set this up before setting mod->state */ mod->waiter = current; /* Stop the machine so refcounts can't move and disable module. */ ret = try_stop_module(mod, flags, &forced); if (ret != 0) goto out; /* Never wait if forced. */ if (!forced && module_refcount(mod) != 0) wait_for_zero_refcount(mod); /* Final destruction now noone is using it. */ if (mod->exit != NULL) { mutex_unlock(&module_mutex); mod->exit(); mutex_lock(&module_mutex); } free_module(mod); out: mutex_unlock(&module_mutex); return ret; } static void print_unload_info(struct seq_file *m, struct module *mod) { struct module_use *use; int printed_something = 0; seq_printf(m, " %u ", module_refcount(mod)); /* Always include a trailing , so userspace can differentiate between this and the old multi-field proc format. */ list_for_each_entry(use, &mod->modules_which_use_me, list) { printed_something = 1; seq_printf(m, "%s,", use->module_which_uses->name); } if (mod->unsafe) { printed_something = 1; seq_printf(m, "[unsafe],"); } if (mod->init != NULL && mod->exit == NULL) { printed_something = 1; seq_printf(m, "[permanent],"); } if (!printed_something) seq_printf(m, "-"); } void __symbol_put(const char *symbol) { struct module *owner; unsigned long flags; const unsigned long *crc; spin_lock_irqsave(&modlist_lock, flags); if (!__find_symbol(symbol, &owner, &crc, 1)) BUG(); module_put(owner); spin_unlock_irqrestore(&modlist_lock, flags); } EXPORT_SYMBOL(__symbol_put); void symbol_put_addr(void *addr) { struct module *modaddr; if (core_kernel_text((unsigned long)addr)) return; if (!(modaddr = module_text_address((unsigned long)addr))) BUG(); module_put(modaddr); } EXPORT_SYMBOL_GPL(symbol_put_addr); static ssize_t show_refcnt(struct module_attribute *mattr, struct module *mod, char *buffer) { /* sysfs holds a reference */ return sprintf(buffer, "%u\n", module_refcount(mod)-1); } static struct module_attribute refcnt = { .attr = { .name = "refcnt", .mode = 0444, .owner = THIS_MODULE }, .show = show_refcnt, }; #else /* !CONFIG_MODULE_UNLOAD */ static void print_unload_info(struct seq_file *m, struct module *mod) { /* We don't know the usage count, or what modules are using. */ seq_printf(m, " - -"); } static inline void module_unload_free(struct module *mod) { } static inline int use_module(struct module *a, struct module *b) { return strong_try_module_get(b); } static inline void module_unload_init(struct module *mod) { } #endif /* CONFIG_MODULE_UNLOAD */ static struct module_attribute *modinfo_attrs[] = { &modinfo_version, &modinfo_srcversion, #ifdef CONFIG_MODULE_UNLOAD &refcnt, #endif NULL, }; static const char vermagic[] = VERMAGIC_STRING; #ifdef CONFIG_MODVERSIONS static int check_version(Elf_Shdr *sechdrs, unsigned int versindex, const char *symname, struct module *mod, const unsigned long *crc) { unsigned int i, num_versions; struct modversion_info *versions; /* Exporting module didn't supply crcs? OK, we're already tainted. */ if (!crc) return 1; versions = (void *) sechdrs[versindex].sh_addr; num_versions = sechdrs[versindex].sh_size / sizeof(struct modversion_info); for (i = 0; i < num_versions; i++) { if (strcmp(versions[i].name, symname) != 0) continue; if (versions[i].crc == *crc) return 1; printk("%s: disagrees about version of symbol %s\n", mod->name, symname); DEBUGP("Found checksum %lX vs module %lX\n", *crc, versions[i].crc); return 0; } /* Not in module's version table. OK, but that taints the kernel. */ if (!(tainted & TAINT_FORCED_MODULE)) { printk("%s: no version for \"%s\" found: kernel tainted.\n", mod->name, symname); add_taint(TAINT_FORCED_MODULE); } return 1; } static inline int check_modstruct_version(Elf_Shdr *sechdrs, unsigned int versindex, struct module *mod) { const unsigned long *crc; struct module *owner; if (!__find_symbol("struct_module", &owner, &crc, 1)) BUG(); return check_version(sechdrs, versindex, "struct_module", mod, crc); } /* First part is kernel version, which we ignore. */ static inline int same_magic(const char *amagic, const char *bmagic) { amagic += strcspn(amagic, " "); bmagic += strcspn(bmagic, " "); return strcmp(amagic, bmagic) == 0; } #else static inline int check_version(Elf_Shdr *sechdrs, unsigned int versindex, const char *symname, struct module *mod, const unsigned long *crc) { return 1; } static inline int check_modstruct_version(Elf_Shdr *sechdrs, unsigned int versindex, struct module *mod) { return 1; } static inline int same_magic(const char *amagic, const char *bmagic) { return strcmp(amagic, bmagic) == 0; } #endif /* CONFIG_MODVERSIONS */ /* Resolve a symbol for this module. I.e. if we find one, record usage. Must be holding module_mutex. */ static unsigned long resolve_symbol(Elf_Shdr *sechdrs, unsigned int versindex, const char *name, struct module *mod) { struct module *owner; unsigned long ret; const unsigned long *crc; ret = __find_symbol(name, &owner, &crc, mod->license_gplok); if (ret) { /* use_module can fail due to OOM, or module unloading */ if (!check_version(sechdrs, versindex, name, mod, crc) || !use_module(mod, owner)) ret = 0; } return ret; } /* * /sys/module/foo/sections stuff * J. Corbet <corbet@lwn.net> */ #ifdef CONFIG_KALLSYMS static ssize_t module_sect_show(struct module_attribute *mattr, struct module *mod, char *buf) { struct module_sect_attr *sattr = container_of(mattr, struct module_sect_attr, mattr); return sprintf(buf, "0x%lx\n", sattr->address); } static void add_sect_attrs(struct module *mod, unsigned int nsect, char *secstrings, Elf_Shdr *sechdrs) { unsigned int nloaded = 0, i, size[2]; struct module_sect_attrs *sect_attrs; struct module_sect_attr *sattr; struct attribute **gattr; /* Count loaded sections and allocate structures */ for (i = 0; i < nsect; i++) if (sechdrs[i].sh_flags & SHF_ALLOC) nloaded++; size[0] = ALIGN(sizeof(*sect_attrs) + nloaded * sizeof(sect_attrs->attrs[0]), sizeof(sect_attrs->grp.attrs[0])); size[1] = (nloaded + 1) * sizeof(sect_attrs->grp.attrs[0]); if (! (sect_attrs = kmalloc(size[0] + size[1], GFP_KERNEL))) return; /* Setup section attributes. */ sect_attrs->grp.name = "sections"; sect_attrs->grp.attrs = (void *)sect_attrs + size[0]; sattr = §_attrs->attrs[0]; gattr = §_attrs->grp.attrs[0]; for (i = 0; i < nsect; i++) { if (! (sechdrs[i].sh_flags & SHF_ALLOC)) continue; sattr->address = sechdrs[i].sh_addr; strlcpy(sattr->name, secstrings + sechdrs[i].sh_name, MODULE_SECT_NAME_LEN); sattr->mattr.show = module_sect_show; sattr->mattr.store = NULL; sattr->mattr.attr.name = sattr->name; sattr->mattr.attr.owner = mod; sattr->mattr.attr.mode = S_IRUGO; *(gattr++) = &(sattr++)->mattr.attr; } *gattr = NULL; if (sysfs_create_group(&mod->mkobj.kobj, §_attrs->grp)) goto out; mod->sect_attrs = sect_attrs; return; out: kfree(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. */ kfree(mod->sect_attrs); mod->sect_attrs = NULL; } } #else static inline void add_sect_attrs(struct module *mod, unsigned int nsect, char *sectstrings, Elf_Shdr *sechdrs) { } static inline void remove_sect_attrs(struct module *mod) { } #endif /* CONFIG_KALLSYMS */ 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]) && !error; i++) { if (!attr->test || (attr->test && attr->test(mod))) { memcpy(temp_attr, attr, sizeof(*temp_attr)); temp_attr->attr.owner = mod; error = sysfs_create_file(&mod->mkobj.kobj,&temp_attr->attr); ++temp_attr; } } return error; } static void module_remove_modinfo_attrs(struct module *mod) { struct module_attribute *attr; int i; for (i = 0; (attr = &mod->modinfo_attrs[i]); i++) { /* 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 mod_sysfs_setup(struct module *mod, struct kernel_param *kparam, unsigned int num_params) { int err;