]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[ena] Map the on-device memory, if present
authorMichael Brown <mcb30@ipxe.org>
Wed, 15 Oct 2025 14:27:03 +0000 (15:27 +0100)
committerMichael Brown <mcb30@ipxe.org>
Wed, 15 Oct 2025 14:55:57 +0000 (15:55 +0100)
Newer generations of the ENA hardware require the use of low latency
transmit queues, where the submission queues and the initial portion
of the transmitted packet are written to on-device memory via BAR2
instead of being read from host memory.

Prepare for this by mapping the on-device memory BAR.  As with the
register BAR, we may need to steal a base address from the upstream
PCI bridge since the BIOS on some instance types (observed with an
m8i.metal-48xl instance in eu-south-2) will fail to assign an address
to the device.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/drivers/net/ena.c
src/drivers/net/ena.h

index 7f6e8164faaa341bcf72372ed4b381d29ce5731f..51375e47f341733bebe1a5ef61751b5ff34ca1a2 100644 (file)
@@ -1095,10 +1095,12 @@ static struct net_device_operations ena_operations = {
  */
 
 /**
- * Assign memory BAR
+ * Assign memory BARs
  *
  * @v ena              ENA device
  * @v pci              PCI device
+ * @v prefmembase      On-device memory base address to fill in
+ * @v prefmemsize      On-device memory size to fill in
  * @ret rc             Return status code
  *
  * Some BIOSes in AWS EC2 are observed to fail to assign a base
@@ -1106,15 +1108,27 @@ static struct net_device_operations ena_operations = {
  * its bridge, and the BIOS does assign a memory window to the bridge.
  * We therefore place the device at the start of the memory window.
  */
-static int ena_membase ( struct ena_nic *ena, struct pci_device *pci ) {
+static int ena_membases ( struct ena_nic *ena, struct pci_device *pci,
+                         unsigned long *prefmembase,
+                         unsigned long *prefmemsize ) {
        struct pci_bridge *bridge;
 
+       /* Get on-device memory base address and size */
+       *prefmembase = pci_bar_start ( pci, ENA_MEM_BAR );
+       *prefmemsize = pci_bar_size ( pci, ENA_MEM_BAR );
+
+       /* Do nothing if addresses are already assigned */
+       if ( pci->membase && ( *prefmembase || ( ! *prefmemsize ) ) )
+               return 0;
+
        /* Locate PCI bridge */
        bridge = pcibridge_find ( pci );
        if ( ! bridge ) {
                DBGC ( ena, "ENA %p found no PCI bridge\n", ena );
                return -ENOTCONN;
        }
+       DBGC ( ena, "ENA %p at " PCI_FMT " claiming bridge " PCI_FMT "\n",
+              ena, PCI_ARGS ( pci ), PCI_ARGS ( bridge->pci ) );
 
        /* Sanity check */
        if ( PCI_SLOT ( pci->busdevfn ) || PCI_FUNC ( pci->busdevfn ) ) {
@@ -1123,12 +1137,21 @@ static int ena_membase ( struct ena_nic *ena, struct pci_device *pci ) {
                return -ENOTSUP;
        }
 
-       /* Place device at start of memory window */
-       pci_bar_set ( pci, PCI_BASE_ADDRESS_0, bridge->membase );
-       pci->membase = bridge->membase;
-       DBGC ( ena, "ENA %p at " PCI_FMT " claiming bridge " PCI_FMT " mem "
-              "%08x\n", ena, PCI_ARGS ( pci ), PCI_ARGS ( bridge->pci ),
-              bridge->membase );
+       /* Place register BAR at start of memory window, if applicable */
+       if ( ! pci->membase ) {
+               pci_bar_set ( pci, ENA_REGS_BAR, bridge->membase );
+               pci->membase = bridge->membase;
+               DBGC ( ena, "ENA %p at " PCI_FMT " claiming mem %08lx\n",
+                      ena, PCI_ARGS ( pci ), pci->membase );
+       }
+
+       /* Place memory BAR at start of prefetchable window, if applicable */
+       if ( *prefmemsize && ( ! *prefmembase ) ) {
+               pci_bar_set ( pci, ENA_MEM_BAR, bridge->prefmembase );
+               *prefmembase = bridge->prefmembase;
+               DBGC ( ena, "ENA %p at " PCI_FMT " claiming prefmem %08lx\n",
+                      ena, PCI_ARGS ( pci ), *prefmembase );
+       }
 
        return 0;
 }
@@ -1143,6 +1166,8 @@ static int ena_probe ( struct pci_device *pci ) {
        struct net_device *netdev;
        struct ena_nic *ena;
        struct ena_host_info *info;
+       unsigned long prefmembase;
+       unsigned long prefmemsize;
        int rc;
 
        /* Allocate and initialise net device */
@@ -1170,14 +1195,27 @@ static int ena_probe ( struct pci_device *pci ) {
        adjust_pci_device ( pci );
 
        /* Fix up PCI BAR if left unassigned by BIOS */
-       if ( ( ! pci->membase ) && ( ( rc = ena_membase ( ena, pci ) ) != 0 ) )
-               goto err_membase;
+       if ( ( rc = ena_membases ( ena, pci, &prefmembase,
+                                  &prefmemsize ) ) != 0 ) {
+               goto err_membases;
+       }
 
        /* Map registers */
-       ena->regs = pci_ioremap ( pci, pci->membase, ENA_BAR_SIZE );
+       ena->regs = pci_ioremap ( pci, pci->membase, ENA_REGS_SIZE );
        if ( ! ena->regs ) {
                rc = -ENODEV;
-               goto err_ioremap;
+               goto err_regs;
+       }
+
+       /* Map device memory */
+       if ( prefmemsize ) {
+               ena->mem = pci_ioremap ( pci, prefmembase, prefmemsize );
+               if ( ! ena->mem ) {
+                       rc = -ENODEV;
+                       goto err_mem;
+               }
+               DBGC ( ena, "ENA %p has %ldkB of on-device memory\n",
+                      ena, ( prefmemsize >> 10 ) );
        }
 
        /* Allocate and initialise host info */
@@ -1242,9 +1280,12 @@ static int ena_probe ( struct pci_device *pci ) {
  err_reset:
        free_phys ( ena->info, PAGE_SIZE );
  err_info:
+       if ( ena->mem )
+               iounmap ( ena->mem );
+ err_mem:
        iounmap ( ena->regs );
- err_ioremap:
- err_membase:
+ err_regs:
+ err_membases:
        netdev_nullify ( netdev );
        netdev_put ( netdev );
  err_alloc:
@@ -1275,8 +1316,12 @@ static void ena_remove ( struct pci_device *pci ) {
        /* Free host info */
        free_phys ( ena->info, PAGE_SIZE );
 
-       /* Free network device */
+       /* Unmap registers and on-device memory */
+       if ( ena->mem )
+               iounmap ( ena->mem );
        iounmap ( ena->regs );
+
+       /* Free network device */
        netdev_nullify ( netdev );
        netdev_put ( netdev );
 }
index 9fda9979fc2c4534b28740153e455cdbfd571156..b5ec6d0bf21063dcb69931af4a319e542d1a86fd 100644 (file)
@@ -12,8 +12,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #include <stdint.h>
 #include <ipxe/if_ether.h>
 
-/** BAR size */
-#define ENA_BAR_SIZE 16384
+/** Register BAR */
+#define ENA_REGS_BAR PCI_BASE_ADDRESS_0
+
+/** Register BAR size */
+#define ENA_REGS_SIZE 16384
+
+/** On-device memory BAR */
+#define ENA_MEM_BAR PCI_BASE_ADDRESS_2
 
 /** Queue alignment */
 #define ENA_ALIGN 4096
@@ -743,6 +749,8 @@ struct ena_qp {
 struct ena_nic {
        /** Registers */
        void *regs;
+       /** On-device memory */
+       void *mem;
        /** Host info */
        struct ena_host_info *info;
        /** Admin queue */