FreeBSD/sys/dev/pci/pci.c
/*
 * By default, pci_alloc_msix() will assign the allocated IRQ
 * resources consecutively to the first N messages in the MSI-X table.
 * However, device drivers may want to use different layouts if they
 * either receive fewer messages than they asked for, or they wish to
 * populate the MSI-X table sparsely.  This method allows the driver
 * to specify what layout it wants.  It must be called after a
 * successful pci_alloc_msix() but before any of the associated
 * SYS_RES_IRQ resources are allocated via bus_alloc_resource().
 *
 * The 'vectors' array contains 'count' message vectors.  The array
 * maps directly to the MSI-X table in that index 0 in the array
 * specifies the vector for the first message in the MSI-X table, etc.
 * The vector value in each array index can either be 0 to indicate
 * that no vector should be assigned to a message slot, or it can be a
 * number from 1 to N (where N is the count returned from a
 * succcessful call to pci_alloc_msix()) to indicate which message
 * vector (IRQ) to be used for the corresponding message.
 *
 * On successful return, each message with a non-zero vector will have
 * an associated SYS_RES_IRQ whose rid is equal to the array index +
 * 1.  Additionally, if any of the IRQs allocated via the previous
 * call to pci_alloc_msix() are not used in the mapping, those IRQs
 * will be freed back to the system automatically.
 *
 * For example, suppose a driver has a MSI-X table with 6 messages and
 * asks for 6 messages, but pci_alloc_msix() only returns a count of
 * 3.  Call the three vectors allocated by pci_alloc_msix() A, B, and
 * C.  After the call to pci_alloc_msix(), the device will be setup to
 * have an MSI-X table of ABC--- (where - means no vector assigned).
 * If the driver then passes a vector array of { 1, 0, 1, 2, 0, 2 },
 * then the MSI-X table will look like A-AB-B, and the 'C' vector will
 * be freed back to the system.  This device will also have valid
 * SYS_RES_IRQ rids of 1, 3, 4, and 6.
 *
 * In any case, the SYS_RES_IRQ rid X will always map to the message
 * at MSI-X table index X - 1 and will only be valid if a vector is
 * assigned to that table entry.
 */
By default, pci_alloc_msix() will assign the allocated IRQ
resources consecutively to the first N messages in the MSI-X table.
However, device drivers may want to use different layouts if they
either receive fewer messages than they asked for, or they wish to
populate the MSI-X table sparsely.  This method allows the driver
to specify what layout it wants.  It must be called after a
successful pci_alloc_msix() but before any of the associated
SYS_RES_IRQ resources are allocated via bus_alloc_resource().

The 'vectors' array contains 'count' message vectors.  The array
maps directly to the MSI-X table in that index 0 in the array
specifies the vector for the first message in the MSI-X table, etc.
The vector value in each array index can either be 0 to indicate
that no vector should be assigned to a message slot, or it can be a
number from 1 to N (where N is the count returned from a
succcessful call to pci_alloc_msix()) to indicate which message
vector (IRQ) to be used for the corresponding message.

On successful return, each message with a non-zero vector will have
an associated SYS_RES_IRQ whose rid is equal to the array index +
1.  Additionally, if any of the IRQs allocated via the previous
call to pci_alloc_msix() are not used in the mapping, those IRQs
will be freed back to the system automatically.

For example, suppose a driver has a MSI-X table with 6 messages and
asks for 6 messages, but pci_alloc_msix() only returns a count of
3.  Call the three vectors allocated by pci_alloc_msix() A, B, and
C.

