static int __xen_pcibk_add_pci_dev()

in xen-pciback/vpci.c [69:148]


static int __xen_pcibk_add_pci_dev(struct xen_pcibk_device *pdev,
				   struct pci_dev *dev, int devid,
				   publish_pci_dev_cb publish_cb)
{
	int err = 0, slot, func = PCI_FUNC(dev->devfn);
	struct pci_dev_entry *t, *dev_entry;
	struct vpci_dev_data *vpci_dev = pdev->pci_dev_data;

	if ((dev->class >> 24) == PCI_BASE_CLASS_BRIDGE) {
		err = -EFAULT;
		xenbus_dev_fatal(pdev->xdev, err,
				 "Can't export bridges on the virtual PCI bus");
		goto out;
	}

	dev_entry = kmalloc(sizeof(*dev_entry), GFP_KERNEL);
	if (!dev_entry) {
		err = -ENOMEM;
		xenbus_dev_fatal(pdev->xdev, err,
				 "Error adding entry to virtual PCI bus");
		goto out;
	}

	dev_entry->dev = dev;

	mutex_lock(&vpci_dev->lock);

	/*
	 * Keep multi-function devices together on the virtual PCI bus, except
	 * that we want to keep virtual functions at func 0 on their own. They
	 * aren't multi-function devices and hence their presence at func 0
	 * may cause guests to not scan the other functions.
	 */
	if (!dev->is_virtfn || func) {
		for (slot = 0; slot < PCI_SLOT_MAX; slot++) {
			if (list_empty(&vpci_dev->dev_list[slot]))
				continue;

			t = list_entry(list_first(&vpci_dev->dev_list[slot]),
				       struct pci_dev_entry, list);
			if (t->dev->is_virtfn && !PCI_FUNC(t->dev->devfn))
				continue;

			if (match_slot(dev, t->dev)) {
				dev_info(&dev->dev, "vpci: assign to virtual slot %d func %d\n",
					 slot, func);
				list_add_tail(&dev_entry->list,
					      &vpci_dev->dev_list[slot]);
				goto unlock;
			}
		}
	}

	/* Assign to a new slot on the virtual PCI bus */
	for (slot = 0; slot < PCI_SLOT_MAX; slot++) {
		if (list_empty(&vpci_dev->dev_list[slot])) {
			dev_info(&dev->dev, "vpci: assign to virtual slot %d\n",
				 slot);
			list_add_tail(&dev_entry->list,
				      &vpci_dev->dev_list[slot]);
			goto unlock;
		}
	}

	err = -ENOMEM;
	xenbus_dev_fatal(pdev->xdev, err,
			 "No more space on root virtual PCI bus");

unlock:
	mutex_unlock(&vpci_dev->lock);

	/* Publish this device. */
	if (!err)
		err = publish_cb(pdev, 0, 0, PCI_DEVFN(slot, func), devid);
	else
		kfree(dev_entry);

out:
	return err;
}