[PATCH 02/10] Driver core: let request_module() send a /sys/modules/kmod/-uevent

2007-02-16 Thread Greg Kroah-Hartman
From: Kay Sievers <[EMAIL PROTECTED]>

On recent systems, calls to /sbin/modprobe are handled by udev depending
on the kind of device the kernel has discovered. This patch creates an
uevent for the kernels internal request_module(), to let udev take control
over the request, instead of forking the binary directly by the kernel.
The direct execution of /sbin/modprobe can be disabled by setting:
  /sys/module/kmod/mod_request_helper (/proc/sys/kernel/modprobe)
to an empty string, the same way /proc/sys/kernel/hotplug is disabled on an
udev system.

Signed-off-by: Kay Sievers <[EMAIL PROTECTED]>
Signed-off-by: Greg Kroah-Hartman <[EMAIL PROTECTED]>
---
 include/linux/kmod.h |2 +
 kernel/kmod.c|  120 ++
 kernel/module.c  |   26 +++
 kernel/params.c  |1 +
 4 files changed, 139 insertions(+), 10 deletions(-)

diff --git a/include/linux/kmod.h b/include/linux/kmod.h
index 10f505c..cc8e674 100644
--- a/include/linux/kmod.h
+++ b/include/linux/kmod.h
@@ -28,8 +28,10 @@
 #ifdef CONFIG_KMOD
 /* modprobe exit status on success, -ve on error.  Return value
  * usually useless though. */
+extern void kmod_sysfs_init(void);
 extern int request_module(const char * name, ...) __attribute__ ((format 
(printf, 1, 2)));
 #else
+static inline void kmod_sysfs_init(void) {};
 static inline int request_module(const char * name, ...) { return -ENOSYS; }
 #endif
 
diff --git a/kernel/kmod.c b/kernel/kmod.c
index 7962761..9f923f8 100644
--- a/kernel/kmod.c
+++ b/kernel/kmod.c
@@ -36,6 +36,8 @@
 #include 
 #include 
 
+extern int delete_module(const char *name, unsigned int flags);
+
 extern int max_threads;
 
 static struct workqueue_struct *khelper_wq;
@@ -46,6 +48,7 @@ static struct workqueue_struct *khelper_wq;
modprobe_path is set via /proc/sys.
 */
 char modprobe_path[KMOD_PATH_LEN] = "/sbin/modprobe";
+struct module_kobject kmod_mk;
 
 /**
  * request_module - try to load a kernel module
@@ -75,6 +78,11 @@ int request_module(const char *fmt, ...)
static atomic_t kmod_concurrent = ATOMIC_INIT(0);
 #define MAX_KMOD_CONCURRENT 50 /* Completely arbitrary value - KAO */
static int kmod_loop_msg;
+   char modalias[16 + MODULE_NAME_LEN] = "MODALIAS=";
+   char *uevent_envp[2] = {
+   modalias,
+   NULL
+   };
 
va_start(args, fmt);
ret = vsnprintf(module_name, MODULE_NAME_LEN, fmt, args);
@@ -82,6 +90,12 @@ int request_module(const char *fmt, ...)
if (ret >= MODULE_NAME_LEN)
return -ENAMETOOLONG;
 
+   strcpy([strlen("MODALIAS=")], module_name);
+   kobject_uevent_env(_mk.kobj, KOBJ_CHANGE, uevent_envp);
+
+   if (modprobe_path[0] == '\0')
+   goto out;
+
/* If modprobe needs a service that is in a module, we get a recursive
 * loop.  Limit the number of running kmod threads to max_threads/2 or
 * MAX_KMOD_CONCURRENT, whichever is the smaller.  A cleaner method
@@ -108,9 +122,115 @@ int request_module(const char *fmt, ...)
 
ret = call_usermodehelper(modprobe_path, argv, envp, 1);
atomic_dec(_concurrent);
+out:
return ret;
 }
 EXPORT_SYMBOL(request_module);
+
+static ssize_t store_mod_request(struct module_attribute *mattr,
+struct module *mod,
+ const char *buffer, size_t count)
+{
+   char name[MODULE_NAME_LEN];
+   int ret;
+
+   if (count < 1 || count+1 > MODULE_NAME_LEN)
+   return -EINVAL;
+   memcpy(name, buffer, count);
+   name[count] = '\0';
+   if (name[count-1] == '\n')
+   name[count-1] = '\0';
+
+   ret = request_module(name);
+   if (ret < 0)
+   return ret;
+   return count;
+}
+
+static struct module_attribute mod_request = {
+   .attr = { .name = "mod_request", .mode = S_IWUSR, .owner = THIS_MODULE 
},
+   .store = store_mod_request,
+};
+
+#ifdef CONFIG_MODULE_UNLOAD
+static ssize_t store_mod_unload(struct module_attribute *mattr,
+   struct module *mod,
+   const char *buffer, size_t count)
+{
+   char name[MODULE_NAME_LEN];
+   int ret;
+
+   if (count < 1 || count+1 > MODULE_NAME_LEN)
+   return -EINVAL;
+   memcpy(name, buffer, count);
+   name[count] = '\0';
+   if (name[count-1] == '\n')
+   name[count-1] = '\0';
+
+   ret = delete_module(name, O_NONBLOCK);
+   if (ret < 0)
+   return ret;
+   return count;
+}
+
+static struct module_attribute mod_unload = {
+   .attr = { .name = "mod_unload", .mode = S_IWUSR, .owner = THIS_MODULE },
+   .store = store_mod_unload,
+};
+#endif
+
+static ssize_t show_mod_request_helper(struct module_attribute *mattr,
+  struct module *mod,
+  char *buffer)
+{
+   return sprintf(buffer, 

[PATCH 02/10] Driver core: let request_module() send a /sys/modules/kmod/-uevent

2007-02-16 Thread Greg Kroah-Hartman
From: Kay Sievers [EMAIL PROTECTED]

On recent systems, calls to /sbin/modprobe are handled by udev depending
on the kind of device the kernel has discovered. This patch creates an
uevent for the kernels internal request_module(), to let udev take control
over the request, instead of forking the binary directly by the kernel.
The direct execution of /sbin/modprobe can be disabled by setting:
  /sys/module/kmod/mod_request_helper (/proc/sys/kernel/modprobe)
to an empty string, the same way /proc/sys/kernel/hotplug is disabled on an
udev system.

Signed-off-by: Kay Sievers [EMAIL PROTECTED]
Signed-off-by: Greg Kroah-Hartman [EMAIL PROTECTED]
---
 include/linux/kmod.h |2 +
 kernel/kmod.c|  120 ++
 kernel/module.c  |   26 +++
 kernel/params.c  |1 +
 4 files changed, 139 insertions(+), 10 deletions(-)

diff --git a/include/linux/kmod.h b/include/linux/kmod.h
index 10f505c..cc8e674 100644
--- a/include/linux/kmod.h
+++ b/include/linux/kmod.h
@@ -28,8 +28,10 @@
 #ifdef CONFIG_KMOD
 /* modprobe exit status on success, -ve on error.  Return value
  * usually useless though. */
