예제 #1
0
/**
 * pci_enable_msix - configure device's MSI-X capability structure
 * @dev: pointer to the pci_dev data structure of MSI-X device function
 * @entries: pointer to an array of MSI-X entries
 * @nvec: number of MSI-X vectors requested for allocation by device driver
 *
 * Setup the MSI-X capability structure of device function with the number
 * of requested vectors upon its software driver call to request for
 * MSI-X mode enabled on its hardware device function. A return of zero
 * indicates the successful configuration of MSI-X capability structure
 * with new allocated MSI-X vectors. A return of < 0 indicates a failure.
 * Or a return of > 0 indicates that driver request is exceeding the number
 * of vectors available. Driver should use the returned value to re-send
 * its request.
 **/
int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec)
{
	int status, pos, nr_entries, free_vectors;
	int i, j, temp;
	u16 control;
	unsigned long flags;

	if (!entries || pci_msi_supported(dev) < 0)
 		return -EINVAL;

	status = msi_init();
	if (status < 0)
		return status;

	pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
	if (!pos)
 		return -EINVAL;

	pci_read_config_word(dev, msi_control_reg(pos), &control);
	nr_entries = multi_msix_capable(control);
	if (nvec > nr_entries)
		return -EINVAL;

	/* Check for any invalid entries */
	for (i = 0; i < nvec; i++) {
		if (entries[i].entry >= nr_entries)
			return -EINVAL;		/* invalid entry */
		for (j = i + 1; j < nvec; j++) {
			if (entries[i].entry == entries[j].entry)
				return -EINVAL;	/* duplicate entry */
		}
	}
	temp = dev->irq;
	WARN_ON(!msi_lookup_vector(dev, PCI_CAP_ID_MSIX));

	/* Check whether driver already requested for MSI vector */
   	if (pci_find_capability(dev, PCI_CAP_ID_MSI) > 0 &&
		!msi_lookup_vector(dev, PCI_CAP_ID_MSI)) {
		printk(KERN_INFO "PCI: %s: Can't enable MSI-X.  "
		       "Device already has an MSI vector assigned\n",
		       pci_name(dev));
		dev->irq = temp;
		return -EINVAL;
	}

	spin_lock_irqsave(&msi_lock, flags);
	/*
	 * msi_lock is provided to ensure that enough vectors resources are
	 * available before granting.
	 */
	free_vectors = pci_vector_resources(last_alloc_vector,
				nr_released_vectors);
	/* Ensure that each MSI/MSI-X device has one vector reserved by
	   default to avoid any MSI-X driver to take all available
 	   resources */
	free_vectors -= nr_reserved_vectors;
	spin_unlock_irqrestore(&msi_lock, flags);

	if (nvec > free_vectors) {
		if (free_vectors > 0)
			return free_vectors;
		else
			return -EBUSY;
	}

	status = msix_capability_init(dev, entries, nvec);

	return status;
}
예제 #2
0
파일: msi.c 프로젝트: rcplay/snake-os
/**
 * pci_enable_msix - configure device's MSI-X capability structure
 * @dev: pointer to the pci_dev data structure of MSI-X device function
 * @entries: pointer to an array of MSI-X entries
 * @nvec: number of MSI-X vectors requested for allocation by device driver
 *
 * Setup the MSI-X capability structure of device function with the number
 * of requested vectors upon its software driver call to request for
 * MSI-X mode enabled on its hardware device function. A return of zero
 * indicates the successful configuration of MSI-X capability structure
 * with new allocated MSI-X vectors. A return of < 0 indicates a failure.
 * Or a return of > 0 indicates that driver request is exceeding the number
 * of vectors available. Driver should use the returned value to re-send
 * its request.
 **/
int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec)
{
    int status, pos, nr_entries, free_vectors;
    int i, j, temp;
    u16 control;
    unsigned long flags;

    if (!pci_msi_enable || !dev || !entries)
        return -EINVAL;

    if ((status = msi_init()) < 0)
        return status;

    if (!(pos = pci_find_capability(dev, PCI_CAP_ID_MSIX)))
        return -EINVAL;

    pci_read_config_word(dev, msi_control_reg(pos), &control);
    if (control & PCI_MSIX_FLAGS_ENABLE)
        return -EINVAL;			/* Already in MSI-X mode */

    nr_entries = multi_msix_capable(control);
    if (nvec > nr_entries)
        return -EINVAL;

    /* Check for any invalid entries */
    for (i = 0; i < nvec; i++) {
        if (entries[i].entry >= nr_entries)
            return -EINVAL;		/* invalid entry */
        for (j = i + 1; j < nvec; j++) {
            if (entries[i].entry == entries[j].entry)
                return -EINVAL;	/* duplicate entry */
        }
    }
    temp = dev->irq;
    if (!msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) {
        /* Lookup Sucess */
        nr_entries = nvec;
        /* Reroute MSI-X table */
        if (reroute_msix_table(dev->irq, entries, &nr_entries)) {
            /* #requested > #previous-assigned */
            dev->irq = temp;
            return nr_entries;
        }
        dev->irq = temp;
        enable_msi_mode(dev, pos, PCI_CAP_ID_MSIX);
        return 0;
    }
    /* Check whether driver already requested for MSI vector */
    if (pci_find_capability(dev, PCI_CAP_ID_MSI) > 0 &&
            !msi_lookup_vector(dev, PCI_CAP_ID_MSI)) {
        printk(KERN_INFO "PCI: %s: Can't enable MSI-X.  "
               "Device already has an MSI vector assigned\n",
               pci_name(dev));
        dev->irq = temp;
        return -EINVAL;
    }

    spin_lock_irqsave(&msi_lock, flags);
    /*
     * msi_lock is provided to ensure that enough vectors resources are
     * available before granting.
     */
    free_vectors = pci_vector_resources(last_alloc_vector,
                                        nr_released_vectors);
    /* Ensure that each MSI/MSI-X device has one vector reserved by
       default to avoid any MSI-X driver to take all available
       resources */
    free_vectors -= nr_reserved_vectors;
    /* Find the average of free vectors among MSI-X devices */
    if (nr_msix_devices > 0)
        free_vectors /= nr_msix_devices;
    spin_unlock_irqrestore(&msi_lock, flags);

    if (nvec > free_vectors) {
        if (free_vectors > 0)
            return free_vectors;
        else
            return -EBUSY;
    }

    status = msix_capability_init(dev, entries, nvec);
    if (!status && nr_msix_devices > 0)
        nr_msix_devices--;

    return status;
}