grey- and blacklisting drivers [Was: Re: Using the "best available" driver]

Kay Sievers kay.sievers at vrfy.org
Sun Dec 11 01:43:48 EST 2005


On Fri, Dec 09, 2005 at 08:20:41PM -0800, Greg KH wrote:
> On Thu, Dec 08, 2005 at 04:31:45PM +0100, Kay Sievers wrote:
> > Wouldn't it make the most sense to have a generic flag to pass to _any_
> > driver to skip the bus probing and wait for a manual bind? This would be
> > useful for the storage controllers too, that have 1000's of disks connected,
> > cause you don't really want to trigger all that buses at once with
> > a modprobe.
> 
> Yes, that would be nice to have.
> 
> > That way, you would just need to disable all autobinding with the module
> > configuration and depend on the bus device events to match a given
> > configuration to "manually" enable every device with the udev event.
> 
> Crazy distro installer authors have suggested that we have a way to
> disable all autobinding for a bus somehow, so they can do the binding
> themselves.  They might not be so crazy after all :)
> 
> But as the codepaths for the "autobind" and the "manual bind" are almost
> identical, it might be tough to disable the automatic stuff...

This quick hack works for me, but does it at the driver level, which is
nicer to use than a global bus control.

If the driver is already loaded, it can be controlled in sysfs or it can
be passed to modprobe and the driver gets initialized with that setting.
That way, modules/drivers can be set-up in modprobe.conf, to wait for
manual bind requests.

Thanks,
Kay

---
add "autobind" control to modules to specify device binding policy

