PCI: Initialize temporary device in new_id_store()

When setting new_id of a PCI device driver using sysfs a lockdep splat
occurs. This is because new_id_store() builds a temporary pci_dev for
pci_match_device(), which calls device_match_driver_override().  That
depends on the driver_override.lock added by cb3d1049f4 ("driver core:
generalize driver_override in struct device").

The new driver_override.lock was not initialized in the temporary pci_dev,
resulting in this lockdep splat.

Initialize the temporary pci_dev to fix this.

Repro:

  Build with CONFIG_LOCKDEP=y, boot with QEMU, and add a new ID:

  # echo "8086 10f5" > /sys/bus/pci/drivers/e1000e/new_id

  INFO: trying to register non-static key.
  The code is fine but needs lockdep annotation, or maybe
  you didn't initialize this object before use?
  turning off the locking correctness validator.
  CPU: 2 UID: 0 PID: 177 Comm: liveupdate-iomm Not tainted 7.0.0+ #9 PREEMPT(full)
  Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.16.3-0-ga6ed6b701f0a-prebuilt.qemu.org 04/01/2014
  Call Trace:
   <TASK>
   dump_stack_lvl+0x5d/0x80
   register_lock_class+0x77e/0x790
   lock_acquire+0xbf/0x2e0
   pci_match_device+0x24/0x180
   new_id_store+0x189/0x1d0
   kernfs_fop_write_iter+0x14f/0x210
   vfs_write+0x263/0x5e0
   ksys_write+0x79/0xf0
   do_syscall_64+0x117/0xf80

Fixes: 10a4206a24 ("PCI: use generic driver_override infrastructure")
Fixes: 8895d3bcb8 ("PCI: Fail new_id for vendor/device values already built into driver")
Signed-off-by: Samiullah Khawaja <skhawaja@google.com>
[bhelgaas: add commit log details and repro, trim backtrace]
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Danilo Krummrich <dakr@kernel.org>
Link: https://patch.msgid.link/20260505234327.716630-1-skhawaja@google.com
This commit is contained in:
Samiullah Khawaja 2026-05-05 23:43:27 +00:00 committed by Bjorn Helgaas
parent 909f7bf9b0
commit f45a49a238

View File

@ -179,6 +179,11 @@ static const struct pci_device_id *pci_match_device(struct pci_driver *drv,
return NULL; return NULL;
} }
static void _pci_free_device(struct device *dev)
{
kfree(to_pci_dev(dev));
}
/** /**
* new_id_store - sysfs frontend to pci_add_dynid() * new_id_store - sysfs frontend to pci_add_dynid()
* @driver: target device driver * @driver: target device driver
@ -214,11 +219,13 @@ static ssize_t new_id_store(struct device_driver *driver, const char *buf,
pdev->subsystem_vendor = subvendor; pdev->subsystem_vendor = subvendor;
pdev->subsystem_device = subdevice; pdev->subsystem_device = subdevice;
pdev->class = class; pdev->class = class;
pdev->dev.release = _pci_free_device;
device_initialize(&pdev->dev);
if (pci_match_device(pdrv, pdev)) if (pci_match_device(pdrv, pdev))
retval = -EEXIST; retval = -EEXIST;
kfree(pdev); put_device(&pdev->dev);
if (retval) if (retval)
return retval; return retval;