Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+74 −43
Original line number Diff line number Diff line
@@ -84,65 +84,92 @@ int usb_amd_find_chipset_info(void)
{
	u8 rev = 0;
	unsigned long flags;
	struct amd_chipset_info info;
	int ret;

	spin_lock_irqsave(&amd_lock, flags);

	amd_chipset.probe_count++;
	/* 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);
		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);
	if (amd_chipset.smbus_dev) {
		rev = amd_chipset.smbus_dev->revision;
	info.smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI, 0x4385, NULL);
	if (info.smbus_dev) {
		rev = info.smbus_dev->revision;
		if (rev >= 0x40)
			amd_chipset.sb_type = 1;
			info.sb_type = 1;
		else if (rev >= 0x30 && rev <= 0x3b)
			amd_chipset.sb_type = 3;
			info.sb_type = 3;
	} else {
		amd_chipset.smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
		info.smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
						0x780b, NULL);
		if (!amd_chipset.smbus_dev) {
			spin_unlock_irqrestore(&amd_lock, flags);
			return 0;
		if (!info.smbus_dev) {
			ret = 0;
			goto commit;
		}
		rev = amd_chipset.smbus_dev->revision;

		rev = info.smbus_dev->revision;
		if (rev >= 0x11 && rev <= 0x18)
			amd_chipset.sb_type = 2;
			info.sb_type = 2;
	}

	if (amd_chipset.sb_type == 0) {
		if (amd_chipset.smbus_dev) {
			pci_dev_put(amd_chipset.smbus_dev);
			amd_chipset.smbus_dev = NULL;
	if (info.sb_type == 0) {
		if (info.smbus_dev) {
			pci_dev_put(info.smbus_dev);
			info.smbus_dev = NULL;
		}
		spin_unlock_irqrestore(&amd_lock, flags);
		return 0;
		ret = 0;
		goto commit;
	}

	amd_chipset.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x9601, NULL);
	if (amd_chipset.nb_dev) {
		amd_chipset.nb_type = 1;
	info.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x9601, NULL);
	if (info.nb_dev) {
		info.nb_type = 1;
	} else {
		amd_chipset.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD,
							0x1510, NULL);
		if (amd_chipset.nb_dev) {
			amd_chipset.nb_type = 2;
		info.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x1510, NULL);
		if (info.nb_dev) {
			info.nb_type = 2;
		} else {
			amd_chipset.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD,
			info.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD,
						     0x9600, NULL);
			if (amd_chipset.nb_dev)
				amd_chipset.nb_type = 3;
			if (info.nb_dev)
				info.nb_type = 3;
		}
	}

	amd_chipset.probe_result = 1;
	ret = info.probe_result = 1;
	printk(KERN_DEBUG "QUIRK: Enable AMD PLL fix\n");

commit:

	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 amd_chipset.probe_result;
	}

	return ret;
}
EXPORT_SYMBOL_GPL(usb_amd_find_chipset_info);

@@ -284,6 +311,7 @@ EXPORT_SYMBOL_GPL(usb_amd_quirk_pll_enable);

void usb_amd_dev_put(void)
{
	struct pci_dev *nb, *smbus;
	unsigned long flags;

	spin_lock_irqsave(&amd_lock, flags);
@@ -294,20 +322,23 @@ void usb_amd_dev_put(void)
		return;
	}

	if (amd_chipset.nb_dev) {
		pci_dev_put(amd_chipset.nb_dev);
	/* save them to pci_dev_put outside of spinlock */
	nb    = amd_chipset.nb_dev;
	smbus = amd_chipset.smbus_dev;

	amd_chipset.nb_dev = NULL;
	}
	if (amd_chipset.smbus_dev) {
		pci_dev_put(amd_chipset.smbus_dev);
	amd_chipset.smbus_dev = NULL;
	}
	amd_chipset.nb_type = 0;
	amd_chipset.sb_type = 0;
	amd_chipset.isoc_reqs = 0;
	amd_chipset.probe_result = 0;

	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);