Commit 9ab7927b authored by Joerg Roedel's avatar Joerg Roedel Committed by Greg Kroah-Hartman

USB host: Fix lockdep warning in AMD PLL quirk

Booting latest kernel on my test machine produces a lockdep
warning from the usb_amd_find_chipset_info() function:

 WARNING: at /data/lemmy/linux.trees.git/kernel/lockdep.c:2465 lockdep_trace_alloc+0x95/0xc2()
 Hardware name: Snook
 Modules linked in:
 Pid: 959, comm: work_for_cpu Not tainted 2.6.39-rc2+ #22
 Call Trace:
  [<ffffffff8103c0d4>] warn_slowpath_common+0x80/0x98
  [<ffffffff812387e6>] ? T.492+0x24/0x26
  [<ffffffff8103c101>] warn_slowpath_null+0x15/0x17
  [<ffffffff81068667>] lockdep_trace_alloc+0x95/0xc2
  [<ffffffff810ed9ac>] slab_pre_alloc_hook+0x18/0x3b
  [<ffffffff810ef227>] kmem_cache_alloc_trace+0x25/0xba
  [<ffffffff812387e6>] T.492+0x24/0x26
  [<ffffffff81238816>] pci_get_subsys+0x2e/0x73
  [<ffffffff8123886c>] pci_get_device+0x11/0x13
  [<ffffffff814082a9>] usb_amd_find_chipset_info+0x3f/0x18a
...

It turns out that this function calls pci_get_device under a spin_lock
with irqs disabled, but the pci_get_device function is only allowed in
preemptible context.

