The PCI framework supports "quirks" for PCI devices via several
DECLARE_PCI_FIXUP_*() macros. These macros allow arch or driver code to
match device IDs to provide customizations or workarounds for broken
devices.
This mechanism is generally used in code that can only be built into the
kernel, but there are a few occasions where this mechanism is used in
drivers that can be modules. For example, see commit 574f29036fce ("PCI:
iproc: Apply quirk_paxc_bridge() for module as well as built-in").
The PCI fixup mechanism only works for built-in code, however, because
pci_fixup_device() only scans the ".pci_fixup_*" linker sections found
in the main kernel; it never touches modules.
Extend the fixup approach to modules.
I don't attempt to be clever here; the algorithm here scales with the
number of modules in the system.
Signed-off-by: Brian Norris <briannorris@chromium.org>
---
drivers/pci/quirks.c | 62 ++++++++++++++++++++++++++++++++++++++++++
include/linux/module.h | 18 ++++++++++++
kernel/module/main.c | 26 ++++++++++++++++++
3 files changed, 106 insertions(+)
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index d97335a40193..db5e0ac82ed7 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -207,6 +207,62 @@ extern struct pci_fixup __end_pci_fixups_suspend_late[];
static bool pci_apply_fixup_final_quirks;
+struct pci_fixup_arg {
+ struct pci_dev *dev;
+ enum pci_fixup_pass pass;
+};
+
+static int pci_module_fixup(struct module *mod, void *parm)
+{
+ struct pci_fixup_arg *arg = parm;
+ void *start;
+ unsigned int size;
+
+ switch (arg->pass) {
+ case pci_fixup_early:
+ start = mod->pci_fixup_early;
+ size = mod->pci_fixup_early_size;
+ break;
+ case pci_fixup_header:
+ start = mod->pci_fixup_header;
+ size = mod->pci_fixup_header_size;
+ break;
+ case pci_fixup_final:
+ start = mod->pci_fixup_final;
+ size = mod->pci_fixup_final_size;
+ break;
+ case pci_fixup_enable:
+ start = mod->pci_fixup_enable;
+ size = mod->pci_fixup_enable_size;
+ break;
+ case pci_fixup_resume:
+ start = mod->pci_fixup_resume;
+ size = mod->pci_fixup_resume_size;
+ break;
+ case pci_fixup_suspend:
+ start = mod->pci_fixup_suspend;
+ size = mod->pci_fixup_suspend_size;
+ break;
+ case pci_fixup_resume_early:
+ start = mod->pci_fixup_resume_early;
+ size = mod->pci_fixup_resume_early_size;
+ break;
+ case pci_fixup_suspend_late:
+ start = mod->pci_fixup_suspend_late;
+ size = mod->pci_fixup_suspend_late_size;
+ break;
+ default:
+ return 0;
+ }
+
+ if (!size)
+ return 0;
+
+ pci_do_fixups(arg->dev, start, start + size);
+
+ return 0;
+}
+
void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev)
{
struct pci_fixup *start, *end;
@@ -259,6 +315,12 @@ void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev)
return;
}
pci_do_fixups(dev, start, end);
+
+ struct pci_fixup_arg arg = {
+ .dev = dev,
+ .pass = pass,
+ };
+ module_for_each_mod(pci_module_fixup, &arg);
}
EXPORT_SYMBOL(pci_fixup_device);
diff --git a/include/linux/module.h b/include/linux/module.h
index 3319a5269d28..7faa8987b9eb 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -539,6 +539,24 @@ struct module {
int num_kunit_suites;
struct kunit_suite **kunit_suites;
#endif
+#ifdef CONFIG_PCI_QUIRKS
+ void *pci_fixup_early;
+ unsigned int pci_fixup_early_size;
+ void *pci_fixup_header;
+ unsigned int pci_fixup_header_size;
+ void *pci_fixup_final;
+ unsigned int pci_fixup_final_size;
+ void *pci_fixup_enable;
+ unsigned int pci_fixup_enable_size;
+ void *pci_fixup_resume;
+ unsigned int pci_fixup_resume_size;
+ void *pci_fixup_suspend;
+ unsigned int pci_fixup_suspend_size;
+ void *pci_fixup_resume_early;
+ unsigned int pci_fixup_resume_early_size;
+ void *pci_fixup_suspend_late;
+ unsigned int pci_fixup_suspend_late_size;
+#endif
#ifdef CONFIG_LIVEPATCH
diff --git a/kernel/module/main.c b/kernel/module/main.c
index c66b26184936..50a80c875adc 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -2702,6 +2702,32 @@ static int find_module_sections(struct module *mod, struct load_info *info)
sizeof(*mod->kunit_init_suites),
&mod->num_kunit_init_suites);
#endif
+#ifdef CONFIG_PCI_QUIRKS
+ mod->pci_fixup_early = section_objs(info, ".pci_fixup_early",
+ sizeof(*mod->pci_fixup_early),
+ &mod->pci_fixup_early_size);
+ mod->pci_fixup_header = section_objs(info, ".pci_fixup_header",
+ sizeof(*mod->pci_fixup_header),
+ &mod->pci_fixup_header_size);
+ mod->pci_fixup_final = section_objs(info, ".pci_fixup_final",
+ sizeof(*mod->pci_fixup_final),
+ &mod->pci_fixup_final_size);
+ mod->pci_fixup_enable = section_objs(info, ".pci_fixup_enable",
+ sizeof(*mod->pci_fixup_enable),
+ &mod->pci_fixup_enable_size);
+ mod->pci_fixup_resume = section_objs(info, ".pci_fixup_resume",
+ sizeof(*mod->pci_fixup_resume),
+ &mod->pci_fixup_resume_size);
+ mod->pci_fixup_suspend = section_objs(info, ".pci_fixup_suspend",
+ sizeof(*mod->pci_fixup_suspend),
+ &mod->pci_fixup_suspend_size);
+ mod->pci_fixup_resume_early = section_objs(info, ".pci_fixup_resume_early",
+ sizeof(*mod->pci_fixup_resume_early),
+ &mod->pci_fixup_resume_early_size);
+ mod->pci_fixup_suspend_late = section_objs(info, ".pci_fixup_suspend_late",
+ sizeof(*mod->pci_fixup_suspend_late),
+ &mod->pci_fixup_suspend_late_size);
+#endif
mod->extable = section_objs(info, "__ex_table",
sizeof(*mod->extable), &mod->num_exentries);
--
2.51.0.384.g4c02a37b29-goog
On 9/13/25 12:59 AM, Brian Norris wrote: > The PCI framework supports "quirks" for PCI devices via several > DECLARE_PCI_FIXUP_*() macros. These macros allow arch or driver code to > match device IDs to provide customizations or workarounds for broken > devices. > > This mechanism is generally used in code that can only be built into the > kernel, but there are a few occasions where this mechanism is used in > drivers that can be modules. For example, see commit 574f29036fce ("PCI: > iproc: Apply quirk_paxc_bridge() for module as well as built-in"). > > The PCI fixup mechanism only works for built-in code, however, because > pci_fixup_device() only scans the ".pci_fixup_*" linker sections found > in the main kernel; it never touches modules. > > Extend the fixup approach to modules. > > I don't attempt to be clever here; the algorithm here scales with the > number of modules in the system. > > Signed-off-by: Brian Norris <briannorris@chromium.org> > --- > > drivers/pci/quirks.c | 62 ++++++++++++++++++++++++++++++++++++++++++ > include/linux/module.h | 18 ++++++++++++ > kernel/module/main.c | 26 ++++++++++++++++++ > 3 files changed, 106 insertions(+) > > diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c > index d97335a40193..db5e0ac82ed7 100644 > --- a/drivers/pci/quirks.c > +++ b/drivers/pci/quirks.c > @@ -207,6 +207,62 @@ extern struct pci_fixup __end_pci_fixups_suspend_late[]; > > static bool pci_apply_fixup_final_quirks; > > +struct pci_fixup_arg { > + struct pci_dev *dev; > + enum pci_fixup_pass pass; > +}; > + > +static int pci_module_fixup(struct module *mod, void *parm) > +{ > + struct pci_fixup_arg *arg = parm; > + void *start; > + unsigned int size; > + > + switch (arg->pass) { > + case pci_fixup_early: > + start = mod->pci_fixup_early; > + size = mod->pci_fixup_early_size; > + break; > + case pci_fixup_header: > + start = mod->pci_fixup_header; > + size = mod->pci_fixup_header_size; > + break; > + case pci_fixup_final: > + start = mod->pci_fixup_final; > + size = mod->pci_fixup_final_size; > + break; > + case pci_fixup_enable: > + start = mod->pci_fixup_enable; > + size = mod->pci_fixup_enable_size; > + break; > + case pci_fixup_resume: > + start = mod->pci_fixup_resume; > + size = mod->pci_fixup_resume_size; > + break; > + case pci_fixup_suspend: > + start = mod->pci_fixup_suspend; > + size = mod->pci_fixup_suspend_size; > + break; > + case pci_fixup_resume_early: > + start = mod->pci_fixup_resume_early; > + size = mod->pci_fixup_resume_early_size; > + break; > + case pci_fixup_suspend_late: > + start = mod->pci_fixup_suspend_late; > + size = mod->pci_fixup_suspend_late_size; > + break; > + default: > + return 0; > + } > + > + if (!size) > + return 0; > + > + pci_do_fixups(arg->dev, start, start + size); > + > + return 0; > +} > + > void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) > { > struct pci_fixup *start, *end; > @@ -259,6 +315,12 @@ void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) > return; > } > pci_do_fixups(dev, start, end); > + > + struct pci_fixup_arg arg = { > + .dev = dev, > + .pass = pass, > + }; > + module_for_each_mod(pci_module_fixup, &arg); The function module_for_each_mod() walks not only modules that are LIVE, but also those in the COMING and GOING states. This means that this code can potentially execute a PCI fixup from a module before its init function is invoked, and similarly, a fixup can be executed after the exit function has already run. Is this intentional? > } > EXPORT_SYMBOL(pci_fixup_device); > > diff --git a/include/linux/module.h b/include/linux/module.h > index 3319a5269d28..7faa8987b9eb 100644 > --- a/include/linux/module.h > +++ b/include/linux/module.h > @@ -539,6 +539,24 @@ struct module { > int num_kunit_suites; > struct kunit_suite **kunit_suites; > #endif > +#ifdef CONFIG_PCI_QUIRKS > + void *pci_fixup_early; > + unsigned int pci_fixup_early_size; > + void *pci_fixup_header; > + unsigned int pci_fixup_header_size; > + void *pci_fixup_final; > + unsigned int pci_fixup_final_size; > + void *pci_fixup_enable; > + unsigned int pci_fixup_enable_size; > + void *pci_fixup_resume; > + unsigned int pci_fixup_resume_size; > + void *pci_fixup_suspend; > + unsigned int pci_fixup_suspend_size; > + void *pci_fixup_resume_early; > + unsigned int pci_fixup_resume_early_size; > + void *pci_fixup_suspend_late; > + unsigned int pci_fixup_suspend_late_size; > +#endif > > > #ifdef CONFIG_LIVEPATCH > diff --git a/kernel/module/main.c b/kernel/module/main.c > index c66b26184936..50a80c875adc 100644 > --- a/kernel/module/main.c > +++ b/kernel/module/main.c > @@ -2702,6 +2702,32 @@ static int find_module_sections(struct module *mod, struct load_info *info) > sizeof(*mod->kunit_init_suites), > &mod->num_kunit_init_suites); > #endif > +#ifdef CONFIG_PCI_QUIRKS > + mod->pci_fixup_early = section_objs(info, ".pci_fixup_early", > + sizeof(*mod->pci_fixup_early), > + &mod->pci_fixup_early_size); > + mod->pci_fixup_header = section_objs(info, ".pci_fixup_header", > + sizeof(*mod->pci_fixup_header), > + &mod->pci_fixup_header_size); > + mod->pci_fixup_final = section_objs(info, ".pci_fixup_final", > + sizeof(*mod->pci_fixup_final), > + &mod->pci_fixup_final_size); > + mod->pci_fixup_enable = section_objs(info, ".pci_fixup_enable", > + sizeof(*mod->pci_fixup_enable), > + &mod->pci_fixup_enable_size); > + mod->pci_fixup_resume = section_objs(info, ".pci_fixup_resume", > + sizeof(*mod->pci_fixup_resume), > + &mod->pci_fixup_resume_size); > + mod->pci_fixup_suspend = section_objs(info, ".pci_fixup_suspend", > + sizeof(*mod->pci_fixup_suspend), > + &mod->pci_fixup_suspend_size); > + mod->pci_fixup_resume_early = section_objs(info, ".pci_fixup_resume_early", > + sizeof(*mod->pci_fixup_resume_early), > + &mod->pci_fixup_resume_early_size); > + mod->pci_fixup_suspend_late = section_objs(info, ".pci_fixup_suspend_late", > + sizeof(*mod->pci_fixup_suspend_late), > + &mod->pci_fixup_suspend_late_size); > +#endif > > mod->extable = section_objs(info, "__ex_table", > sizeof(*mod->extable), &mod->num_exentries); Nit: I suggest writing the object_size argument passed to section_objs() here directly as "1" instead of using sizeof(*mod->pci_fixup_...) = sizeof(void). This makes the style consistent with the other code in find_module_sections(). -- Thanks, Petr
Hi Petr, On Tue, Sep 23, 2025 at 02:55:34PM +0200, Petr Pavlu wrote: > On 9/13/25 12:59 AM, Brian Norris wrote: > > @@ -259,6 +315,12 @@ void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) > > return; > > } > > pci_do_fixups(dev, start, end); > > + > > + struct pci_fixup_arg arg = { > > + .dev = dev, > > + .pass = pass, > > + }; > > + module_for_each_mod(pci_module_fixup, &arg); > > The function module_for_each_mod() walks not only modules that are LIVE, > but also those in the COMING and GOING states. This means that this code > can potentially execute a PCI fixup from a module before its init > function is invoked, and similarly, a fixup can be executed after the > exit function has already run. Is this intentional? Thanks for the callout. I didn't really give this part much thought previously. Per the comments, COMING means "Full formed, running module_init". I believe that is a good thing, actually; specifically for controller drivers, module_init() might be probing the controller and enumerating child PCI devices to which we should apply these FIXUPs. That is a key case to support. GOING is not clearly defined in the header comments, but it seems like it's a relatively narrow window between determining there are no module refcounts (and transition to GOING) and starting to really tear it down (transitioning to UNFORMED before any significant teardown). module_exit() runs in the GOING phase. I think it does not make sense to execute FIXUPs on a GOING module; I'll make that change. Re-quoting one piece: > This means that this code > can potentially execute a PCI fixup from a module before its init > function is invoked, IIUC, this part is not true? A module is put into COMING state before its init function is invoked. > > --- a/kernel/module/main.c > > +++ b/kernel/module/main.c > > @@ -2702,6 +2702,32 @@ static int find_module_sections(struct module *mod, struct load_info *info) > > sizeof(*mod->kunit_init_suites), > > &mod->num_kunit_init_suites); > > #endif > > +#ifdef CONFIG_PCI_QUIRKS > > + mod->pci_fixup_early = section_objs(info, ".pci_fixup_early", > > + sizeof(*mod->pci_fixup_early), > > + &mod->pci_fixup_early_size); > > + mod->pci_fixup_header = section_objs(info, ".pci_fixup_header", > > + sizeof(*mod->pci_fixup_header), > > + &mod->pci_fixup_header_size); > > + mod->pci_fixup_final = section_objs(info, ".pci_fixup_final", > > + sizeof(*mod->pci_fixup_final), > > + &mod->pci_fixup_final_size); > > + mod->pci_fixup_enable = section_objs(info, ".pci_fixup_enable", > > + sizeof(*mod->pci_fixup_enable), > > + &mod->pci_fixup_enable_size); > > + mod->pci_fixup_resume = section_objs(info, ".pci_fixup_resume", > > + sizeof(*mod->pci_fixup_resume), > > + &mod->pci_fixup_resume_size); > > + mod->pci_fixup_suspend = section_objs(info, ".pci_fixup_suspend", > > + sizeof(*mod->pci_fixup_suspend), > > + &mod->pci_fixup_suspend_size); > > + mod->pci_fixup_resume_early = section_objs(info, ".pci_fixup_resume_early", > > + sizeof(*mod->pci_fixup_resume_early), > > + &mod->pci_fixup_resume_early_size); > > + mod->pci_fixup_suspend_late = section_objs(info, ".pci_fixup_suspend_late", > > + sizeof(*mod->pci_fixup_suspend_late), > > + &mod->pci_fixup_suspend_late_size); > > +#endif > > > > mod->extable = section_objs(info, "__ex_table", > > sizeof(*mod->extable), &mod->num_exentries); > > Nit: I suggest writing the object_size argument passed to section_objs() > here directly as "1" instead of using sizeof(*mod->pci_fixup_...) = > sizeof(void). This makes the style consistent with the other code in > find_module_sections(). Ack. Thanks, Brian
On 9/23/25 7:42 PM, Brian Norris wrote: > Hi Petr, > > On Tue, Sep 23, 2025 at 02:55:34PM +0200, Petr Pavlu wrote: >> On 9/13/25 12:59 AM, Brian Norris wrote: >>> @@ -259,6 +315,12 @@ void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) >>> return; >>> } >>> pci_do_fixups(dev, start, end); >>> + >>> + struct pci_fixup_arg arg = { >>> + .dev = dev, >>> + .pass = pass, >>> + }; >>> + module_for_each_mod(pci_module_fixup, &arg); >> >> The function module_for_each_mod() walks not only modules that are LIVE, >> but also those in the COMING and GOING states. This means that this code >> can potentially execute a PCI fixup from a module before its init >> function is invoked, and similarly, a fixup can be executed after the >> exit function has already run. Is this intentional? > > Thanks for the callout. I didn't really give this part much thought > previously. > > Per the comments, COMING means "Full formed, running module_init". I > believe that is a good thing, actually; specifically for controller > drivers, module_init() might be probing the controller and enumerating > child PCI devices to which we should apply these FIXUPs. That is a key > case to support. > > GOING is not clearly defined in the header comments, but it seems like > it's a relatively narrow window between determining there are no module > refcounts (and transition to GOING) and starting to really tear it down > (transitioning to UNFORMED before any significant teardown). > module_exit() runs in the GOING phase. > > I think it does not make sense to execute FIXUPs on a GOING module; I'll > make that change. Note that when walking the modules list using module_for_each_mod(), the delete_module() operation can concurrently transition a module to MODULE_STATE_GOING. If you are thinking about simply having pci_module_fixup() check that mod->state isn't MODULE_STATE_GOING, I believe this won't quite work. > > Re-quoting one piece: >> This means that this code >> can potentially execute a PCI fixup from a module before its init >> function is invoked, > > IIUC, this part is not true? A module is put into COMING state before > its init function is invoked. When loading a module, the load_module() function calls complete_formation(), which puts the module into the COMING state. At this point, the new code in pci_fixup_device() can see the new module and potentially attempt to invoke its PCI fixups. However, such a module has still a bit of way to go before its init function is called from do_init_module(). The module hasn't yet had its arguments parsed, is not linked in sysfs, isn't fully registered with codetag support, and hasn't invoked its constructors (needed for gcov/kasan support). I don't know enough about PCI fixups and what is allowable in them, but I suspect it would be better to ensure that no fixup can be invoked from the module during this period. If the above makes sense, I think using module_for_each_mod() might not be the right approach. Alternative options include registering a module notifier or having modules explicitly register their PCI fixups in their init function. -- Cheers, Petr
On Fri, 2025-09-12 at 15:59 -0700, Brian Norris wrote: > The PCI framework supports "quirks" for PCI devices via several > DECLARE_PCI_FIXUP_*() macros. These macros allow arch or driver code to > match device IDs to provide customizations or workarounds for broken > devices. > > This mechanism is generally used in code that can only be built into the > kernel, but there are a few occasions where this mechanism is used in > drivers that can be modules. For example, see commit 574f29036fce ("PCI: > iproc: Apply quirk_paxc_bridge() for module as well as built-in"). > > The PCI fixup mechanism only works for built-in code, however, because > pci_fixup_device() only scans the ".pci_fixup_*" linker sections found > in the main kernel; it never touches modules. > > Extend the fixup approach to modules. This _feels_ a bit odd to me - what if you reload a module, should the fixup be done twice? Right now this was not possible in a module, which is a bit of a gotcha, but at least that's only one for developers, not for users (unless someone messes up and puts it into modular code, as in the example you gave.) Although, come to think of it, you don't even apply the fixup when the module is loaded, so what I just wrote isn't really true. That almost seems like an oversight though, now the module has to be loaded before the PCI device is enumerated, which is unlikely to happen in practice? But then we get the next gotcha - the device is already enumerated, so the fixups cannot be applied at the various enumeration stages, and you're back to having to load the module before PCI enumeration, which could be tricky, or somehow forcing re-enumeration of a given device from userspace, but then you're firmly in "gotcha for the user" territory again ... I don't really have any skin in this game, but overall I'd probably argue it's better to occasionally have to fix things such as in the commit you point out but have a predictable system, than apply things from modules. Perhaps it'd be better to extend the section checking infrastructure to catch and error out on these sections in modules instead, so we catch it at build time, rather than finding things missing at runtime? And yeah, now I've totally ignored the kunit angle, but ... not sure how to combine the two requirements if they are, as I think, conflicting. johannes
Hi Johannes, On Mon, Sep 15, 2025 at 08:33:08AM +0200, Johannes Berg wrote: > On Fri, 2025-09-12 at 15:59 -0700, Brian Norris wrote: > > The PCI framework supports "quirks" for PCI devices via several > > DECLARE_PCI_FIXUP_*() macros. These macros allow arch or driver code to > > match device IDs to provide customizations or workarounds for broken > > devices. > > > > This mechanism is generally used in code that can only be built into the > > kernel, but there are a few occasions where this mechanism is used in > > drivers that can be modules. For example, see commit 574f29036fce ("PCI: > > iproc: Apply quirk_paxc_bridge() for module as well as built-in"). > > > > The PCI fixup mechanism only works for built-in code, however, because > > pci_fixup_device() only scans the ".pci_fixup_*" linker sections found > > in the main kernel; it never touches modules. > > > > Extend the fixup approach to modules. > > This _feels_ a bit odd to me - what if you reload a module, should the > fixup be done twice? Right now this was not possible in a module, which > is a bit of a gotcha, but at least that's only one for developers, not > for users (unless someone messes up and puts it into modular code, as in > the example you gave.) My assumption was that FIXUPs in modules are only legitimate if they apply to a dependency chain that involves the module they are built into. So for example, the fixup could apply to a bridge that is supported only by the module (driver) in question; or it could apply only to devices that sit under the controller in question [1]. Everything I see that could potentially be in a module works like this AFAICT. To answer your question: no, the fixup should not be done twice, unless the device is removed and recreated. More below. [1] The quirks in drivers/pci/controller/dwc/pci-keystone.c look like this. (Side note: pci-keystone.c cannot be built as a module today.) > Although, come to think of it, you don't even apply the fixup when the > module is loaded, so what I just wrote isn't really true. That almost > seems like an oversight though, now the module has to be loaded before > the PCI device is enumerated, which is unlikely to happen in practice? > But then we get the next gotcha - the device is already enumerated, so > the fixups cannot be applied at the various enumeration stages, and > you're back to having to load the module before PCI enumeration, which > could be tricky, or somehow forcing re-enumeration of a given device > from userspace, but then you're firmly in "gotcha for the user" > territory again ... With my assumption above, none of this would really be needed. The relevant device(s) will only exist after the module is loaded, and they will go away when the module is gone. Or am I misreading your problem statements? > I don't really have any skin in this game, but overall I'd probably > argue it's better to occasionally have to fix things such as in the > commit you point out but have a predictable system, than apply things > from modules. FWIW, I believe some folks are working on making *more* controller drivers modular. So this problem will bite more people. (Specifically, I believe Manivannan was working on drivers/pci/controller/dwc/pcie-qcom.c, and it has plenty of FIXUPs.) I also don't think it makes things much less predictable, as long as developers abide by my above assumption. I think that's a perfectly reasonable assumption (it's not so different than, say, MODULE_DEVICE_TABLE), but I could perhaps be convinced otherwise. > Perhaps it'd be better to extend the section checking infrastructure to > catch and error out on these sections in modules instead, so we catch it > at build time, rather than finding things missing at runtime? Maybe I'm missing something here, but it seems like it'd be pretty easy to do something like: #ifdef MODULE #define DECLARE_PCI_FIXUP_SECTION...) BUILD_BUG() #else ... real definitions ... #endif I'd prefer not doing this though, if we can help it, since I believe (a) FIXUPs are useful in perfectly reasonable ways for controller drivers and (b) controller drivers can potentially be modules (yes, there are some pitfalls besides $subject). > And yeah, now I've totally ignored the kunit angle, but ... not sure how > to combine the two requirements if they are, as I think, conflicting. Right, either we support FIXUPs in modules, or we should outlaw them. For kunit: we could still add tests, but just force them to be built-in. It wouldn't be the first kernel subsystem to need that. Brian
© 2016 - 2025 Red Hat, Inc.