In any case, the SYS_RES_IRQ rid X will always map to the message
at MSI-X table index X - 1 and will only be valid if a vector is
assigned to that table entry.
By default pci_alloc_msix () will assign the allocated IRQ resources consecutively to the first N messages in the MSI - X table . However device drivers may want to use different layouts if they either receive fewer messages than they asked for or they wish to populate the MSI - X table sparsely . This method allows the driver to specify what layout it wants . It must be called after a successful pci_alloc_msix () but before any of the associated SYS_RES_IRQ resources are allocated via bus_alloc_resource () . The ' vectors ' array contains ' count ' message vectors . The array maps directly to the MSI - X table in that index 0 in the array specifies the vector for the first message in the MSI - X table etc . The vector value in each array index can either be 0 to indicate that no vector should be assigned to a message slot or it can be a number from 1 to N ( where N is the count returned from a succcessful call to pci_alloc_msix () ) to indicate which message vector ( IRQ ) to be used for the corresponding message . On successful return each message with a non - zero vector will have an associated SYS_RES_IRQ whose rid is equal to the array index + 1 . Additionally if any of the IRQs allocated via the previous call to pci_alloc_msix () are not used in the mapping those IRQs will be freed back to the system automatically . For example suppose a driver has a MSI - X table with 6 messages and asks for 6 messages but pci_alloc_msix () only returns a count of 3 . Call the three vectors allocated by pci_alloc_msix () A B and C . In any case the SYS_RES_IRQ rid X will always map to the message at MSI - X table index X - 1 and will only be valid if a vector is assigned to that table entry .
int
pci_remap_msix_method(device_t dev, device_t child, int count,
    const u_int *vectors)
{
	struct pci_devinfo *dinfo = device_get_ivars(child);
	struct pcicfg_msix *msix = &dinfo->cfg.msix;
	struct resource_list_entry *rle;
	int i, irq, j, *used;
	if (count == 0 || count > msix->msix_msgnum)
		return (EINVAL);
	for (i = 0; i < count; i++)
		if (vectors[i] > msix->msix_alloc)
			return (EINVAL);
	used = malloc(sizeof(int) * msix->msix_alloc, M_DEVBUF, M_WAITOK |
	    M_ZERO);
	for (i = 0; i < count; i++)
		if (vectors[i] != 0)
			used[vectors[i] - 1] = 1;
	for (i = 0; i < msix->msix_alloc - 1; i++)
		if (used[i] == 0 && used[i + 1] == 1) {
			free(used, M_DEVBUF);
			return (EINVAL);
		}
	if (used[0] != 1) {
		free(used, M_DEVBUF);
		return (EINVAL);
	}
	for (i = 0; i < msix->msix_table_len; i++) {
		if (msix->msix_table[i].mte_vector == 0)
			continue;
		if (msix->msix_table[i].mte_handlers > 0) {
			free(used, M_DEVBUF);
			return (EBUSY);
		}
		rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, i + 1);
		KASSERT(rle != NULL, ("missing resource"));
		if (rle->res != NULL) {
			free(used, M_DEVBUF);
			return (EBUSY);
		}
	}
	for (i = 0; i < msix->msix_table_len; i++) {
		if (msix->msix_table[i].mte_vector == 0)
			continue;
		resource_list_delete(&dinfo->resources, SYS_RES_IRQ, i + 1);
	}
	free(msix->msix_table, M_DEVBUF);
	msix->msix_table = malloc(sizeof(struct msix_table_entry) * count,
	    M_DEVBUF, M_WAITOK | M_ZERO);
	for (i = 0; i < count; i++)
		msix->msix_table[i].mte_vector = vectors[i];
	msix->msix_table_len = count;
	j = msix->msix_alloc - 1;
	if (used[j] == 0) {
		struct msix_vector *vec;
		while (used[j] == 0) {
			PCIB_RELEASE_MSIX(device_get_parent(dev), child,
			    msix->msix_vectors[j].mv_irq);
			j--;
		}
		vec = malloc(sizeof(struct msix_vector) * (j + 1), M_DEVBUF,
		    M_WAITOK);
		bcopy(msix->msix_vectors, vec, sizeof(struct msix_vector) *
		    (j + 1));
		free(msix->msix_vectors, M_DEVBUF);
		msix->msix_vectors = vec;
		msix->msix_alloc = j + 1;
	}
	free(used, M_DEVBUF);
	for (i = 0; i < count; i++) {
		if (vectors[i] == 0)
			continue;
		irq = msix->msix_vectors[vectors[i] - 1].mv_irq;
		resource_list_add(&dinfo->resources, SYS_RES_IRQ, i + 1, irq,
		    irq, 1);
	}
	if (bootverbose) {
		device_printf(child, "Remapped MSI-X IRQs as: ");
		for (i = 0; i < count; i++) {
			if (i != 0)
				printf(", ");
			if (vectors[i] == 0)
				printf("---");
			else
				printf("%d",
				    msix->msix_vectors[vectors[i] - 1].mv_irq);
		}
		printf("\n");
	}
	return (0);
}
int pci_remap_msix_method ( device_t dev , device_t child , int count , const u_int * vectors ) { struct pci_devinfo * dinfo = device_get_ivars ( child ) ; struct pcicfg_msix * msix = & dinfo -> cfg . msix ; struct resource_list_entry * rle ; int i , irq , j , * used ; if ( count == 0 || count > msix -> msix_msgnum ) return ( EINVAL ) ; for ( i = 0 ; i < count ; i ++ ) if ( vectors [ i ] > msix -> msix_alloc ) return ( EINVAL ) ; used = malloc ( sizeof ( int ) * msix -> msix_alloc , M_DEVBUF , M_WAITOK | M_ZERO ) ; for ( i = 0 ; i < count ; i ++ ) if ( vectors [ i ] != 0 ) used [ vectors [ i ] - 1 ] = 1 ; for ( i = 0 ; i < msix -> msix_alloc - 1 ; i ++ ) if ( used [ i ] == 0 && used [ i + 1 ] == 1 ) { free ( used , M_DEVBUF ) ; return ( EINVAL ) ; } if ( used [ 0 ] != 1 ) { free ( used , M_DEVBUF ) ; return ( EINVAL ) ; } for ( i = 0 ; i < msix -> msix_table_len ; i ++ ) { if ( msix -> msix_table [ i ] . mte_vector == 0 ) continue ; if ( msix -> msix_table [ i ] . mte_handlers > 0 ) { free ( used , M_DEVBUF ) ; return ( EBUSY ) ; } rle = resource_list_find ( & dinfo -> resources , SYS_RES_IRQ , i + 1 ) ; KASSERT ( rle != NULL , ( " " ) ) ; if ( rle -> res != NULL ) { free ( used , M_DEVBUF ) ; return ( EBUSY ) ; } } for ( i = 0 ; i < msix -> msix_table_len ; i ++ ) { if ( msix -> msix_table [ i ] . mte_vector == 0 ) continue ; resource_list_delete ( & dinfo -> resources , SYS_RES_IRQ , i + 1 ) ; } free ( msix -> msix_table , M_DEVBUF ) ; msix -> msix_table = malloc ( sizeof ( struct msix_table_entry ) * count , M_DEVBUF , M_WAITOK | M_ZERO ) ; for ( i = 0 ; i < count ; i ++ ) msix -> msix_table [ i ] . mte_vector = vectors [ i ] ; msix -> msix_table_len = count ; j = msix -> msix_alloc - 1 ; if ( used [ j ] == 0 ) { struct msix_vector * vec ; while ( used [ j ] == 0 ) { PCIB_RELEASE_MSIX ( device_get_parent ( dev ) , child , msix -> msix_vectors [ j ] . mv_irq ) ; j -- ; } vec = malloc ( sizeof ( struct msix_vector ) * ( j + 1 ) , M_DEVBUF , M_WAITOK ) ; bcopy ( msix -> msix_vectors , vec , sizeof ( struct msix_vector ) * ( j + 1 ) ) ; free ( msix -> msix_vectors , M_DEVBUF ) ; msix -> msix_vectors = vec ; msix -> msix_alloc = j + 1 ; } free ( used , M_DEVBUF ) ; for ( i = 0 ; i < count ; i ++ ) { if ( vectors [ i ] == 0 ) continue ; irq = msix -> msix_vectors [ vectors [ i ] - 1 ] . mv_irq ; resource_list_add ( & dinfo -> resources , SYS_RES_IRQ , i + 1 , irq , irq , 1 ) ; } if ( bootverbose ) { device_printf ( child , " " ) ; for ( i = 0 ; i < count ; i ++ ) { if ( i != 0 ) printf ( " " ) ; if ( vectors [ i ] == 0 ) printf ( " " ) ; else printf ( " " , msix -> msix_vectors [ vectors [ i ] - 1 ] . mv_irq ) ; } printf ( " \n " ) ; } return ( 0 ) ; }
By default, pci_alloc_msix() will assign the allocated IRQ
 resources consecutively to the first N messages in the MSI-X table.
By default pci_alloc_msix () will assign the allocated IRQ resources consecutively to the first N messages in the MSI - X table .
/*
	 * Have to have at least one message in the table but the
	 * table can't be bigger than the actual MSI-X table in the
	 * device.
	 */ /* Sanity check the vectors. */ /*
	 * Make sure there aren't any holes in the vectors to be used.
	 * It's a big pain to support it, and it doesn't really make
	 * sense anyway.  Also, at least one vector must be used.
	 */ /* Make sure none of the resources are allocated. */ /* Free the existing resource list entries. */ /*
	 * Build the new virtual table keeping track of which vectors are
	 * used.
	 */ /* Free any unused IRQs and resize the vectors array if necessary. */ /* Map the IRQs onto the rids. */