diff options
Diffstat (limited to 'kernel/kmod.c')
| -rw-r--r-- | kernel/kmod.c | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/kernel/kmod.c b/kernel/kmod.c index 796276141e51..9f923f8ce6a0 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c | |||
| @@ -36,6 +36,8 @@ | |||
| 36 | #include <linux/resource.h> | 36 | #include <linux/resource.h> |
| 37 | #include <asm/uaccess.h> | 37 | #include <asm/uaccess.h> |
| 38 | 38 | ||
| 39 | extern int delete_module(const char *name, unsigned int flags); | ||
| 40 | |||
| 39 | extern int max_threads; | 41 | extern int max_threads; |
| 40 | 42 | ||
| 41 | static struct workqueue_struct *khelper_wq; | 43 | static struct workqueue_struct *khelper_wq; |
| @@ -46,6 +48,7 @@ static struct workqueue_struct *khelper_wq; | |||
| 46 | modprobe_path is set via /proc/sys. | 48 | modprobe_path is set via /proc/sys. |
| 47 | */ | 49 | */ |
| 48 | char modprobe_path[KMOD_PATH_LEN] = "/sbin/modprobe"; | 50 | char modprobe_path[KMOD_PATH_LEN] = "/sbin/modprobe"; |
| 51 | struct module_kobject kmod_mk; | ||
| 49 | 52 | ||
| 50 | /** | 53 | /** |
| 51 | * request_module - try to load a kernel module | 54 | * request_module - try to load a kernel module |
| @@ -75,6 +78,11 @@ int request_module(const char *fmt, ...) | |||
| 75 | static atomic_t kmod_concurrent = ATOMIC_INIT(0); | 78 | static atomic_t kmod_concurrent = ATOMIC_INIT(0); |
| 76 | #define MAX_KMOD_CONCURRENT 50 /* Completely arbitrary value - KAO */ | 79 | #define MAX_KMOD_CONCURRENT 50 /* Completely arbitrary value - KAO */ |
| 77 | static int kmod_loop_msg; | 80 | static int kmod_loop_msg; |
| 81 | char modalias[16 + MODULE_NAME_LEN] = "MODALIAS="; | ||
| 82 | char *uevent_envp[2] = { | ||
| 83 | modalias, | ||
| 84 | NULL | ||
| 85 | }; | ||
| 78 | 86 | ||
| 79 | va_start(args, fmt); | 87 | va_start(args, fmt); |
| 80 | ret = vsnprintf(module_name, MODULE_NAME_LEN, fmt, args); | 88 | ret = vsnprintf(module_name, MODULE_NAME_LEN, fmt, args); |
| @@ -82,6 +90,12 @@ int request_module(const char *fmt, ...) | |||
| 82 | if (ret >= MODULE_NAME_LEN) | 90 | if (ret >= MODULE_NAME_LEN) |
| 83 | return -ENAMETOOLONG; | 91 | return -ENAMETOOLONG; |
| 84 | 92 | ||
| 93 | strcpy(&modalias[strlen("MODALIAS=")], module_name); | ||
| 94 | kobject_uevent_env(&kmod_mk.kobj, KOBJ_CHANGE, uevent_envp); | ||
| 95 | |||
| 96 | if (modprobe_path[0] == '\0') | ||
| 97 | goto out; | ||
| 98 | |||
| 85 | /* If modprobe needs a service that is in a module, we get a recursive | 99 | /* If modprobe needs a service that is in a module, we get a recursive |
| 86 | * loop. Limit the number of running kmod threads to max_threads/2 or | 100 | * loop. Limit the number of running kmod threads to max_threads/2 or |
| 87 | * MAX_KMOD_CONCURRENT, whichever is the smaller. A cleaner method | 101 | * MAX_KMOD_CONCURRENT, whichever is the smaller. A cleaner method |
| @@ -108,9 +122,115 @@ int request_module(const char *fmt, ...) | |||
| 108 | 122 | ||
| 109 | ret = call_usermodehelper(modprobe_path, argv, envp, 1); | 123 | ret = call_usermodehelper(modprobe_path, argv, envp, 1); |
| 110 | atomic_dec(&kmod_concurrent); | 124 | atomic_dec(&kmod_concurrent); |
| 125 | out: | ||
| 111 | return ret; | 126 | return ret; |
| 112 | } | 127 | } |
| 113 | EXPORT_SYMBOL(request_module); | 128 | EXPORT_SYMBOL(request_module); |
| 129 | |||
| 130 | static ssize_t store_mod_request(struct module_attribute *mattr, | ||
| 131 | struct module *mod, | ||
| 132 | const char *buffer, size_t count) | ||
| 133 | { | ||
| 134 | char name[MODULE_NAME_LEN]; | ||
| 135 | int ret; | ||
| 136 | |||
| 137 | if (count < 1 || count+1 > MODULE_NAME_LEN) | ||
| 138 | return -EINVAL; | ||
| 139 | memcpy(name, buffer, count); | ||
| 140 | name[count] = '\0'; | ||
| 141 | if (name[count-1] == '\n') | ||
| 142 | name[count-1] = '\0'; | ||
| 143 | |||
| 144 | ret = request_module(name); | ||
| 145 | if (ret < 0) | ||
| 146 | return ret; | ||
| 147 | return count; | ||
| 148 | } | ||
| 149 | |||
| 150 | static struct module_attribute mod_request = { | ||
| 151 | .attr = { .name = "mod_request", .mode = S_IWUSR, .owner = THIS_MODULE }, | ||
| 152 | .store = store_mod_request, | ||
| 153 | }; | ||
| 154 | |||
| 155 | #ifdef CONFIG_MODULE_UNLOAD | ||
| 156 | static ssize_t store_mod_unload(struct module_attribute *mattr, | ||
| 157 | struct module *mod, | ||
| 158 | const char *buffer, size_t count) | ||
| 159 | { | ||
| 160 | char name[MODULE_NAME_LEN]; | ||
| 161 | int ret; | ||
| 162 | |||
| 163 | if (count < 1 || count+1 > MODULE_NAME_LEN) | ||
| 164 | return -EINVAL; | ||
| 165 | memcpy(name, buffer, count); | ||
| 166 | name[count] = '\0'; | ||
| 167 | if (name[count-1] == '\n') | ||
| 168 | name[count-1] = '\0'; | ||
| 169 | |||
| 170 | ret = delete_module(name, O_NONBLOCK); | ||
| 171 | if (ret < 0) | ||
| 172 | return ret; | ||
| 173 | return count; | ||
| 174 | } | ||
| 175 | |||
| 176 | static struct module_attribute mod_unload = { | ||
| 177 | .attr = { .name = "mod_unload", .mode = S_IWUSR, .owner = THIS_MODULE }, | ||
| 178 | .store = store_mod_unload, | ||
| 179 | }; | ||
| 180 | #endif | ||
| 181 | |||
| 182 | static ssize_t show_mod_request_helper(struct module_attribute *mattr, | ||
| 183 | struct module *mod, | ||
| 184 | char *buffer) | ||
| 185 | { | ||
| 186 | return sprintf(buffer, "%s\n", modprobe_path); | ||
| 187 | } | ||
| 188 | |||
| 189 | static ssize_t store_mod_request_helper(struct module_attribute *mattr, | ||
| 190 | struct module *mod, | ||
| 191 | const char *buffer, size_t count) | ||
| 192 | { | ||
| 193 | if (count < 1 || count+1 > KMOD_PATH_LEN) | ||
| 194 | return -EINVAL; | ||
| 195 | memcpy(modprobe_path, buffer, count); | ||
| 196 | modprobe_path[count] = '\0'; | ||
| 197 | if (modprobe_path[count-1] == '\n') | ||
| 198 | modprobe_path[count-1] = '\0'; | ||
| 199 | return count; | ||
| 200 | } | ||
| 201 | |||
| 202 | static struct module_attribute mod_request_helper = { | ||
| 203 | .attr = { | ||
| 204 | .name = "mod_request_helper", | ||
| 205 | .mode = S_IWUSR | S_IRUGO, | ||
| 206 | .owner = THIS_MODULE | ||
| 207 | }, | ||
| 208 | .show = show_mod_request_helper, | ||
| 209 | .store = store_mod_request_helper, | ||
| 210 | }; | ||
| 211 | |||
| 212 | void __init kmod_sysfs_init(void) | ||
| 213 | { | ||
| 214 | int ret; | ||
| 215 | |||
| 216 | kmod_mk.mod = THIS_MODULE; | ||
| 217 | kobj_set_kset_s(&kmod_mk, module_subsys); | ||
| 218 | kobject_set_name(&kmod_mk.kobj, "kmod"); | ||
| 219 | kobject_init(&kmod_mk.kobj); | ||
| 220 | ret = kobject_add(&kmod_mk.kobj); | ||
| 221 | if (ret < 0) | ||
| 222 | goto out; | ||
| 223 | |||
| 224 | ret = sysfs_create_file(&kmod_mk.kobj, &mod_request_helper.attr); | ||
| 225 | ret = sysfs_create_file(&kmod_mk.kobj, &mod_request.attr); | ||
| 226 | #ifdef CONFIG_MODULE_UNLOAD | ||
| 227 | ret = sysfs_create_file(&kmod_mk.kobj, &mod_unload.attr); | ||
| 228 | #endif | ||
| 229 | |||
| 230 | kobject_uevent(&kmod_mk.kobj, KOBJ_ADD); | ||
| 231 | out: | ||
| 232 | return; | ||
| 233 | } | ||
| 114 | #endif /* CONFIG_KMOD */ | 234 | #endif /* CONFIG_KMOD */ |
| 115 | 235 | ||
| 116 | struct subprocess_info { | 236 | struct subprocess_info { |
