diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/cpu.c | 6 | ||||
-rw-r--r-- | kernel/module.c | 59 | ||||
-rw-r--r-- | kernel/stop_machine.c | 55 |
3 files changed, 94 insertions, 26 deletions
diff --git a/kernel/cpu.c b/kernel/cpu.c index 47fff3b63cbf..30e74dd6d01b 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c | |||
@@ -269,8 +269,11 @@ out_release: | |||
269 | 269 | ||
270 | int __ref cpu_down(unsigned int cpu) | 270 | int __ref cpu_down(unsigned int cpu) |
271 | { | 271 | { |
272 | int err = 0; | 272 | int err; |
273 | 273 | ||
274 | err = stop_machine_create(); | ||
275 | if (err) | ||
276 | return err; | ||
274 | cpu_maps_update_begin(); | 277 | cpu_maps_update_begin(); |
275 | 278 | ||
276 | if (cpu_hotplug_disabled) { | 279 | if (cpu_hotplug_disabled) { |
@@ -297,6 +300,7 @@ int __ref cpu_down(unsigned int cpu) | |||
297 | 300 | ||
298 | out: | 301 | out: |
299 | cpu_maps_update_done(); | 302 | cpu_maps_update_done(); |
303 | stop_machine_destroy(); | ||
300 | return err; | 304 | return err; |
301 | } | 305 | } |
302 | EXPORT_SYMBOL(cpu_down); | 306 | EXPORT_SYMBOL(cpu_down); |
diff --git a/kernel/module.c b/kernel/module.c index dd2a54155b54..f47cce910f25 100644 --- a/kernel/module.c +++ b/kernel/module.c | |||
@@ -757,8 +757,16 @@ sys_delete_module(const char __user *name_user, unsigned int flags) | |||
757 | return -EFAULT; | 757 | return -EFAULT; |
758 | name[MODULE_NAME_LEN-1] = '\0'; | 758 | name[MODULE_NAME_LEN-1] = '\0'; |
759 | 759 | ||
760 | if (mutex_lock_interruptible(&module_mutex) != 0) | 760 | /* Create stop_machine threads since free_module relies on |
761 | return -EINTR; | 761 | * a non-failing stop_machine call. */ |
762 | ret = stop_machine_create(); | ||
763 | if (ret) | ||
764 | return ret; | ||
765 | |||
766 | if (mutex_lock_interruptible(&module_mutex) != 0) { | ||
767 | ret = -EINTR; | ||
768 | goto out_stop; | ||
769 | } | ||
762 | 770 | ||
763 | mod = find_module(name); | 771 | mod = find_module(name); |
764 | if (!mod) { | 772 | if (!mod) { |
@@ -817,10 +825,12 @@ sys_delete_module(const char __user *name_user, unsigned int flags) | |||
817 | 825 | ||
818 | out: | 826 | out: |
819 | mutex_unlock(&module_mutex); | 827 | mutex_unlock(&module_mutex); |
828 | out_stop: | ||
829 | stop_machine_destroy(); | ||
820 | return ret; | 830 | return ret; |
821 | } | 831 | } |
822 | 832 | ||
823 | static void print_unload_info(struct seq_file *m, struct module *mod) | 833 | static inline void print_unload_info(struct seq_file *m, struct module *mod) |
824 | { | 834 | { |
825 | struct module_use *use; | 835 | struct module_use *use; |
826 | int printed_something = 0; | 836 | int printed_something = 0; |
@@ -893,7 +903,7 @@ void module_put(struct module *module) | |||
893 | EXPORT_SYMBOL(module_put); | 903 | EXPORT_SYMBOL(module_put); |
894 | 904 | ||
895 | #else /* !CONFIG_MODULE_UNLOAD */ | 905 | #else /* !CONFIG_MODULE_UNLOAD */ |
896 | static void print_unload_info(struct seq_file *m, struct module *mod) | 906 | static inline void print_unload_info(struct seq_file *m, struct module *mod) |
897 | { | 907 | { |
898 | /* We don't know the usage count, or what modules are using. */ | 908 | /* We don't know the usage count, or what modules are using. */ |
899 | seq_printf(m, " - -"); | 909 | seq_printf(m, " - -"); |
@@ -1578,11 +1588,21 @@ static int simplify_symbols(Elf_Shdr *sechdrs, | |||
1578 | return ret; | 1588 | return ret; |
1579 | } | 1589 | } |
1580 | 1590 | ||
1591 | /* Additional bytes needed by arch in front of individual sections */ | ||
1592 | unsigned int __weak arch_mod_section_prepend(struct module *mod, | ||
1593 | unsigned int section) | ||
1594 | { | ||
1595 | /* default implementation just returns zero */ | ||
1596 | return 0; | ||
1597 | } | ||
1598 | |||
1581 | /* Update size with this section: return offset. */ | 1599 | /* Update size with this section: return offset. */ |
1582 | static long get_offset(unsigned int *size, Elf_Shdr *sechdr) | 1600 | static long get_offset(struct module *mod, unsigned int *size, |
1601 | Elf_Shdr *sechdr, unsigned int section) | ||
1583 | { | 1602 | { |
1584 | long ret; | 1603 | long ret; |
1585 | 1604 | ||
1605 | *size += arch_mod_section_prepend(mod, section); | ||
1586 | ret = ALIGN(*size, sechdr->sh_addralign ?: 1); | 1606 | ret = ALIGN(*size, sechdr->sh_addralign ?: 1); |
1587 | *size = ret + sechdr->sh_size; | 1607 | *size = ret + sechdr->sh_size; |
1588 | return ret; | 1608 | return ret; |
@@ -1622,7 +1642,7 @@ static void layout_sections(struct module *mod, | |||
1622 | || strncmp(secstrings + s->sh_name, | 1642 | || strncmp(secstrings + s->sh_name, |
1623 | ".init", 5) == 0) | 1643 | ".init", 5) == 0) |
1624 | continue; | 1644 | continue; |
1625 | s->sh_entsize = get_offset(&mod->core_size, s); | 1645 | s->sh_entsize = get_offset(mod, &mod->core_size, s, i); |
1626 | DEBUGP("\t%s\n", secstrings + s->sh_name); | 1646 | DEBUGP("\t%s\n", secstrings + s->sh_name); |
1627 | } | 1647 | } |
1628 | if (m == 0) | 1648 | if (m == 0) |
@@ -1640,7 +1660,7 @@ static void layout_sections(struct module *mod, | |||
1640 | || strncmp(secstrings + s->sh_name, | 1660 | || strncmp(secstrings + s->sh_name, |
1641 | ".init", 5) != 0) | 1661 | ".init", 5) != 0) |
1642 | continue; | 1662 | continue; |
1643 | s->sh_entsize = (get_offset(&mod->init_size, s) | 1663 | s->sh_entsize = (get_offset(mod, &mod->init_size, s, i) |
1644 | | INIT_OFFSET_MASK); | 1664 | | INIT_OFFSET_MASK); |
1645 | DEBUGP("\t%s\n", secstrings + s->sh_name); | 1665 | DEBUGP("\t%s\n", secstrings + s->sh_name); |
1646 | } | 1666 | } |
@@ -1725,15 +1745,15 @@ static const struct kernel_symbol *lookup_symbol(const char *name, | |||
1725 | return NULL; | 1745 | return NULL; |
1726 | } | 1746 | } |
1727 | 1747 | ||
1728 | static int is_exported(const char *name, const struct module *mod) | 1748 | static int is_exported(const char *name, unsigned long value, |
1749 | const struct module *mod) | ||
1729 | { | 1750 | { |
1730 | if (!mod && lookup_symbol(name, __start___ksymtab, __stop___ksymtab)) | 1751 | const struct kernel_symbol *ks; |
1731 | return 1; | 1752 | if (!mod) |
1753 | ks = lookup_symbol(name, __start___ksymtab, __stop___ksymtab); | ||
1732 | else | 1754 | else |
1733 | if (mod && lookup_symbol(name, mod->syms, mod->syms + mod->num_syms)) | 1755 | ks = lookup_symbol(name, mod->syms, mod->syms + mod->num_syms); |
1734 | return 1; | 1756 | return ks != NULL && ks->value == value; |
1735 | else | ||
1736 | return 0; | ||
1737 | } | 1757 | } |
1738 | 1758 | ||
1739 | /* As per nm */ | 1759 | /* As per nm */ |
@@ -1865,6 +1885,13 @@ static noinline struct module *load_module(void __user *umod, | |||
1865 | /* vmalloc barfs on "unusual" numbers. Check here */ | 1885 | /* vmalloc barfs on "unusual" numbers. Check here */ |
1866 | if (len > 64 * 1024 * 1024 || (hdr = vmalloc(len)) == NULL) | 1886 | if (len > 64 * 1024 * 1024 || (hdr = vmalloc(len)) == NULL) |
1867 | return ERR_PTR(-ENOMEM); | 1887 | return ERR_PTR(-ENOMEM); |
1888 | |||
1889 | /* Create stop_machine threads since the error path relies on | ||
1890 | * a non-failing stop_machine call. */ | ||
1891 | err = stop_machine_create(); | ||
1892 | if (err) | ||
1893 | goto free_hdr; | ||
1894 | |||
1868 | if (copy_from_user(hdr, umod, len) != 0) { | 1895 | if (copy_from_user(hdr, umod, len) != 0) { |
1869 | err = -EFAULT; | 1896 | err = -EFAULT; |
1870 | goto free_hdr; | 1897 | goto free_hdr; |
@@ -2248,6 +2275,7 @@ static noinline struct module *load_module(void __user *umod, | |||
2248 | /* Get rid of temporary copy */ | 2275 | /* Get rid of temporary copy */ |
2249 | vfree(hdr); | 2276 | vfree(hdr); |
2250 | 2277 | ||
2278 | stop_machine_destroy(); | ||
2251 | /* Done! */ | 2279 | /* Done! */ |
2252 | return mod; | 2280 | return mod; |
2253 | 2281 | ||
@@ -2270,6 +2298,7 @@ static noinline struct module *load_module(void __user *umod, | |||
2270 | kfree(args); | 2298 | kfree(args); |
2271 | free_hdr: | 2299 | free_hdr: |
2272 | vfree(hdr); | 2300 | vfree(hdr); |
2301 | stop_machine_destroy(); | ||
2273 | return ERR_PTR(err); | 2302 | return ERR_PTR(err); |
2274 | 2303 | ||
2275 | truncated: | 2304 | truncated: |
@@ -2504,7 +2533,7 @@ int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type, | |||
2504 | strlcpy(name, mod->strtab + mod->symtab[symnum].st_name, | 2533 | strlcpy(name, mod->strtab + mod->symtab[symnum].st_name, |
2505 | KSYM_NAME_LEN); | 2534 | KSYM_NAME_LEN); |
2506 | strlcpy(module_name, mod->name, MODULE_NAME_LEN); | 2535 | strlcpy(module_name, mod->name, MODULE_NAME_LEN); |
2507 | *exported = is_exported(name, mod); | 2536 | *exported = is_exported(name, *value, mod); |
2508 | preempt_enable(); | 2537 | preempt_enable(); |
2509 | return 0; | 2538 | return 0; |
2510 | } | 2539 | } |
diff --git a/kernel/stop_machine.c b/kernel/stop_machine.c index 286c41722e8c..0cd415ee62a2 100644 --- a/kernel/stop_machine.c +++ b/kernel/stop_machine.c | |||
@@ -38,7 +38,10 @@ struct stop_machine_data { | |||
38 | static unsigned int num_threads; | 38 | static unsigned int num_threads; |
39 | static atomic_t thread_ack; | 39 | static atomic_t thread_ack; |
40 | static DEFINE_MUTEX(lock); | 40 | static DEFINE_MUTEX(lock); |
41 | 41 | /* setup_lock protects refcount, stop_machine_wq and stop_machine_work. */ | |
42 | static DEFINE_MUTEX(setup_lock); | ||
43 | /* Users of stop_machine. */ | ||
44 | static int refcount; | ||
42 | static struct workqueue_struct *stop_machine_wq; | 45 | static struct workqueue_struct *stop_machine_wq; |
43 | static struct stop_machine_data active, idle; | 46 | static struct stop_machine_data active, idle; |
44 | static const cpumask_t *active_cpus; | 47 | static const cpumask_t *active_cpus; |
@@ -109,6 +112,43 @@ static int chill(void *unused) | |||
109 | return 0; | 112 | return 0; |
110 | } | 113 | } |
111 | 114 | ||
115 | int stop_machine_create(void) | ||
116 | { | ||
117 | mutex_lock(&setup_lock); | ||
118 | if (refcount) | ||
119 | goto done; | ||
120 | stop_machine_wq = create_rt_workqueue("kstop"); | ||
121 | if (!stop_machine_wq) | ||
122 | goto err_out; | ||
123 | stop_machine_work = alloc_percpu(struct work_struct); | ||
124 | if (!stop_machine_work) | ||
125 | goto err_out; | ||
126 | done: | ||
127 | refcount++; | ||
128 | mutex_unlock(&setup_lock); | ||
129 | return 0; | ||
130 | |||
131 | err_out: | ||
132 | if (stop_machine_wq) | ||
133 | destroy_workqueue(stop_machine_wq); | ||
134 | mutex_unlock(&setup_lock); | ||
135 | return -ENOMEM; | ||
136 | } | ||
137 | EXPORT_SYMBOL_GPL(stop_machine_create); | ||
138 | |||
139 | void stop_machine_destroy(void) | ||
140 | { | ||
141 | mutex_lock(&setup_lock); | ||
142 | refcount--; | ||
143 | if (refcount) | ||
144 | goto done; | ||
145 | destroy_workqueue(stop_machine_wq); | ||
146 | free_percpu(stop_machine_work); | ||
147 | done: | ||
148 | mutex_unlock(&setup_lock); | ||
149 | } | ||
150 | EXPORT_SYMBOL_GPL(stop_machine_destroy); | ||
151 | |||
112 | int __stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus) | 152 | int __stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus) |
113 | { | 153 | { |
114 | struct work_struct *sm_work; | 154 | struct work_struct *sm_work; |
@@ -146,19 +186,14 @@ int stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus) | |||
146 | { | 186 | { |
147 | int ret; | 187 | int ret; |
148 | 188 | ||
189 | ret = stop_machine_create(); | ||
190 | if (ret) | ||
191 | return ret; | ||
149 | /* No CPUs can come up or down during this. */ | 192 | /* No CPUs can come up or down during this. */ |
150 | get_online_cpus(); | 193 | get_online_cpus(); |
151 | ret = __stop_machine(fn, data, cpus); | 194 | ret = __stop_machine(fn, data, cpus); |
152 | put_online_cpus(); | 195 | put_online_cpus(); |
153 | 196 | stop_machine_destroy(); | |
154 | return ret; | 197 | return ret; |
155 | } | 198 | } |
156 | EXPORT_SYMBOL_GPL(stop_machine); | 199 | EXPORT_SYMBOL_GPL(stop_machine); |
157 | |||
158 | static int __init stop_machine_init(void) | ||
159 | { | ||
160 | stop_machine_wq = create_rt_workqueue("kstop"); | ||
161 | stop_machine_work = alloc_percpu(struct work_struct); | ||
162 | return 0; | ||
163 | } | ||
164 | core_initcall(stop_machine_init); | ||