This adds a sysfs control to every driver to control the automatic
binding of drivers to devices. If set to "0", the driver will only
bind devices if explicitely asked for, by using the "bind" file.

  $ tree /sys/bus/usb/drivers/ov511/
  /sys/bus/usb/drivers/ov511/
  |-- autobind
  |-- bind
  |-- module -> ../../../../module/ov511
  `-- unbind

At module load, "autobind=0" disables the automatic device bindung.
This is availabe to all current modules without changes.

  $ modprobe ov511
  $ cat /sys/bus/usb/drivers/ov511/autobind
  1

  $ rmmod ov511
  $ modprobe ov511 autobind=0
  $ cat /sys/bus/usb/drivers/ov511/autobind
  0

If a module implements the parameter "autobind" itself, the default
parameter is ignored.
---

diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index fa601b0..946ac47 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -143,6 +143,18 @@ static int driver_helper(struct device *
 	return 0;
 }
 
+static ssize_t show_autobind(struct device_driver *drv, char *buf)
+{
+	return sprintf(buf, "%u\n",  (drv->autobind != 0));
+}
+static ssize_t store_autobind(struct device_driver *drv,
+			      const char *buf, size_t count)
+{
+	drv->autobind = (buf[0] != '0');
+	return count;
+}
+static DRIVER_ATTR(autobind, S_IRUSR | S_IWUSR, show_autobind, store_autobind);
+
 static ssize_t driver_unbind(struct device_driver *drv,
 			     const char *buf, size_t count)
 {
@@ -444,13 +456,15 @@ int bus_add_driver(struct device_driver 
 			return error;
 		}
 
-		driver_attach(drv);
+		if (drv->autobind)
+			driver_attach(drv);
 		klist_add_tail(&drv->knode_bus, &bus->klist_drivers);
 		module_add_driver(drv->owner, drv);
 
 		driver_add_attrs(bus, drv);
 		driver_create_file(drv, &driver_attr_unbind);
 		driver_create_file(drv, &driver_attr_bind);
+		driver_create_file(drv, &driver_attr_autobind);
 	}
 	return error;
 }
@@ -468,6 +482,7 @@ int bus_add_driver(struct device_driver 
 void bus_remove_driver(struct device_driver * drv)
 {
 	if (drv->bus) {
+		driver_remove_file(drv, &driver_attr_autobind);
 		driver_remove_file(drv, &driver_attr_bind);
 		driver_remove_file(drv, &driver_attr_unbind);
 		driver_remove_attrs(drv->bus, drv);
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 3b419c9..16312a9 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -110,6 +110,8 @@ int driver_probe_device(struct device_dr
 static int __device_attach(struct device_driver * drv, void * data)
 {
 	struct device * dev = data;
+	if (!drv->autobind)
+		return 0;
 	return driver_probe_device(drv, dev);
 }
 
diff --git a/drivers/base/driver.c b/drivers/base/driver.c
index 161f3a3..9982543 100644
--- a/drivers/base/driver.c
+++ b/drivers/base/driver.c
@@ -171,6 +171,10 @@ static void klist_devices_put(struct kli
  */
 int driver_register(struct device_driver * drv)
 {
+	if (drv->owner)
+		drv->autobind = drv->owner->autobind;
+	else
+		drv->autobind = 1;
 	klist_init(&drv->klist_devices, klist_devices_get, klist_devices_put);
 	init_completion(&drv->unloaded);
 	return bus_add_driver(drv);
diff --git a/include/linux/device.h b/include/linux/device.h
index 0cdee78..cb46422 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -98,6 +98,7 @@ struct device_driver {
 	struct klist_node	knode_bus;
 
 	struct module		* owner;
+	int			autobind;
 
 	int	(*probe)	(struct device * dev);
 	int	(*remove)	(struct device * dev);
diff --git a/include/linux/module.h b/include/linux/module.h
index 84d75f3..770429b 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -283,6 +283,9 @@ struct module
 	/* Am I GPL-compatible */
 	int license_gplok;
 
+	/* Drivers should bind at module init */
+	int autobind;
+
 #ifdef CONFIG_MODULE_UNLOAD
 	/* Reference counts */
 	struct module_ref ref[NR_CPUS];
diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h
index 368ec8e..676a9fd 100644
--- a/include/linux/moduleparam.h
+++ b/include/linux/moduleparam.h
@@ -96,7 +96,8 @@ extern int parse_args(const char *name,
 		      char *args,
 		      struct kernel_param *params,
 		      unsigned num,
-		      int (*unknown)(char *param, char *val));
+		      int (*unknown)(char *param, char *val, void *data),
+		      void *data);
 
 /* All the helper functions */
 /* The macros to do compile-time type checking stolen from Jakub
diff --git a/init/main.c b/init/main.c
index 27f97f9..3b871d3 100644
--- a/init/main.c
+++ b/init/main.c
@@ -224,7 +224,7 @@ __setup("loglevel=", loglevel);
  * Unknown boot options get handed to init, unless they look like
  * failed parameters
  */
-static int __init unknown_bootoption(char *param, char *val)
+static int __init unknown_bootoption(char *param, char *val, void *data)
 {
 	/* Change NUL term back to "=", to make "param" the whole string. */
 	if (val) {
@@ -408,7 +408,7 @@ static void noinline rest_init(void)
 } 
 
 /* Check for early params. */
-static int __init do_early_param(char *param, char *val)
+static int __init do_early_param(char *param, char *val, void *data)
 {
 	struct obs_kernel_param *p;
 
@@ -434,7 +434,8 @@ void __init parse_early_param(void)
 
 	/* All fall through to do_early_param. */
 	strlcpy(tmp_cmdline, saved_command_line, COMMAND_LINE_SIZE);
-	parse_args("early options", tmp_cmdline, NULL, 0, do_early_param);
+	parse_args("early options", tmp_cmdline, NULL, 0,
+		   do_early_param, NULL);
 	done = 1;
 }
 
@@ -480,7 +481,7 @@ asmlinkage void __init start_kernel(void
 	parse_early_param();
 	parse_args("Booting kernel", command_line, __start___param,
 		   __stop___param - __start___param,
-		   &unknown_bootoption);
+		   &unknown_bootoption, NULL);
 	sort_main_extable();
 	trap_init();
 	rcu_init();
diff --git a/kernel/module.c b/kernel/module.c
index 2ea929d..98ccc2a 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -842,7 +842,7 @@ static int obsolete_params(const char *n
 		kp[i].arg = &obsparm[i];
 	}
 
-	ret = parse_args(name, args, kp, num, NULL);
+	ret = parse_args(name, args, kp, num, NULL, NULL);
  out:
 	kfree(kp);
 	return ret;
@@ -1494,6 +1494,24 @@ static inline void add_kallsyms(struct m
 }
 #endif /* CONFIG_KALLSYMS */
 
+/* Catch given module paramters, not defined in the module itself */
+static int default_param(char *param, char *val, void *data)
+{
+	struct module *mod = data;
+
+	DEBUGP("default module param: param=%s, val=%s, data=%p\n",
+	       param, val, data);
+
+	if (!mod)
+		return -ENOENT;
+
+	if (strcmp(param, "autobind") == 0) {
+		mod->autobind = (val[0] != '0');
+		return 0;
+	}
+	return -ENOENT;
+}
+
 /* Allocate and load the module: note that size of section 0 is always
    zero, and we rely on this for optional sections. */
 static struct module *load_module(void __user *umod,
@@ -1799,6 +1817,7 @@ static struct module *load_module(void _
 			   (unsigned long)mod->module_core + mod->core_size);
 
 	set_fs(old_fs);
+	mod->autobind = 1;
 
 	mod->args = args;
 	if (obsparmindex) {
@@ -1820,7 +1839,7 @@ static struct module *load_module(void _
 				 sechdrs[setupindex].sh_addr,
 				 sechdrs[setupindex].sh_size
 				 / sizeof(struct kernel_param),
-				 NULL);
+				 default_param, mod);
 	}
 	if (err < 0)
 		goto arch_cleanup;
diff --git a/kernel/params.c b/kernel/params.c
index 47ba695..59f5b04 100644
--- a/kernel/params.c
+++ b/kernel/params.c
@@ -51,7 +51,8 @@ static int parse_one(char *param,
 		     char *val,
 		     struct kernel_param *params, 
 		     unsigned num_params,
-		     int (*handle_unknown)(char *param, char *val))
+		     int (*unknown)(char *param, char *val, void *data),
+		     void *data)
 {
 	unsigned int i;
 
@@ -64,9 +65,9 @@ static int parse_one(char *param,
 		}
 	}
 
-	if (handle_unknown) {
+	if (unknown) {
 		DEBUGP("Unknown argument: calling %p\n", handle_unknown);
-		return handle_unknown(param, val);
+		return unknown(param, val, data);
 	}
 
 	DEBUGP("Unknown argument `%s'\n", param);
@@ -132,7 +133,8 @@ int parse_args(const char *name,
 	       char *args,
 	       struct kernel_param *params,
 	       unsigned num,
-	       int (*unknown)(char *param, char *val))
+	       int (*unknown)(char *param, char *val, void *data),
+	       void *data)
 {
 	char *param, *val;
 
@@ -146,7 +148,7 @@ int parse_args(const char *name,
 		int ret;
 
 		args = next_arg(args, &param, &val);
-		ret = parse_one(param, val, params, num, unknown);
+		ret = parse_one(param, val, params, num, unknown, data);
 		switch (ret) {
 		case -ENOENT:
 			printk(KERN_ERR "%s: Unknown parameter `%s'\n",




More information about the linux-pcmcia mailing list