netdev
[Top] [All Lists]

[PATCH] pci_get_slot()

To: Linus Torvalds <torvalds@xxxxxxxx>
Subject: [PATCH] pci_get_slot()
From: Matthew Wilcox <willy@xxxxxxxxxx>
Date: Wed, 15 Oct 2003 19:32:13 +0100
Cc: Greg KH <greg@xxxxxxxxx>, "David S. Miller" <davem@xxxxxxxxxx>, Jeff Garzik <jgarzik@xxxxxxxxx>, linux-pci@xxxxxxxxxxxxxxxxxxxxxxxx, netdev@xxxxxxxxxxx, linux-kernel@xxxxxxxxxxxxxxx
Sender: netdev-bounce@xxxxxxxxxxx
User-agent: Mutt/1.4.1i
Hi Linus.

tg3.c has a bug where it can find the wrong 5704 peer on a machine with
PCI domains.  The problem is that pci_find_slot() can't distinguish
whether it has the correct domain or not.

This patch fixes that problem by introducing pci_get_slot() and converts
tg3 to use it.  It also fixes another problem where tg3 wouldn't find
a peer on function 7 (0 to <8, not 0 to <7).

Index: linux-2.6/drivers/net/tg3.c
===================================================================
RCS file: /var/cvs/linux-2.6/drivers/net/tg3.c,v
retrieving revision 1.5
diff -u -p -r1.5 tg3.c
--- linux-2.6/drivers/net/tg3.c 8 Sep 2003 21:42:12 -0000       1.5
+++ linux-2.6/drivers/net/tg3.c 15 Oct 2003 18:18:28 -0000
@@ -7462,23 +7462,24 @@ static char * __devinit tg3_phy_string(s
 
 static struct pci_dev * __devinit tg3_find_5704_peer(struct tg3 *tp)
 {
-       struct pci_dev *peer = NULL;
-       unsigned int func;
+       struct pci_dev *peer;
+       unsigned int func, devnr = tp->pdev->devfn & ~7;
 
-       for (func = 0; func < 7; func++) {
-               unsigned int devfn = tp->pdev->devfn;
-
-               devfn &= ~7;
-               devfn |= func;
-
-               if (devfn == tp->pdev->devfn)
-                       continue;
-               peer = pci_find_slot(tp->pdev->bus->number, devfn);
-               if (peer)
+       for (func = 0; func < 8; func++) {
+               peer = pci_get_slot(tp->pdev->bus, devnr | func);
+               if (peer && peer != tp->pdev)
                        break;
+               pci_dev_put(peer);
        }
        if (!peer || peer == tp->pdev)
                BUG();
+
+       /*
+        * We don't need to keep the refcount elevated; there's no way
+        * to remove one half of this device without removing the other
+        */
+       pci_dev_put(peer);
+
        return peer;
 }
 
Index: linux-2.6/drivers/pci/search.c
===================================================================
RCS file: /var/cvs/linux-2.6/drivers/pci/search.c,v
retrieving revision 1.1
diff -u -p -r1.1 search.c
--- linux-2.6/drivers/pci/search.c      29 Jul 2003 17:01:25 -0000      1.1
+++ linux-2.6/drivers/pci/search.c      15 Oct 2003 18:18:28 -0000
@@ -104,6 +104,41 @@ pci_find_slot(unsigned int bus, unsigned
 }
 
 /**
+ * pci_get_slot - locate PCI device for a given PCI slot
+ * @bus: PCI bus on which desired PCI device resides
+ * @devfn: encodes number of PCI slot in which the desired PCI 
+ * device resides and the logical device number within that slot 
+ * in case of multi-function devices.
+ *
+ * Given a PCI bus and slot/function number, the desired PCI device 
+ * is located in the list of PCI devices.
+ * If the device is found, its reference count is increased and this
+ * function returns a pointer to its data structure.  The caller must
+ * decrement the reference count by calling pci_dev_put().
+ * If no device is found, %NULL is returned.
+ */
+struct pci_dev * pci_get_slot(struct pci_bus *bus, unsigned int devfn)
+{
+       struct list_head *tmp;
+       struct pci_dev *dev;
+
+       WARN_ON(in_interrupt());
+       spin_lock(&pci_bus_lock);
+
+       list_for_each(tmp, &bus->children) {
+               dev = pci_dev_b(tmp);
+               if (dev->devfn == devfn)
+                       goto out;
+       }
+
+       dev = NULL;
+ out:
+       pci_dev_get(dev);
+       spin_unlock(&pci_bus_lock);
+       return dev;
+}
+
+/**
  * pci_find_subsys - begin or continue searching for a PCI device by 
vendor/subvendor/device/subdevice id
  * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids
  * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids
@@ -319,3 +354,4 @@ EXPORT_SYMBOL(pci_find_slot);
 EXPORT_SYMBOL(pci_find_subsys);
 EXPORT_SYMBOL(pci_get_device);
 EXPORT_SYMBOL(pci_get_subsys);
+EXPORT_SYMBOL(pci_get_slot);
Index: linux-2.6/include/linux/pci.h
===================================================================
RCS file: /var/cvs/linux-2.6/include/linux/pci.h,v
retrieving revision 1.5
diff -u -p -r1.5 pci.h
--- linux-2.6/include/linux/pci.h       8 Oct 2003 20:53:03 -0000       1.5
+++ linux-2.6/include/linux/pci.h       15 Oct 2003 18:18:34 -0000
@@ -607,6 +607,8 @@ struct pci_dev *pci_get_device (unsigned
 struct pci_dev *pci_get_subsys (unsigned int vendor, unsigned int device,
                                unsigned int ss_vendor, unsigned int ss_device,
                                struct pci_dev *from);
+struct pci_dev *pci_get_slot (struct pci_bus *bus, unsigned int devfn);
+
 int pci_bus_read_config_byte (struct pci_bus *bus, unsigned int devfn, int 
where, u8 *val);
 int pci_bus_read_config_word (struct pci_bus *bus, unsigned int devfn, int 
where, u16 *val);
 int pci_bus_read_config_dword (struct pci_bus *bus, unsigned int devfn, int 
where, u32 *val);

-- 
"It's not Hollywood.  War is real, war is primarily not about defeat or
victory, it is about death.  I've seen thousands and thousands of dead bodies.
Do you think I want to have an academic debate on this subject?" -- Robert Fisk

<Prev in Thread] Current Thread [Next in Thread>