Hi,

On Mon, Sep 15, 2025 at 08:06:33AM +0000, Tzung-Bi Shih wrote:
> On Fri, Sep 12, 2025 at 03:59:33PM -0700, Brian Norris wrote:
> > +static int test_config_read(struct pci_bus *bus, unsigned int devfn, int 
> > where,
> > +                       int size, u32 *val)
> > +{
> > +   if (PCI_SLOT(devfn) > 0)
> > +           return PCIBIOS_DEVICE_NOT_FOUND;
> > +
> > +   if (where + size > TEST_CONF_SIZE)
> > +           return PCIBIOS_BUFFER_TOO_SMALL;
> > +
> > +   if (size == 1)
> > +           *val = test_readb(test_conf_space + where);
> > +   else if (size == 2)
> > +           *val = test_readw(test_conf_space + where);
> > +   else if (size == 4)
> > +           *val = test_readl(test_conf_space + where);
> > +
> > +   return PCIBIOS_SUCCESSFUL;
> 
> To handle cases where size might be a value other than {1, 2, 4}, would a
> switch statement with a default case be more robust here?

I was patterning based on pci_generic_config_read() and friends, but I
see that those use an 'else' for the last block, where I used an 'else
if'.

I suppose I could switch to 'else'. I'm not sure 'switch/case' is much
better.

> > +static int test_config_write(struct pci_bus *bus, unsigned int devfn, int 
> > where,
> > +                        int size, u32 val)
> > +{
> > +   if (PCI_SLOT(devfn) > 0)
> > +           return PCIBIOS_DEVICE_NOT_FOUND;
> > +
> > +   if (where + size > TEST_CONF_SIZE)
> > +           return PCIBIOS_BUFFER_TOO_SMALL;
> > +
> > +   if (size == 1)
> > +           test_writeb(test_conf_space + where, val);
> > +   else if (size == 2)
> > +           test_writew(test_conf_space + where, val);
> > +   else if (size == 4)
> > +           test_writel(test_conf_space + where, val);
> > +
> > +   return PCIBIOS_SUCCESSFUL;
> 
> Same here.
> 
> > +static struct pci_dev *hook_device_early;
> > +static struct pci_dev *hook_device_header;
> > +static struct pci_dev *hook_device_final;
> > +static struct pci_dev *hook_device_enable;
> > +
> > +static void pci_fixup_early_hook(struct pci_dev *pdev)
> > +{
> > +   hook_device_early = pdev;
> > +}
> > +DECLARE_PCI_FIXUP_EARLY(TEST_VENDOR_ID, TEST_DEVICE_ID, 
> > pci_fixup_early_hook);
> > [...]
> > +static int pci_fixup_test_init(struct kunit *test)
> > +{
> > +   hook_device_early = NULL;
> > +   hook_device_header = NULL;
> > +   hook_device_final = NULL;
> > +   hook_device_enable = NULL;
> > +
> > +   return 0;
> > +}
> 
> FWIW: if the probe is synchronous and the thread is the same task_struct,
> the module level variables can be eliminated by using:
> 
>     test->priv = kunit_kzalloc(...);
>     KUNIT_ASSERT_PTR_NE(...);
> 
> And in the hooks, kunit_get_current_test() returns the struct kunit *.

Ah, good suggestion, will give that a shot.

> > +static void pci_fixup_match_test(struct kunit *test)
> > +{
> > +   struct device *dev = kunit_device_register(test, DEVICE_NAME);
> > +
> > +   KUNIT_ASSERT_PTR_NE(test, NULL, dev);
> > +
> > +   test_conf_space = kunit_kzalloc(test, TEST_CONF_SIZE, GFP_KERNEL);
> > +   KUNIT_ASSERT_PTR_NE(test, NULL, test_conf_space);
> 
> The common initialization code can be moved to pci_fixup_test_init().
> 
> > +   struct pci_host_bridge *bridge = devm_pci_alloc_host_bridge(dev, 0);
> > +
> > +   KUNIT_ASSERT_PTR_NE(test, NULL, bridge);
> > +   bridge->ops = &test_ops;
> 
> The `bridge` allocation can be moved to .init() too.
> 
> > +   KUNIT_EXPECT_PTR_EQ(test, NULL, hook_device_early);
> > +   KUNIT_EXPECT_PTR_EQ(test, NULL, hook_device_header);
> > +   KUNIT_EXPECT_PTR_EQ(test, NULL, hook_device_final);
> > +   KUNIT_EXPECT_PTR_EQ(test, NULL, hook_device_enable);
> 
> Does it really need to check them?  They are just initialized by .init().

Probably not. I wrote these before there were multiple test cases and an
.init() function, and I didn't reconsider them afterward. And they'll be
especially pointless once these move into a kzalloc'd private structure.

Thanks for the suggestions.

Brian

Reply via email to