This patch fixes the warning by making all data-structure
modifications on temporal storage and commiting this back
into the visible structure at the end. While at it, this
patch also moves the pci_dev_put calls out of the spinlocks
because this function might sleep too.
Signed-off-by: default avatarJoerg Roedel <joerg.roedel@amd.com>
Acked-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 16a2f970
...@@ -84,65 +84,92 @@ int usb_amd_find_chipset_info(void) ...@@ -84,65 +84,92 @@ int usb_amd_find_chipset_info(void)
{ {
u8 rev = 0; u8 rev = 0;
unsigned long flags; unsigned long flags;
struct amd_chipset_info info;
int ret;
spin_lock_irqsave(&amd_lock, flags); spin_lock_irqsave(&amd_lock, flags);
amd_chipset.probe_count++;
/* probe only once */ /* probe only once */
if (amd_chipset.probe_count > 1) { if (amd_chipset.probe_count > 0) {
amd_chipset.probe_count++;
spin_unlock_irqrestore(&amd_lock, flags); spin_unlock_irqrestore(&amd_lock, flags);
return amd_chipset.probe_result; return amd_chipset.probe_result;
} }
memset(&info, 0, sizeof(info));
spin_unlock_irqrestore(&amd_lock, flags);
amd_chipset.smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI, 0x4385, NULL); info.smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI, 0x4385, NULL);
if (amd_chipset.smbus_dev) { if (info.smbus_dev) {
rev = amd_chipset.smbus_dev->revision; rev = info.smbus_dev->revision;
if (rev >= 0x40) if (rev >= 0x40)
amd_chipset.sb_type = 1; info.sb_type = 1;
else if (rev >= 0x30 && rev <= 0x3b) else if (rev >= 0x30 && rev <= 0x3b)
amd_chipset.sb_type = 3; info.sb_type = 3;
} else { } else {
amd_chipset.smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD, info.smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
0x780b, NULL); 0x780b, NULL);
if (!amd_chipset.smbus_dev) { if (!info.smbus_dev) {
spin_unlock_irqrestore(&amd_lock, flags); ret = 0;
return 0; goto commit;
} }
rev = amd_chipset.smbus_dev->revision;
rev = info.smbus_dev->revision;
if (rev >= 0x11 && rev <= 0x18) if (rev >= 0x11 && rev <= 0x18)
amd_chipset.sb_type = 2; info.sb_type = 2;
} }
if (amd_chipset.sb_type == 0) { if (info.sb_type == 0) {
if (amd_chipset.smbus_dev) { if (info.smbus_dev) {
pci_dev_put(amd_chipset.smbus_dev); pci_dev_put(info.smbus_dev);
amd_chipset.smbus_dev = NULL; info.smbus_dev = NULL;
} }
spin_unlock_irqrestore(&amd_lock, flags); ret = 0;
return 0; goto commit;
} }
amd_chipset.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x9601, NULL); info.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x9601, NULL);
if (amd_chipset.nb_dev) { if (info.nb_dev) {
amd_chipset.nb_type = 1; info.nb_type = 1;
} else { } else {
amd_chipset.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, info.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x1510, NULL);
0x1510, NULL); if (info.nb_dev) {
if (amd_chipset.nb_dev) { info.nb_type = 2;
amd_chipset.nb_type = 2; } else {
} else { info.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD,
amd_chipset.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x9600, NULL);
0x9600, NULL); if (info.nb_dev)
if (amd_chipset.nb_dev) info.nb_type = 3;
amd_chipset.nb_type = 3;
} }
} }
amd_chipset.probe_result = 1; ret = info.probe_result = 1;
printk(KERN_DEBUG "QUIRK: Enable AMD PLL fix\n"); printk(KERN_DEBUG "QUIRK: Enable AMD PLL fix\n");
spin_unlock_irqrestore(&amd_lock, flags); commit:
return amd_chipset.probe_result;
spin_lock_irqsave(&amd_lock, flags);
if (amd_chipset.probe_count > 0) {
/* race - someone else was faster - drop devices */
/* Mark that we where here */
amd_chipset.probe_count++;
ret = amd_chipset.probe_result;
spin_unlock_irqrestore(&amd_lock, flags);
if (info.nb_dev)
pci_dev_put(info.nb_dev);
if (info.smbus_dev)
pci_dev_put(info.smbus_dev);
} else {
/* no race - commit the result */
info.probe_count++;
amd_chipset = info;
spin_unlock_irqrestore(&amd_lock, flags);
}
return ret;
} }
EXPORT_SYMBOL_GPL(usb_amd_find_chipset_info); EXPORT_SYMBOL_GPL(usb_amd_find_chipset_info);
...@@ -284,6 +311,7 @@ EXPORT_SYMBOL_GPL(usb_amd_quirk_pll_enable); ...@@ -284,6 +311,7 @@ EXPORT_SYMBOL_GPL(usb_amd_quirk_pll_enable);
void usb_amd_dev_put(void) void usb_amd_dev_put(void)
{ {
struct pci_dev *nb, *smbus;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&amd_lock, flags); spin_lock_irqsave(&amd_lock, flags);
...@@ -294,20 +322,23 @@ void usb_amd_dev_put(void) ...@@ -294,20 +322,23 @@ void usb_amd_dev_put(void)
return; return;
} }
if (amd_chipset.nb_dev) { /* save them to pci_dev_put outside of spinlock */
pci_dev_put(amd_chipset.nb_dev); nb = amd_chipset.nb_dev;
amd_chipset.nb_dev = NULL; smbus = amd_chipset.smbus_dev;
}
if (amd_chipset.smbus_dev) { amd_chipset.nb_dev = NULL;
pci_dev_put(amd_chipset.smbus_dev); amd_chipset.smbus_dev = NULL;
amd_chipset.smbus_dev = NULL;
}
amd_chipset.nb_type = 0; amd_chipset.nb_type = 0;
amd_chipset.sb_type = 0; amd_chipset.sb_type = 0;
amd_chipset.isoc_reqs = 0; amd_chipset.isoc_reqs = 0;
amd_chipset.probe_result = 0; amd_chipset.probe_result = 0;
spin_unlock_irqrestore(&amd_lock, flags); spin_unlock_irqrestore(&amd_lock, flags);
if (nb)
pci_dev_put(nb);
if (smbus)
pci_dev_put(smbus);
} }
EXPORT_SYMBOL_GPL(usb_amd_dev_put); EXPORT_SYMBOL_GPL(usb_amd_dev_put);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment