Dynamically allocate memory for PCI devices

Instead of using a static table assuming always 2 CPU sockets, allocate
space dynamically for Nehalem PCI devs.

This patch is part of a series of patches that changes i7core_edac to
allow more than 2 sockets and to properly report one memory controller
per socket.
diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c
index af222ff..7bcb599 100644
--- a/drivers/edac/i7core_edac.c
+++ b/drivers/edac/i7core_edac.c
@@ -192,10 +192,9 @@
 };
 
 struct pci_id_descr {
-	int		dev;
-	int		func;
-	int 		dev_id;
-	struct pci_dev	*pdev[NUM_SOCKETS];
+	int			dev;
+	int			func;
+	int 			dev_id;
 };
 
 struct i7core_pvt {
@@ -229,6 +228,17 @@
 	spinlock_t		mce_lock;
 };
 
+struct i7core_dev {
+	struct list_head           list;
+
+	int socket;
+	struct pci_dev **pdev;
+};
+
+/* Static vars */
+static LIST_HEAD(i7core_edac_list);
+static DEFINE_MUTEX(i7core_edac_lock);
+
 /* Device name and register DID (Device ID) */
 struct i7core_dev_info {
 	const char *ctl_name;	/* name for this device */
@@ -240,7 +250,7 @@
 	.func = (function),			\
 	.dev_id = (device_id)
 
-struct pci_id_descr pci_devs[] = {
+struct pci_id_descr pci_dev_descr[] = {
 		/* Memory controller */
 	{ PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR)     },
 	{ PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD)  },
@@ -275,11 +285,10 @@
 	{ PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NOCORE)  },
 
 };
-#define N_DEVS ARRAY_SIZE(pci_devs)
+#define N_DEVS ARRAY_SIZE(pci_dev_descr)
 
 /*
  *	pci_device_id	table for which devices we are looking for
- * This should match the first device at pci_devs table
  */
 static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)},
@@ -288,7 +297,7 @@
 
 
 /* Table of devices attributes supported by this driver */
-static const struct i7core_dev_info i7core_devs[] = {
+static const struct i7core_dev_info i7core_probe_devs[] = {
 	{
 		.ctl_name = "i7 Core",
 		.fsb_mapping_errors = PCI_DEVICE_ID_INTEL_I7_MCR,
@@ -347,21 +356,37 @@
 	return cols[col & 0x3];
 }
 
+static struct i7core_dev *get_i7core_dev(int socket)
+{
+	struct i7core_dev *i7core_dev;
+
+	list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
+		if (i7core_dev->socket == socket)
+			return i7core_dev;
+	}
+
+	return NULL;
+}
+
 /****************************************************************************
 			Memory check routines
  ****************************************************************************/
 static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot,
 					  unsigned func)
 {
+	struct i7core_dev *i7core_dev = get_i7core_dev(socket);
 	int i;
 
+	if (!i7core_dev)
+		return NULL;
+
 	for (i = 0; i < N_DEVS; i++) {
-		if (!pci_devs[i].pdev[socket])
+		if (!i7core_dev->pdev[i])
 			continue;
 
-		if (PCI_SLOT(pci_devs[i].pdev[socket]->devfn) == slot &&
-		    PCI_FUNC(pci_devs[i].pdev[socket]->devfn) == func) {
-			return pci_devs[i].pdev[socket];
+		if (PCI_SLOT(i7core_dev->pdev[i]->devfn) == slot &&
+		    PCI_FUNC(i7core_dev->pdev[i]->devfn) == func) {
+			return i7core_dev->pdev[i];
 		}
 	}
 
@@ -1153,9 +1178,18 @@
 {
 	int i, j;
 
-	for (i = 0; i < NUM_SOCKETS; i++)
+	for (i = 0; i < NUM_SOCKETS; i++) {
+		struct i7core_dev *i7core_dev = get_i7core_dev(i);
+		if (!i7core_dev)
+			continue;
+
 		for (j = 0; j < N_DEVS; j++)
-			pci_dev_put(pci_devs[j].pdev[i]);
+			pci_dev_put(i7core_dev->pdev[j]);
+
+		list_del(&i7core_dev->list);
+		kfree(i7core_dev->pdev);
+		kfree(i7core_dev);
+	}
 }
 
 static void i7core_xeon_pci_fixup(void)
@@ -1168,7 +1202,7 @@
 	 * to detect them
 	 */
 	pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
-			      pci_devs[0].dev_id, NULL);
+			      pci_dev_descr[0].dev_id, NULL);
 	if (unlikely(!pdev)) {
 		for (i = 0; i < NUM_SOCKETS; i ++)
 			pcibios_scan_specific_bus(255-i);
@@ -1183,19 +1217,21 @@
  */
 int i7core_get_onedevice(struct pci_dev **prev, int devno)
 {
+	struct i7core_dev *i7core_dev;
+
 	struct pci_dev *pdev = NULL;
 	u8 bus = 0;
 	u8 socket = 0;
 
 	pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
-			      pci_devs[devno].dev_id, *prev);
+			      pci_dev_descr[devno].dev_id, *prev);
 
 	/*
 	 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
 	 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
 	 * to probe for the alternate address in case of failure
 	 */