+extern void kmod_sysfs_init(void);
 extern int request_module(const char * name, ...) __attribute__ ((format 
(printf, 1, 2)));
 #else
+static inline void kmod_sysfs_init(void) {};
 static inline int request_module(const char * name, ...) { return -ENOSYS; }
 #endif
 
diff --git a/kernel/kmod.c b/kernel/kmod.c
index 7962761..9f923f8 100644
--- a/kernel/kmod.c
+++ b/kernel/kmod.c
@@ -36,6 +36,8 @@
 #include linux/resource.h
 #include asm/uaccess.h
 
+extern int delete_module(const char *name, unsigned int flags);
+
 extern int max_threads;
 
 static struct workqueue_struct *khelper_wq;
@@ -46,6 +48,7 @@ static struct workqueue_struct *khelper_wq;
modprobe_path is set via /proc/sys.
 */
 char modprobe_path[KMOD_PATH_LEN] = /sbin/modprobe;
+struct module_kobject kmod_mk;
 
 /**
  * request_module - try to load a kernel module
@@ -75,6 +78,11 @@ int request_module(const char *fmt, ...)
static atomic_t kmod_concurrent = ATOMIC_INIT(0);
 #define MAX_KMOD_CONCURRENT 50 /* Completely arbitrary value - KAO */
static int kmod_loop_msg;
+   char modalias[16 + MODULE_NAME_LEN] = MODALIAS=;
+   char *uevent_envp[2] = {
+   modalias,
+   NULL
+   };
 
va_start(args, fmt);
ret = vsnprintf(module_name, MODULE_NAME_LEN, fmt, args);
@@ -82,6 +90,12 @@ int request_module(const char *fmt, ...)
if (ret = MODULE_NAME_LEN)
return -ENAMETOOLONG;
 
+   strcpy(modalias[strlen(MODALIAS=)], module_name);
+   kobject_uevent_env(kmod_mk.kobj, KOBJ_CHANGE, uevent_envp);
+
+   if (modprobe_path[0] == '\0')
+   goto out;
+
/* If modprobe needs a service that is in a module, we get a recursive
 * loop.  Limit the number of running kmod threads to max_threads/2 or
 * MAX_KMOD_CONCURRENT, whichever is the smaller.  A cleaner method
@@ -108,9 +122,115 @@ int request_module(const char *fmt, ...)
 
ret = call_usermodehelper(modprobe_path, argv, envp, 1);
atomic_dec(kmod_concurrent);
+out:
return ret;
 }
 EXPORT_SYMBOL(request_module);
+
+static ssize_t store_mod_request(struct module_attribute *mattr,
+struct module *mod,
+ const char *buffer, size_t count)
+{
+   char name[MODULE_NAME_LEN];
+   int ret;
+
+   if (count  1 || count+1  MODULE_NAME_LEN)
+   return -EINVAL;
+   memcpy(name, buffer, count);
+   name[count] = '\0';
+   if (name[count-1] == '\n')
+   name[count-1] = '\0';
+
+   ret = request_module(name);
+   if (ret  0)
+   return ret;
+   return count;
+}
+
+static struct module_attribute mod_request = {
+   .attr = { .name = mod_request, .mode = S_IWUSR, .owner = THIS_MODULE 
},
+   .store = store_mod_request,
+};
+
+#ifdef CONFIG_MODULE_UNLOAD
+static ssize_t store_mod_unload(struct module_attribute *mattr,
+   struct module *mod,
+   const char *buffer, size_t count)
+{
+   char name[MODULE_NAME_LEN];
+   int ret;
+
+   if (count  1 || count+1  MODULE_NAME_LEN)
+   return -EINVAL;
+   memcpy(name, buffer, count);
+   name[count] = '\0';
+   if (name[count-1] == '\n')
+   name[count-1] = '\0';
+
+   ret = delete_module(name, O_NONBLOCK);
+   if (ret  0)
+   return ret;
+   return count;
+}
+
+static struct module_attribute mod_unload = {
+   .attr = { .name = mod_unload, .mode = S_IWUSR, .owner = THIS_MODULE },
+   .store = store_mod_unload,
+};
+#endif
+
+static ssize_t show_mod_request_helper(struct module_attribute *mattr,
+  struct module *mod,
+  char *buffer)
+{
+   return