Module alias and table support
Subsystems (5)
PATCH 1/1 Module alias and table support
Hi guys,
Below is a preliminary patch which implements module aliases
and reimplements support for MODULE_DEVICE_TABLE on top of it. Tested on drivers/usb/net/pegasus.c, seems to work. The reason for doing this is so that userspace tools are shielded from ever needing to know the layout of the xxx_id structures.
The idea that modules can contain a ".modalias" section of
strings which are aliases for the module. Every module goes through a "finishing" stage (scripts/modfinish) which looks for __module_table* symbols and uses scripts/table2alias.c to add aliases such as "usb:v0506p4601dl*dh*dc*dsc*dp*ic*isc*ip*" which hotplug can use to probe for matching modules.
You'll need the latest (experimental) version of
module-init-tools which support primitive aliases:
http://www.kernel.org/pub/linux/kernel/people/rusty/module-init-tools-0.7.tar.gz
Issues:
1) Make doesn't clean up xxx.raw.o files automatically for some reason
2) Might be neater to call the final result ".mod" and fix up the assumptions about ".o" in the current module-init-tools.
3) Should also create the *.map files at module install time, for backwards compatibility.
4) script is damn ugly, should probably turn into .c file once prototyping done.
5) pci_device, isapnp_card and isapnp_device need testing.
Feedback welcome,
Rusty.
--
Anyone who quotes me in their sig is an idiot. -- Rusty Russell.
Name: Module alias and device table support
Author: Rusty Russell
Status: Experimental
Depends: Module/modfixes2.patch.gz
Depends: Module/module-ppc.patch.gz
D: Introduces "MODULE_ALIAS" which modules can use to embed their own
D: aliases for modprobe to use. Also adds a "finishing" step to modules to
D: supplement their aliases based on MODULE_TABLE declarations, eg.
D: 'usb:v0506p4601dl*dh*dc*dsc*dp*ic*isc*ip*' for drivers/usb/net/pegasus.o
include/linux/module.h +18 -2
@@ -27,8 +27,6 @@#define MODULE_AUTHOR (name)#define MODULE_DESCRIPTION (desc)#define MODULE_SUPPORTED_DEVICE (name) -#define MODULE_GENERIC_TABLE (gtype,name) -#define MODULE_DEVICE_TABLE (type,name)#define MODULE_PARM_DESC (var,desc)#define print_symbol (format, addr)#define print_modules ()
@@ -45,8 +43,26 @@ struct kernel_symbola constant so it works in initializers. */ extern struct module __this_module;#define THIS_MODULE (&__this_module) +/* For userspace: you can also call me... */ +#define MODULE_ALIAS (alias) \ + static const char __alias_##__LINE__[] \ + __attribute__((section(".modalias"))) = alias + +/* Sets up aliases for hotplug-style automagic. Known types are: +pci - struct pci_device_id - List of PCI ids supported +isapnp - struct isapnp_device_id - List of ISA PnP ids supported +usb - struct usb_device_id - List of USB ids supported +*/ +#define MODULE_TABLE (type, table) \ + extern const struct type##_id __module_table_##type \ + __attribute__((alias(#table))) +#define MODULE_DEVICE_TABLE (type, table) \ + MODULE_TABLE(type##_device, table)#else #define THIS_MODULE ((struct module *)0) +#define MODULE_ALIAS (alias) +#define MODULE_TABLE (type, table) +#define MODULE_DEVICE_TABLE (type, table)#endif #ifdef CONFIG_MODULES
include/linux/isapnp.h +3 -1
@@ -140,8 +140,9 @@ struct isapnp_resources {/* export used IDs outside module */ #define ISAPNP_CARD_TABLE (name) \ - MODULE_GENERIC_TABLE(isapnp_card, name) + MODULE_TABLE(isapnp_card, name) +/* If you change this, you must update scripts/table2alias.c. */ struct isapnp_card_id {unsigned long driver_data;/* data private to the driver */ unsigned short card_vendor, card_device;
@@ -156,6 +157,7 @@ struct isapnp_card_id {#define ISAPNP_DEVICE_SINGLE_END \ .card_vendor = 0, .card_device = 0 +/* If you change this, you must update scripts/table2alias.c. */ struct isapnp_device_id {unsigned short card_vendor, card_device;unsigned short vendor, function;
include/linux/pci.h +1 -0
@@ -467,6 +467,7 @@ struct pbus_set_ranges_dataunsigned long prefetch_start, prefetch_end; }; +/* If you change this, you must update scripts/table2alias.c. */ struct pci_device_id {unsigned int vendor, device;/* Vendor and device ID or PCI_ANY_ID */ unsigned int subvendor, subdevice;/* Subsystem ID's or PCI_ANY_ID */
include/linux/usb.h +1 -0
@@ -361,6 +361,7 @@ static inline int usb_make_path (struct* matches towards the beginning of your table, so that driver_info can * record quirks of specific products. */ +/* If you change this, you must update scripts/table2alias.c. */ struct usb_device_id {/* which fields to match against? */ __u16 match_flags;
scripts/Makefile +2 -2
@@ -8,7 +8,7 @@ # docproc: Preprocess .tmpl file in order to generate .sgml documentation # conmakehash: Create arrays for initializing the kernel console tables -EXTRA_TARGETS := fixdep split-include docproc conmakehash +EXTRA_TARGETS := fixdep split-include docproc conmakehash table2alias subdir- := lxdialog kconfig
@@ -22,7 +22,7 @@ KBUILD_BUILTIN := 1 # can't do it # --------------------------------------------------------------------------- -host-progs := fixdep split-include conmakehash docproc +host-progs := fixdep split-include conmakehash docproc table2alias include $(TOPDIR)/Rules.make
scripts/Makefile.build +23 -4
@@ -91,7 +91,8 @@ cmd_cc_i_c = $(CPP) $(c_flags) - quiet_cmd_cc_o_c = CC $@ cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< -%.o: %.c FORCE +# Not for modules +$(sort $(objs-y) $(multi-objs)) %.o: %.c FORCE $(call if_changed_dep,cc_o_c) quiet_cmd_cc_lst_c = MKLST $@
@@ -100,6 +101,16 @@ cmd_cc_lst_c = $(CC) $(c_flags) -g %.lst: %.c FORCE $(call if_changed_dep,cc_lst_c) +# Modules need to go via "finishing" step. +quiet_cmd_modfinish = FINISH $@ +cmd_modfinish = sh scripts/modfinish $< $@ + +$(patsubst %.o,%.raw.o,$(filter-out $(multi-used-m),$(obj-m))): %.raw.o: %.c + $(call if_changed_dep,cc_o_c) + +$(obj-m): %.o: %.raw.o + $(call cmd,modfinish) + # Compile assembler sources (.S) # ---------------------------------------------------------------------------
@@ -161,7 +172,7 @@ targets += $(L_TARGET) endif # -# Rule to link composite objects +# Rule to link composite objects (builtin) # quiet_cmd_link_multi = LD $@
@@ -174,8 +185,16 @@ cmd_link_multi = $(LD) $(LDFLAGS) $(EXTR $(multi-used-y) : %.o: $(multi-objs-y) FORCE $(call if_changed,link_multi) -$(multi-used-m) : %.o: $(multi-objs-m) FORCE - $(call if_changed,link_multi) +# +# Rule to link composite objects (module) +# + +quiet_cmd_link_mmulti = LD $@ +cmd_link_mmulti = $(LD) $(LDFLAGS) $(EXTRA_LDFLAGS) -r -o $@ $(filter $(addprefix $(obj)/,$($(subst $(obj)/,,$(@:.raw.o=-objs))) $($(subst $(obj)/,,$(@:.raw.o=-y)))),$^) + +# Need finishing step +$(multi-used-m:.o=.raw.o) : %.raw.o: $(multi-objs-m) FORCE + $(call if_changed,link_mmulti) targets += $(multi-used-y) $(multi-used-m)
scripts/modfinish +47 -0
47 lines not on this page · raw message
scripts/table2alias.c +316 -0
316 lines not on this page · raw message
Quoted 7 lines · Rusty Russell
Hi guys,
Below is a preliminary patch which implements module aliases
and reimplements support forMODULE_DEVICE_TABLEon top of it. Tested ondrivers/usb/net/pegasus.c, seems to work. The reason for doing this is so that userspace tools are shielded from ever needing to know the layout of thexxx_idstructures.
Nice, I like that.
Quoted 6 lines
The idea that modules can contain a ".modalias" section of
strings which are aliases for the module. Every module goes through a "finishing" stage (scripts/modfinish) which looks for __module_table* symbols and usesscripts/table2alias.cto add aliases such as "usb:v0506p4601dl*dh*dc*dsc*dp*ic*isc*ip*" which hotplug can use to probe for matching modules.
How are you going to be able to handle hex values in that string? Or are you just going to rely on the fact that no valid hex value can be a identifier? You might want to add a ':' or something between fields to make it easier to parse, unless you've already written a bash and C parser that I can use :)
Hm, there goes my wonderful % decrease in size claims of diethotplug if we shink the original size of the module_table files...
scripts/table2alias.c line 59
Quoted 13 lines
scripts/table2alias.c +13
-0+ +#define USB_DEVICE_ID_MATCH_VENDOR 0x0001 +#define USB_DEVICE_ID_MATCH_PRODUCT 0x0002 +#define USB_DEVICE_ID_MATCH_DEV_LO 0x0004 +#define USB_DEVICE_ID_MATCH_DEV_HI 0x0008 +#define USB_DEVICE_ID_MATCH_DEV_CLASS 0x0010 +#define USB_DEVICE_ID_MATCH_DEV_SUBCLASS 0x0020 +#define USB_DEVICE_ID_MATCH_DEV_PROTOCOL 0x0040 +#define USB_DEVICE_ID_MATCH_INT_CLASS 0x0080 +#define USB_DEVICE_ID_MATCH_INT_SUBCLASS 0x0100 +#define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200 + +struct usb_device_id {
We're already including other kernel header files, why not include usb.h too? That way we don't have to remember to update the structures in two places.
Quoted 11 lines
scripts/table2alias.c +11
-0+/* which fields to match against? */ +uint16_t match_flags; + +/* Used for product specific matches; range is inclusive */ +uint16_t idVendor; +uint16_t idProduct; +uint16_t bcdDevice_lo; +uint16_t bcdDevice_hi; + +/* Used for device class matches */ +uint8_t bDeviceClass;
scripts/table2alias.c line 80
Quoted 10 lines
scripts/table2alias.c +10
-0+uint8_t bDeviceSubClass; +uint8_t bDeviceProtocol; + +/* Used for interface class matches */ +uint8_t bInterfaceClass; +uint8_t bInterfaceSubClass; +uint8_t bInterfaceProtocol; + +/* not matched against */ +kernel_long driver_info;
Or is it because of "kernel_long"? I'm pretty sure this field is only used within the kernel, and userspace does not care at all about it.
Quoted 7 lines
scripts/table2alias.c +7
-0+/* Looks like "usb:vNpNdlNdhNdcNdscNdpNicNiscNipN" */ +static int do_usb_table (void ) +{ +struct usb_device_id id; +int r; +char alias[200 ]; +
scripts/table2alias.c line 140
Quoted 10 lines
scripts/table2alias.c +10
-0+while ((r =xread (STDIN_FILENO , &id,sizeof (id))) ==sizeof (id)) { +TO_NATIVE (id.match_flags); +TO_NATIVE (id.idVendor); +TO_NATIVE (id.idProduct); +TO_NATIVE (id.bcdDevice_lo); +TO_NATIVE (id.bcdDevice_hi); + +strcpy (alias,"usb:" ); +ADD (alias,"v" , id.match_flags&USB_DEVICE_ID_MATCH_VENDOR , + id.idVendor);
Why are you doing this "preprocessing" of the flags and the different fields? If you do this, you mess with the logic of the current /sbin/hotplug tools a lot, as they expect to have to do this.
Granted, it's much nicer to do this only once, at this moment in time, but it is a change that we need to be aware of.
Also realize that if you do this, you can't generate the existing modules.*map files from the exported values.
scripts/table2alias.c line 176
Quoted 6 lines
scripts/table2alias.c +6
-0+struct pci_device_id { +unsigned int vendor, device;/* Vendor and device ID or PCI_ANY_ID */ +unsigned int subvendor, subdevice;/* Subsystem ID's or PCI_ANY_ID */ +unsigned int class, class_mask;/* (class,subclass,prog-if) triplet */ +kernel_long driver_data;/* Data private to the driver */ +};
You should specify a proper size of vendor, device, and other fields of size "int" like you did above for USB. Also, driver_data isn't used by userspace, so don't worry about keeping the size properly.
Or just include <linux/pci.h> and don't worry about it :)
Quoted 7 lines
scripts/table2alias.c +7
-0+/* Looks like: pci:vNdNsvNsdNcN. */ +static int do_pci_table (void ) +{ +struct pci_device_id id; +int r; +char alias[200 ]; +
scripts/table2alias.c line 194
Quoted 10 lines
scripts/table2alias.c +10
-0+while ((r =xread (STDIN_FILENO , &id,sizeof (id))) ==sizeof (id)) { +TO_NATIVE (id.vendor); +TO_NATIVE (id.device); +TO_NATIVE (id.subvendor); +TO_NATIVE (id.subdevice); +TO_NATIVE (id.class); +TO_NATIVE (id.class_mask); + +strcpy (alias,"pci:" ); +ADD (alias,"v" , id.vendor !=PCI_ANY_ID , id.vendor);
Again, same "preprocessing" comment as made above for USB.
In summary, I like the idea, and removing userspace knowledge of kernel structures is very nice. Just a few tweaks are needed.
thanks,
greg k-h
In message <[email protected]> you write:
Quoted 8 lines · Rusty Russell
Quoted 6 lines
The idea that modules can contain a ".modalias" section of
strings which are aliases for the module. Every module goes through a "finishing" stage (scripts/modfinish) which looks for __module_table* symbols and usesscripts/table2alias.cto add aliases such as "usb:v0506p4601dl*dh*dc*dsc*dp*ic*isc*ip*" which hotplug can use to probe for matching modules.
Quoted 5 lines
How are you going to be able to handle hex values in that string? Or are you just going to rely on the fact that no valid hex value can be a identifier? You might want to add a ':' or something between fields to make it easier to parse, unless you've already written a bash and C parser that I can use :)
Yes, good idea. Of course, you shouldn't need to parse them, you just need to create them, but clearer is better (I'd use , not : though).
Quoted 2 lines
Hm, there goes my wonderful % decrease in size claims of diethotplug if we shink the original size of the
module_tablefiles...
Sorry. I'll try to be less efficient in the future.
Quoted 9 lines
We're already including other kernel header files, why not include
usb.htoo? That way we don't have to remember to update the structures in two places.scripts/table2alias.c line 80
Quoted 2 lines
scripts/table2alias.c +2
-0+/* not matched against */ +kernel_long driver_info;Or is it because of "
kernel_long"? I'm pretty sure this field is only used within the kernel, and userspace does not care at all about it.
It sucks to reproduce this, yes. But you need to know the size of the structure to grab it out of the object file. At least this way it's in the kernel source where we can change it.
Quoted 13 lines
Quoted 13 lines
scripts/table2alias.c +13
-0+/* Looks like "usb:vNpNdlNdhNdcNdscNdpNicNiscNipN" */ +static int do_usb_table (void ) +{ +struct usb_device_id id; +int r; +char alias[200 ]; + +while ((r =xread (STDIN_FILENO , &id,sizeof (id))) ==sizeof (id)) { +TO_NATIVE (id.match_flags); +TO_NATIVE (id.idVendor); +TO_NATIVE (id.idProduct); +TO_NATIVE (id.bcdDevice_lo); +TO_NATIVE (id.bcdDevice_hi);
Quoted 8 lines
scripts/table2alias.c line 140
Quoted 4 lines
scripts/table2alias.c +4
-0+ +strcpy (alias,"usb:" ); +ADD (alias,"v" , id.match_flags&USB_DEVICE_ID_MATCH_VENDOR , + id.idVendor);Why are you doing this "preprocessing" of the flags and the different fields? If you do this, you mess with the logic of the current
/sbin/hotplugtools a lot, as they expect to have to do this.
The plan was that /sbin/hotplug will gather all the fields for
whatever device has been inserted and create the modprobe string, eg:
system("modprobe usb:v0506p4601dl01dh01dc01dsc01dp01ic01isc01ip01");
Which will simply match the alias such as:
usb:v0506p4601dl*dh*dc*dsc*dp*ic*isc*ip*Quoted 2 lines
Also realize that if you do this, you can't generate the existing modules.*map files from the exported values.
Hmm, I thought about enhancing this code to generate the .map
files as well (where it has all the information). BTW, did I break the current depmod map-generating code?
Quoted 2 lines
In summary, I like the idea, and removing userspace knowledge of kernel structures is very nice. Just a few tweaks are needed.
Thanks,
Rusty.
--
Anyone who quotes me in their sig is an idiot. -- Rusty Russell.
Quoted 9 lines · Rusty Russell
Quoted 5 lines
scripts/table2alias.c line 80
Quoted 2 lines
scripts/table2alias.c +2
-0+/* not matched against */ +kernel_long driver_info;Or is it because of "
kernel_long"? I'm pretty sure this field is only used within the kernel, and userspace does not care at all about it.It sucks to reproduce this, yes. But you need to know the size of the structure to grab it out of the object file. At least this way it's in the kernel source where we can change it.
But can't we still just use the structure from the kernel header and not have to retype it here? If needed, we can change the type of driver_info into something more portable than what it is today. That would be much easier in the long run.
Quoted 11 lines
Quoted 3 lines
Why are you doing this "preprocessing" of the flags and the different fields? If you do this, you mess with the logic of the current
/sbin/hotplugtools a lot, as they expect to have to do this.The plan was that
/sbin/hotplugwill gather all the fields for
whatever device has been inserted and create the modprobe string, eg:system("modprobe usb:v0506p4601dl01dh01dc01dsc01dp01ic01isc01ip01"); Which will simply match the alias such as: usb:v0506p4601dl*dh*dc*dsc*dp*ic*isc*ip*
Ah, you never told me about this plan :)
Yes, that would be very nice to have, pushing the logic out of the /sbin/hotplug code and into modprobe doesn't bother me. That just saved a _whole_ bunch of space in diethotplug, so you've made up for making the size smaller.
How would modprobe know which driver to load based on the above line? Are you going to scan all module files, or rely on something like the modules.*map files of today?
Quoted 6 lines
Quoted 2 lines
Also realize that if you do this, you can't generate the existing modules.*map files from the exported values.
Hmm, I thought about enhancing this code to generate the .map
files as well (where it has all the information). BTW, did I break the current depmod map-generating code?
I don't know, I haven't looked into that. Just realize that if you pre-process the information like you are proposing to do, you can't get it back later, which changes the way things work.
thanks,
greg k-h
In message <[email protected]> you write:
Quoted 7 lines · Rusty Russell
Quoted 6 lines
Quoted 5 lines
scripts/table2alias.c line 80
Quoted 2 lines
scripts/table2alias.c +2
-0+/* not matched against */ +kernel_long driver_info;Or is it because of "
kernel_long"? I'm pretty sure this field is only used within the kernel, and userspace does not care at all about it.
Quoted 8 lines
Quoted 3 lines
It sucks to reproduce this, yes. But you need to know the size of the structure to grab it out of the object file. At least this way it's in the kernel source where we can change it.
But can't we still just use the structure from the kernel header and not have to retype it here? If needed, we can change the type of
driver_infointo something more portable than what it is today. That would be much easier in the long run.
Absolutely. But I didn't want to mess with your headers.
Quoted 4 lines
Quoted 2 lines
Which will simply match the alias such as: usb:v0506p4601dl*dh*dc*dsc*dp*ic*isc*ip*Ah, you never told me about this plan :)
Oh, so I didn't explain the "clever" part of my clever plan? Oops.
Quoted 8 lines
Yes, that would be very nice to have, pushing the logic out of the
/sbin/hotplugcode and into modprobe doesn't bother me. That just saved a _whole_ bunch of space in diethotplug, so you've made up for making the size smaller.How would modprobe know which driver to load based on the above line? Are you going to scan all module files, or rely on something like the modules.*map files of today?
0.7 scans all the files (since it has no modules.dep and needs the dependencies anyway). 0.8 reintroduces modules.dep (thanks Adam!) which is where the alias info logically belongs ("stuff extracted from modules"). Or maybe a separate file (implementation handwave).
Quoted 3 lines
Just realize that if you pre-process the information like you are proposing to do, you can't get it back later, which changes the way things work.
Well, you could, by sucking out the aliases which start with "usb:" (the post-processing just turns the tables into aliases, doesn't delete them or anything). You could also use the old-fashioned way and suck it from the tables directly, but then you're back in "intimate knowledge of kernel internals" land.
Cheers,
Rusty.
--
Anyone who quotes me in their sig is an idiot. -- Rusty Russell.