-	if (pci_devs[devno].dev_id == PCI_DEVICE_ID_INTEL_I7_NOCORE && !pdev)
+	if (pci_dev_descr[devno].dev_id == PCI_DEVICE_ID_INTEL_I7_NOCORE && !pdev)
 		pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
 				      PCI_DEVICE_ID_INTEL_I7_NOCORE_ALT, *prev);
 
@@ -1209,15 +1245,15 @@
 		 * Dev 3 function 2 only exists on chips with RDIMMs
 		 * so, it is ok to not found it
 		 */
-		if ((pci_devs[devno].dev == 3) && (pci_devs[devno].func == 2)) {
+		if ((pci_dev_descr[devno].dev == 3) && (pci_dev_descr[devno].func == 2)) {
 			*prev = pdev;
 			return 0;
 		}
 
 		i7core_printk(KERN_ERR,
 			"Device not found: dev %02x.%d PCI ID %04x:%04x\n",
-			pci_devs[devno].dev, pci_devs[devno].func,
-			PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
+			pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
+			PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
 
 		/* End of list, leave */
 		return -ENODEV;
@@ -1229,37 +1265,40 @@
 	else
 		socket = 255 - bus;
 
-	if (socket >= NUM_SOCKETS) {
-		i7core_printk(KERN_ERR,
-			"Unexpected socket for "
-			"dev %02x:%02x.%d PCI ID %04x:%04x\n",
-			bus, pci_devs[devno].dev, pci_devs[devno].func,
-			PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
-		pci_dev_put(pdev);
-		return -ENODEV;
+	i7core_dev = get_i7core_dev(socket);
+	if (!i7core_dev) {
+		i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL);
+		if (!i7core_dev)
+			return -ENOMEM;
+		i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * N_DEVS,
+					   GFP_KERNEL);
+		if (!i7core_dev->pdev)
+			return -ENOMEM;
+		i7core_dev->socket = socket;
+		list_add_tail(&i7core_dev->list, &i7core_edac_list);
 	}
 
-	if (pci_devs[devno].pdev[socket]) {
+	if (i7core_dev->pdev[devno]) {
 		i7core_printk(KERN_ERR,
 			"Duplicated device for "
 			"dev %02x:%02x.%d PCI ID %04x:%04x\n",
-			bus, pci_devs[devno].dev, pci_devs[devno].func,
-			PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
+			bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
+			PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
 		pci_dev_put(pdev);
 		return -ENODEV;
 	}
 
-	pci_devs[devno].pdev[socket] = pdev;
+	i7core_dev->pdev[devno] = pdev;
 
 	/* Sanity check */
-	if (unlikely(PCI_SLOT(pdev->devfn) != pci_devs[devno].dev ||
-			PCI_FUNC(pdev->devfn) != pci_devs[devno].func)) {
+	if (unlikely(PCI_SLOT(pdev->devfn) != pci_dev_descr[devno].dev ||
+			PCI_FUNC(pdev->devfn) != pci_dev_descr[devno].func)) {
 		i7core_printk(KERN_ERR,
 			"Device PCI ID %04x:%04x "
 			"has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
-			PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id,
+			PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id,
 			bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
-			bus, pci_devs[devno].dev, pci_devs[devno].func);
+			bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func);
 		return -ENODEV;
 	}
 
@@ -1268,27 +1307,29 @@
 		i7core_printk(KERN_ERR,
 			"Couldn't enable "
 			"dev %02x:%02x.%d PCI ID %04x:%04x\n",
-			bus, pci_devs[devno].dev, pci_devs[devno].func,
-			PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
+			bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
+			PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
 		return -ENODEV;
 	}
 
 	i7core_printk(KERN_INFO,
 			"Registered socket %d "
 			"dev %02x:%02x.%d PCI ID %04x:%04x\n",
-			socket, bus, pci_devs[devno].dev, pci_devs[devno].func,
-			PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
+			socket, bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
+			PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
 
 	*prev = pdev;
 
 	return 0;
 }
 
-static int i7core_get_devices(void)
+static int i7core_get_devices(u8 *sockets)
 {
 	int i;
 	struct pci_dev *pdev = NULL;
+	struct i7core_dev *i7core_dev = NULL;
 
+	*sockets = 0;
 	for (i = 0; i < N_DEVS; i++) {
 		pdev = NULL;
 		do {
@@ -1298,6 +1339,12 @@
 			}
 		} while (pdev);
 	}
+
+	list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
+		if (i7core_dev->socket + 1 > *sockets)
+			*sockets = i7core_dev->socket + 1;
+	}
+
 	return 0;
 }
 
@@ -1307,11 +1354,15 @@
 	struct pci_dev *pdev;
 	int i, j, func, slot;
 
-
 	for (i = 0; i < pvt->sockets; i++) {
+		struct i7core_dev *i7core_dev = get_i7core_dev(i);
+
+		if (!i7core_dev)
+			continue;
+
 		pvt->is_registered[i] = 0;
 		for (j = 0; j < N_DEVS; j++) {
-			pdev = pci_devs[j].pdev[i];
+			pdev = i7core_dev->pdev[j];
 			if (!pdev)
 				continue;
 
@@ -1723,20 +1774,17 @@
 	int rc, i;
 	u8 sockets;
 
-	if (unlikely(dev_idx >= ARRAY_SIZE(i7core_devs)))
+	/*
+	 * FIXME: All memory controllers are allocated at the first pass.
+	 */
+	if (unlikely(dev_idx >= 1))
 		return -EINVAL;
 
 	/* get the pci devices we want to reserve for our use */
-	rc = i7core_get_devices();
+	mutex_lock(&i7core_edac_lock);
+	rc = i7core_get_devices(&sockets);
 	if (unlikely(rc < 0))
-		return rc;
-
-	sockets = 1;
-	for (i = NUM_SOCKETS - 1; i > 0; i--)
-		if (pci_devs[0].pdev[i]) {
-			sockets = i + 1;
-			break;
-		}
+		goto fail0;
 
 	for (i = 0; i < sockets; i++) {
 		int channels;
@@ -1745,7 +1793,7 @@
 		/* Check the number of active and not disabled channels */
 		rc = i7core_get_active_channels(i, &channels, &csrows);
 		if (unlikely(rc < 0))
-			goto fail0;
+			goto fail1;
 
 		num_channels += channels;
 		num_csrows += csrows;
@@ -1755,7 +1803,7 @@
 	mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0);
 	if (unlikely(!mci)) {
 		rc = -ENOMEM;
-		goto fail0;
+		goto fail1;
 	}
 
 	debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
@@ -1776,7 +1824,7 @@
 	mci->edac_cap = EDAC_FLAG_NONE;
 	mci->mod_name = "i7core_edac.c";
 	mci->mod_ver = I7CORE_REVISION;
-	mci->ctl_name = i7core_devs[dev_idx].ctl_name;
+	mci->ctl_name = i7core_probe_devs[dev_idx].ctl_name;
 	mci->dev_name = pci_name(pdev);
 	mci->ctl_page_to_phys = NULL;
 	mci->mc_driver_sysfs_attributes = i7core_inj_attrs;
@@ -1786,7 +1834,7 @@
 	/* Store pci devices at mci for faster access */
 	rc = mci_bind_devs(mci);
 	if (unlikely(rc < 0))
-		goto fail1;
+		goto fail2;
 
 	/* Get dimm basic config */
 	for (i = 0; i < sockets; i++)
@@ -1801,7 +1849,7 @@
 		 */
 
 		rc = -EINVAL;
-		goto fail1;
+		goto fail2;
 	}
 
 	/* allocating generic PCI control info */
@@ -1832,18 +1880,21 @@
 	if (unlikely(rc < 0)) {
 		debugf0("MC: " __FILE__
 			": %s(): failed edac_mce_register()\n", __func__);
-		goto fail1;
+		goto fail2;
 	}
 
 	i7core_printk(KERN_INFO, "Driver loaded.\n");
 
+	mutex_unlock(&i7core_edac_lock);
 	return 0;
 
-fail1:
+fail2:
 	edac_mc_free(mci);
 
-fail0:
+fail1:
 	i7core_put_devices();
+fail0:
+	mutex_unlock(&i7core_edac_lock);
 	return rc;
 }
 
@@ -1871,7 +1922,9 @@
 	edac_mce_unregister(&pvt->edac_mce);
 
 	/* retrieve references to resources, and free those resources */
+	mutex_lock(&i7core_edac_lock);
 	i7core_put_devices();
+	mutex_unlock(&i7core_edac_lock);
 
 	edac_mc_free(mci);
 }