From 87f1796f158fe3e06d292f4b7e16bad879803b69 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 23 May 2022 12:23:53 +0100 Subject: [ecm] Treat ACPI MAC address as being a non-permanent MAC address When applying an ACPI-provided system-specific MAC address, apply it to netdev->ll_addr rather than netdev->hw_addr. This allows iPXE scripts to access the permanent MAC address via the ${netX/hwaddr} setting (and thereby provides scripts with a mechanism to ascertain that the NIC is using a MAC address other than its own permanent hardware address). Signed-off-by: Michael Brown --- src/drivers/net/ecm.c | 28 ++++++++++++++++------------ src/drivers/net/ecm.h | 2 +- src/drivers/net/ncm.c | 2 +- 3 files changed, 18 insertions(+), 14 deletions(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/ecm.c b/src/drivers/net/ecm.c index 826b3b16d..68ac962ab 100644 --- a/src/drivers/net/ecm.c +++ b/src/drivers/net/ecm.c @@ -84,24 +84,18 @@ ecm_ethernet_descriptor ( struct usb_configuration_descriptor *config, * * @v func USB function * @v desc Ethernet functional descriptor - * @v hw_addr Hardware address to fill in + * @v netdev Network device * @ret rc Return status code */ int ecm_fetch_mac ( struct usb_function *func, - struct ecm_ethernet_descriptor *desc, uint8_t *hw_addr ) { + struct ecm_ethernet_descriptor *desc, + struct net_device *netdev ) { struct usb_device *usb = func->usb; char buf[ base16_encoded_len ( ETH_ALEN ) + 1 /* NUL */ ]; + uint8_t amac[ETH_ALEN]; int len; int rc; - /* Use system-specific MAC address, if present and not already used */ - if ( ( ( rc = acpi_mac ( hw_addr ) ) == 0 ) && - ! find_netdev_by_ll_addr ( ðernet_protocol, hw_addr ) ) { - DBGC ( usb, "USB %s using system-specific MAC %s\n", - func->name, eth_ntoa ( hw_addr ) ); - return 0; - } - /* Fetch MAC address string */ len = usb_get_string_descriptor ( usb, desc->mac, 0, buf, sizeof ( buf ) ); @@ -118,7 +112,7 @@ int ecm_fetch_mac ( struct usb_function *func, } /* Decode MAC address */ - len = base16_decode ( buf, hw_addr, ETH_ALEN ); + len = base16_decode ( buf, netdev->hw_addr, ETH_ALEN ); if ( len < 0 ) { rc = len; DBGC ( usb, "USB %s could not decode ECM MAC \"%s\": %s\n", @@ -126,6 +120,16 @@ int ecm_fetch_mac ( struct usb_function *func, return rc; } + /* Apply system-specific MAC address as current link-layer + * address, if present and not already used. + */ + if ( ( ( rc = acpi_mac ( amac ) ) == 0 ) && + ! find_netdev_by_ll_addr ( ðernet_protocol, amac ) ) { + memcpy ( netdev->ll_addr, amac, ETH_ALEN ); + DBGC ( usb, "USB %s using system-specific MAC %s\n", + func->name, eth_ntoa ( netdev->ll_addr ) ); + } + return 0; } @@ -474,7 +478,7 @@ static int ecm_probe ( struct usb_function *func, } /* Fetch MAC address */ - if ( ( rc = ecm_fetch_mac ( func, ethernet, netdev->hw_addr ) ) != 0 ) { + if ( ( rc = ecm_fetch_mac ( func, ethernet, netdev ) ) != 0 ) { DBGC ( ecm, "ECM %p could not fetch MAC address: %s\n", ecm, strerror ( rc ) ); goto err_fetch_mac; diff --git a/src/drivers/net/ecm.h b/src/drivers/net/ecm.h index 0ad3ddb95..a7d03cf94 100644 --- a/src/drivers/net/ecm.h +++ b/src/drivers/net/ecm.h @@ -88,6 +88,6 @@ ecm_ethernet_descriptor ( struct usb_configuration_descriptor *config, struct usb_interface_descriptor *interface ); extern int ecm_fetch_mac ( struct usb_function *func, struct ecm_ethernet_descriptor *desc, - uint8_t *hw_addr ); + struct net_device *netdev ); #endif /* _ECM_H */ diff --git a/src/drivers/net/ncm.c b/src/drivers/net/ncm.c index 1e8088d76..2c0f91e21 100644 --- a/src/drivers/net/ncm.c +++ b/src/drivers/net/ncm.c @@ -598,7 +598,7 @@ static int ncm_probe ( struct usb_function *func, } /* Fetch MAC address */ - if ( ( rc = ecm_fetch_mac ( func, ethernet, netdev->hw_addr ) ) != 0 ) { + if ( ( rc = ecm_fetch_mac ( func, ethernet, netdev ) ) != 0 ) { DBGC ( ncm, "NCM %p could not fetch MAC address: %s\n", ncm, strerror ( rc ) ); goto err_fetch_mac; -- cgit v1.2.3-55-g7522 From 5d3fad5c10c72d836560331b3ffc61a7f62e7994 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 14 Mar 2022 16:28:24 +0000 Subject: [intelxl] Fix retrieval of switch configuration via admin queue Commit 8f3e648 ("[intelxl] Use one admin queue buffer per admin queue descriptor") changed the API for intelxl_admin_command() such that the caller now constructs the command directly within the next available descriptor ring entry, rather than relying on intelxl_admin_command() to copy the descriptor to and from the descriptor ring. This introduced a regression in intelxl_admin_switch(), since the second and subsequent iterations of the loop will not have constructed a valid command in the new descriptor ring entry before calling intelxl_admin_command(). Fix by constructing the command within the loop. Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index ac9e37c5a..111ac98ed 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -593,18 +593,20 @@ static int intelxl_admin_switch ( struct intelxl_nic *intelxl ) { struct intelxl_admin_descriptor *cmd; struct intelxl_admin_switch_params *sw; union intelxl_admin_buffer *buf; + uint16_t next = 0; int rc; - /* Populate descriptor */ - cmd = intelxl_admin_command_descriptor ( intelxl ); - cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_SWITCH ); - cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ); - cmd->len = cpu_to_le16 ( sizeof ( buf->sw ) ); - sw = &cmd->params.sw; - buf = intelxl_admin_command_buffer ( intelxl ); - /* Get each configuration in turn */ do { + /* Populate descriptor */ + cmd = intelxl_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_SWITCH ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->sw ) ); + sw = &cmd->params.sw; + sw->next = next; + buf = intelxl_admin_command_buffer ( intelxl ); + /* Issue command */ if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) return rc; @@ -624,7 +626,7 @@ static int intelxl_admin_switch ( struct intelxl_nic *intelxl ) { buf->sw.cfg.connection ); } - } while ( sw->next ); + } while ( ( next = sw->next ) ); /* Check that we found a VSI */ if ( ! intelxl->vsi ) { -- cgit v1.2.3-55-g7522 From 725f0370fa6495cd766335b94cc218f02df08122 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 14 Mar 2022 16:40:32 +0000 Subject: [intelxl] Fix bit width of function number in PFFUNC_RID register Signed-off-by: Michael Brown --- src/drivers/net/intelxl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index a4a776d28..69e98f0cd 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -993,7 +993,7 @@ intelxl_init_ring ( struct intelxl_ring *ring, unsigned int count, size_t len, /** Function Requester ID Information Register */ #define INTELXL_PFFUNC_RID 0x09c000 #define INTELXL_PFFUNC_RID_FUNC_NUM(x) \ - ( ( (x) >> 0 ) & 0x3 ) /**< Function number */ + ( ( (x) >> 0 ) & 0x7 ) /**< Function number */ /** PF Queue Allocation Register */ #define INTELXL_PFLAN_QALLOC 0x1c0400 -- cgit v1.2.3-55-g7522 From 814aef68c51503d53d07832bf882302bc75aa4bb Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 8 Aug 2022 14:26:54 +0100 Subject: [intelxl] Add missing padding bytes to receive queue context For the sake of completeness, ensure that all 32 bytes of the receive queue context are programmed (including the unused final 8 bytes). Signed-off-by: Michael Brown --- src/drivers/net/intelxl.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/drivers/net') diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index 69e98f0cd..f3b81e6ad 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -685,6 +685,8 @@ struct intelxl_context_rx { uint8_t reserved_b[7]; /** Maximum frame size */ uint16_t mfs; + /** Reserved */ + uint8_t reserved_c[8]; } __attribute__ (( packed )); /** Receive queue base address and queue count */ -- cgit v1.2.3-55-g7522 From 319caeaa7b1505d8f2c2e90b51ba7202c3981cc6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 23 Mar 2022 12:21:49 +0000 Subject: [intelxl] Identify rings consistently in debug messages Use the tail register offset (which exists for all ring types) as the ring identifier in all relevant debug messages. Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index 111ac98ed..7885fbce8 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -965,8 +965,7 @@ int intelxl_alloc_ring ( struct intelxl_nic *intelxl, ring->cons = 0; DBGC ( intelxl, "INTELXL %p ring %06x is at [%08lx,%08lx)\n", - intelxl, ( ring->reg + ring->tail ), - virt_to_phys ( ring->desc.raw ), + intelxl, ring->tail, virt_to_phys ( ring->desc.raw ), ( virt_to_phys ( ring->desc.raw ) + ring->len ) ); return 0; @@ -1205,7 +1204,7 @@ static int intelxl_enable_ring ( struct intelxl_nic *intelxl, qxx_ena = readl ( ring_regs + INTELXL_QXX_ENA ); if ( ! ( qxx_ena & INTELXL_QXX_ENA_STAT ) ) { DBGC ( intelxl, "INTELXL %p ring %06x failed to enable: " - "%#08x\n", intelxl, ring->reg, qxx_ena ); + "%#08x\n", intelxl, ring->tail, qxx_ena ); return -EIO; } @@ -1241,7 +1240,7 @@ static int intelxl_disable_ring ( struct intelxl_nic *intelxl, } DBGC ( intelxl, "INTELXL %p ring %06x timed out waiting for disable: " - "%#08x\n", intelxl, ring->reg, qxx_ena ); + "%#08x\n", intelxl, ring->tail, qxx_ena ); return -ETIMEDOUT; } -- cgit v1.2.3-55-g7522 From d8014b1801f1dee59d7d1450a62b31f844e2bd5c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 18 Mar 2022 12:40:23 +0000 Subject: [intelxl] Include admin command response data buffer in debug output Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index 7885fbce8..5de45913f 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -409,7 +409,7 @@ int intelxl_admin_command ( struct intelxl_nic *intelxl ) { /* Post command descriptor */ DBGC2_HDA ( intelxl, virt_to_phys ( cmd ), cmd, sizeof ( *cmd ) ); - if ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ) ) { + if ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_RD ) ) { DBGC2_HDA ( intelxl, virt_to_phys ( buf ), buf, le16_to_cpu ( cmd->len ) ); } @@ -428,6 +428,10 @@ int intelxl_admin_command ( struct intelxl_nic *intelxl ) { intelxl, index ); DBGC2_HDA ( intelxl, virt_to_phys ( cmd ), cmd, sizeof ( *cmd ) ); + if ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ) ) { + DBGC2_HDA ( intelxl, virt_to_phys ( buf ), buf, + le16_to_cpu ( cmd->len ) ); + } /* Check for cookie mismatch */ if ( cmd->cookie != cookie ) { -- cgit v1.2.3-55-g7522 From 9dfcdc04c86503924e1424757082d07e3649f59e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 8 Aug 2022 14:13:34 +0100 Subject: [intelxl] Update list of PCI IDs Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/drivers/net') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index 5de45913f..9d11f6458 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -1826,6 +1826,10 @@ static void intelxl_remove ( struct pci_device *pci ) { /** PCI device IDs */ static struct pci_device_id intelxl_nics[] = { + PCI_ROM ( 0x8086, 0x0cf8, "x710-n3000", "X710 FPGA N3000", 0 ), + PCI_ROM ( 0x8086, 0x0d58, "xxv710-n3000", "XXV710 FPGA N3000", 0 ), + PCI_ROM ( 0x8086, 0x104e, "x710-sfp-b", "X710 10GbE SFP+", 0 ), + PCI_ROM ( 0x8086, 0x104f, "x710-kx-b", "X710 10GbE backplane", 0 ), PCI_ROM ( 0x8086, 0x1572, "x710-sfp", "X710 10GbE SFP+", 0 ), PCI_ROM ( 0x8086, 0x1574, "xl710-qemu", "Virtual XL710", 0 ), PCI_ROM ( 0x8086, 0x1580, "xl710-kx-b", "XL710 40GbE backplane", 0 ), @@ -1839,6 +1843,7 @@ static struct pci_device_id intelxl_nics[] = { PCI_ROM ( 0x8086, 0x1589, "x710-10gt4", "X710 10GBASE-T4", 0 ), PCI_ROM ( 0x8086, 0x158a, "xxv710", "XXV710 25GbE backplane", 0 ), PCI_ROM ( 0x8086, 0x158b, "xxv710-sfp28", "XXV710 25GbE SFP28", 0 ), + PCI_ROM ( 0x8086, 0x15ff, "x710-10gt-b", "X710 10GBASE-T", 0 ), PCI_ROM ( 0x8086, 0x37ce, "x722-kx", "X722 10GbE backplane", 0 ), PCI_ROM ( 0x8086, 0x37cf, "x722-qsfp", "X722 10GbE QSFP+", 0 ), PCI_ROM ( 0x8086, 0x37d0, "x722-sfp", "X722 10GbE SFP+", 0 ), -- cgit v1.2.3-55-g7522 From 0965cec53c9e421f900bb050b1c83196a0a5687b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 8 Aug 2022 16:39:40 +0100 Subject: [pci] Generalise function-level reset mechanism Signed-off-by: Michael Brown --- src/drivers/bus/pciextra.c | 22 ++++++++++++++++++++++ src/drivers/net/intelxlvf.c | 23 +++-------------------- src/include/ipxe/pci.h | 1 + 3 files changed, 26 insertions(+), 20 deletions(-) (limited to 'src/drivers/net') diff --git a/src/drivers/bus/pciextra.c b/src/drivers/bus/pciextra.c index 3082d8a3d..23617bc9a 100644 --- a/src/drivers/bus/pciextra.c +++ b/src/drivers/bus/pciextra.c @@ -1,6 +1,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include +#include #include static int pci_find_capability_common ( struct pci_device *pci, @@ -112,3 +113,24 @@ unsigned long pci_bar_size ( struct pci_device *pci, unsigned int reg ) { size = size & ~( size - 1 ); return size; } + +/** + * Perform PCI Express function-level reset (FLR) + * + * @v pci PCI device + * @v exp PCI Express Capability address + */ +void pci_reset ( struct pci_device *pci, unsigned int exp ) { + uint16_t control; + + /* Perform a PCIe function-level reset */ + pci_read_config_word ( pci, ( exp + PCI_EXP_DEVCTL ), &control ); + control |= PCI_EXP_DEVCTL_FLR; + pci_write_config_word ( pci, ( exp + PCI_EXP_DEVCTL ), control ); + + /* Allow time for reset to complete */ + mdelay ( PCI_EXP_FLR_DELAY_MS ); + + /* Re-enable device */ + adjust_pci_device ( pci ); +} diff --git a/src/drivers/net/intelxlvf.c b/src/drivers/net/intelxlvf.c index 752de7815..b5957a53b 100644 --- a/src/drivers/net/intelxlvf.c +++ b/src/drivers/net/intelxlvf.c @@ -45,23 +45,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); ****************************************************************************** */ -/** - * Reset hardware via PCIe function-level reset - * - * @v intelxl Intel device - */ -static void intelxlvf_reset_flr ( struct intelxl_nic *intelxl, - struct pci_device *pci ) { - uint16_t control; - - /* Perform a PCIe function-level reset */ - pci_read_config_word ( pci, ( intelxl->exp + PCI_EXP_DEVCTL ), - &control ); - pci_write_config_word ( pci, ( intelxl->exp + PCI_EXP_DEVCTL ), - ( control | PCI_EXP_DEVCTL_FLR ) ); - mdelay ( INTELXL_RESET_DELAY_MS ); -} - /** * Wait for admin event queue to be torn down * @@ -637,7 +620,7 @@ static int intelxlvf_probe ( struct pci_device *pci ) { } /* Reset the function via PCIe FLR */ - intelxlvf_reset_flr ( intelxl, pci ); + pci_reset ( pci, intelxl->exp ); /* Enable MSI-X dummy interrupt */ if ( ( rc = intelxl_msix_enable ( intelxl, pci ) ) != 0 ) @@ -669,7 +652,7 @@ static int intelxlvf_probe ( struct pci_device *pci ) { err_open_admin: intelxl_msix_disable ( intelxl, pci ); err_msix: - intelxlvf_reset_flr ( intelxl, pci ); + pci_reset ( pci, intelxl->exp ); err_exp: iounmap ( intelxl->regs ); err_ioremap: @@ -701,7 +684,7 @@ static void intelxlvf_remove ( struct pci_device *pci ) { intelxl_msix_disable ( intelxl, pci ); /* Reset the function via PCIe FLR */ - intelxlvf_reset_flr ( intelxl, pci ); + pci_reset ( pci, intelxl->exp ); /* Free network device */ iounmap ( intelxl->regs ); diff --git a/src/include/ipxe/pci.h b/src/include/ipxe/pci.h index 933f48530..bf6174c23 100644 --- a/src/include/ipxe/pci.h +++ b/src/include/ipxe/pci.h @@ -309,6 +309,7 @@ extern int pci_find_capability ( struct pci_device *pci, int capability ); extern int pci_find_next_capability ( struct pci_device *pci, int pos, int capability ); extern unsigned long pci_bar_size ( struct pci_device *pci, unsigned int reg ); +extern void pci_reset ( struct pci_device *pci, unsigned int exp ); /** * Initialise PCI device -- cgit v1.2.3-55-g7522 From a202de385dcb9c15a1b2b3fd339abbe721ed5c86 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 17 Mar 2022 13:45:31 +0000 Subject: [intelxl] Use function-level reset instead of PFGEN_CTRL.PFSWR Remove knowledge of the PFGEN_CTRL register (which changes location between XL710 and E810 register maps), and instead use PCIe FLR to reset the physical function. Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 45 ++++++++++++++------------------------------- src/drivers/net/intelxl.h | 7 ------- src/drivers/net/intelxlvf.c | 2 +- src/drivers/net/intelxlvf.h | 3 +++ 4 files changed, 18 insertions(+), 39 deletions(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index 9d11f6458..20db51acf 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -44,31 +44,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -/****************************************************************************** - * - * Device reset - * - ****************************************************************************** - */ - -/** - * Reset hardware - * - * @v intelxl Intel device - * @ret rc Return status code - */ -static int intelxl_reset ( struct intelxl_nic *intelxl ) { - uint32_t pfgen_ctrl; - - /* Perform a global software reset */ - pfgen_ctrl = readl ( intelxl->regs + INTELXL_PFGEN_CTRL ); - writel ( ( pfgen_ctrl | INTELXL_PFGEN_CTRL_PFSWR ), - intelxl->regs + INTELXL_PFGEN_CTRL ); - mdelay ( INTELXL_RESET_DELAY_MS ); - - return 0; -} - /****************************************************************************** * * MAC address @@ -1704,9 +1679,17 @@ static int intelxl_probe ( struct pci_device *pci ) { dma_set_mask_64bit ( intelxl->dma ); netdev->dma = intelxl->dma; - /* Reset the NIC */ - if ( ( rc = intelxl_reset ( intelxl ) ) != 0 ) - goto err_reset; + /* Locate PCI Express capability */ + intelxl->exp = pci_find_capability ( pci, PCI_CAP_ID_EXP ); + if ( ! intelxl->exp ) { + DBGC ( intelxl, "INTELXL %p missing PCIe capability\n", + intelxl ); + rc = -ENXIO; + goto err_exp; + } + + /* Reset the function via PCIe FLR */ + pci_reset ( pci, intelxl->exp ); /* Get function number, port number and base queue number */ pffunc_rid = readl ( intelxl->regs + INTELXL_PFFUNC_RID ); @@ -1787,8 +1770,8 @@ static int intelxl_probe ( struct pci_device *pci ) { intelxl_msix_disable ( intelxl, pci ); err_msix: err_fetch_mac: - intelxl_reset ( intelxl ); - err_reset: + pci_reset ( pci, intelxl->exp ); + err_exp: iounmap ( intelxl->regs ); err_ioremap: netdev_nullify ( netdev ); @@ -1816,7 +1799,7 @@ static void intelxl_remove ( struct pci_device *pci ) { intelxl_msix_disable ( intelxl, pci ); /* Reset the NIC */ - intelxl_reset ( intelxl ); + pci_reset ( pci, intelxl->exp ); /* Free network device */ iounmap ( intelxl->regs ); diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index f3b81e6ad..f3588bd34 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -985,13 +985,6 @@ intelxl_init_ring ( struct intelxl_ring *ring, unsigned int count, size_t len, INTELXL_QINT_TQCTL_NEXTQ_TYPE ( 0x1 ) /**< Transmit queue */ #define INTELXL_QINT_TQCTL_CAUSE_ENA 0x40000000UL /**< Enable */ -/** PF Control Register */ -#define INTELXL_PFGEN_CTRL 0x092400 -#define INTELXL_PFGEN_CTRL_PFSWR 0x00000001UL /**< Software Reset */ - -/** Time to delay for device reset, in milliseconds */ -#define INTELXL_RESET_DELAY_MS 100 - /** Function Requester ID Information Register */ #define INTELXL_PFFUNC_RID 0x09c000 #define INTELXL_PFFUNC_RID_FUNC_NUM(x) \ diff --git a/src/drivers/net/intelxlvf.c b/src/drivers/net/intelxlvf.c index b5957a53b..e0e7dc610 100644 --- a/src/drivers/net/intelxlvf.c +++ b/src/drivers/net/intelxlvf.c @@ -122,7 +122,7 @@ static int intelxlvf_reset_admin ( struct intelxl_nic *intelxl ) { goto err_command; /* Wait for minimum reset time */ - mdelay ( INTELXL_RESET_DELAY_MS ); + mdelay ( INTELXLVF_RESET_DELAY_MS ); /* Wait for reset to take effect */ if ( ( rc = intelxlvf_reset_wait_teardown ( intelxl ) ) != 0 ) diff --git a/src/drivers/net/intelxlvf.h b/src/drivers/net/intelxlvf.h index ffcae5674..cf449a744 100644 --- a/src/drivers/net/intelxlvf.h +++ b/src/drivers/net/intelxlvf.h @@ -64,6 +64,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define INTELXLVF_VFGEN_RSTAT_VFR_STATE(x) ( (x) & 0x3 ) #define INTELXLVF_VFGEN_RSTAT_VFR_STATE_ACTIVE 0x2 +/** Minimum time to wait for reset to complete */ +#define INTELXLVF_RESET_DELAY_MS 100 + /** Maximum time to wait for reset to complete */ #define INTELXLVF_RESET_MAX_WAIT_MS 1000 -- cgit v1.2.3-55-g7522 From 1b61c2118ca54a8d9ad71cc402e7c9f6094f4ec6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 10 Aug 2022 12:27:19 +0100 Subject: [intelxl] Fix invocation of intelxlvf_admin_queues() The second parameter to intelxlvf_admin_queues() is a boolean used to select the VF opcode, rather than the raw VF opcode itself. Signed-off-by: Michael Brown --- src/drivers/net/intelxlvf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/intelxlvf.c b/src/drivers/net/intelxlvf.c index e0e7dc610..d9922a7c6 100644 --- a/src/drivers/net/intelxlvf.c +++ b/src/drivers/net/intelxlvf.c @@ -510,7 +510,7 @@ static int intelxlvf_open ( struct net_device *netdev ) { return 0; err_promisc: - intelxlvf_admin_queues ( netdev, INTELXL_ADMIN_VF_DISABLE ); + intelxlvf_admin_queues ( netdev, 0 ); err_enable: err_irq_map: err_configure: -- cgit v1.2.3-55-g7522 From b4216fa5063e464a16361bba247d49e8620a2cf2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 9 Mar 2022 00:24:22 +0000 Subject: [intelxl] Use non-zero MSI-X vector for virtual function interrupts The 100 Gigabit physical function driver requires a virtual function driver to request that transmit and receive queues are mapped to MSI-X vector 1 or higher. Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 25 ++++++++++++++----------- src/drivers/net/intelxl.h | 11 ++++++++--- src/drivers/net/intelxlvf.c | 10 ++++++---- src/drivers/net/intelxlvf.h | 11 +++++++++++ 4 files changed, 39 insertions(+), 18 deletions(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index 20db51acf..a7fbd60f9 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -104,10 +104,11 @@ static int intelxl_fetch_mac ( struct intelxl_nic *intelxl, * * @v intelxl Intel device * @v pci PCI device + * @v vector MSI-X vector * @ret rc Return status code */ int intelxl_msix_enable ( struct intelxl_nic *intelxl, - struct pci_device *pci ) { + struct pci_device *pci, unsigned int vector ) { int rc; /* Map dummy target location */ @@ -126,12 +127,12 @@ int intelxl_msix_enable ( struct intelxl_nic *intelxl, goto err_enable; } - /* Configure interrupt zero to write to dummy location */ - pci_msix_map ( &intelxl->msix.cap, 0, + /* Configure interrupt to write to dummy location */ + pci_msix_map ( &intelxl->msix.cap, vector, dma ( &intelxl->msix.map, &intelxl->msix.msg ), 0 ); - /* Enable dummy interrupt zero */ - pci_msix_unmask ( &intelxl->msix.cap, 0 ); + /* Enable dummy interrupt */ + pci_msix_unmask ( &intelxl->msix.cap, vector ); return 0; @@ -147,12 +148,13 @@ int intelxl_msix_enable ( struct intelxl_nic *intelxl, * * @v intelxl Intel device * @v pci PCI device + * @v vector MSI-X vector */ void intelxl_msix_disable ( struct intelxl_nic *intelxl, - struct pci_device *pci ) { + struct pci_device *pci, unsigned int vector ) { - /* Disable dummy interrupt zero */ - pci_msix_mask ( &intelxl->msix.cap, 0 ); + /* Disable dummy interrupts */ + pci_msix_mask ( &intelxl->msix.cap, vector ); /* Disable MSI-X capability */ pci_msix_disable ( pci, &intelxl->msix.cap ); @@ -1707,7 +1709,8 @@ static int intelxl_probe ( struct pci_device *pci ) { goto err_fetch_mac; /* Enable MSI-X dummy interrupt */ - if ( ( rc = intelxl_msix_enable ( intelxl, pci ) ) != 0 ) + if ( ( rc = intelxl_msix_enable ( intelxl, pci, + INTELXL_MSIX_VECTOR ) ) != 0 ) goto err_msix; /* Open admin queues */ @@ -1767,7 +1770,7 @@ static int intelxl_probe ( struct pci_device *pci ) { err_admin_clear_pxe: intelxl_close_admin ( intelxl ); err_open_admin: - intelxl_msix_disable ( intelxl, pci ); + intelxl_msix_disable ( intelxl, pci, INTELXL_MSIX_VECTOR ); err_msix: err_fetch_mac: pci_reset ( pci, intelxl->exp ); @@ -1796,7 +1799,7 @@ static void intelxl_remove ( struct pci_device *pci ) { intelxl_close_admin ( intelxl ); /* Disable MSI-X dummy interrupt */ - intelxl_msix_disable ( intelxl, pci ); + intelxl_msix_disable ( intelxl, pci, INTELXL_MSIX_VECTOR ); /* Reset the NIC */ pci_reset ( pci, intelxl->exp ); diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index f3588bd34..92d7f8882 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -1035,7 +1035,10 @@ struct intelxl_msix { struct dma_mapping map; }; -/** An Intel 40Gigabit network card */ +/** MSI-X interrupt vector */ +#define INTELXL_MSIX_VECTOR 0 + +/** An Intel 40 Gigabit network card */ struct intelxl_nic { /** Registers */ void *regs; @@ -1084,9 +1087,11 @@ struct intelxl_nic { }; extern int intelxl_msix_enable ( struct intelxl_nic *intelxl, - struct pci_device *pci ); + struct pci_device *pci, + unsigned int vector ); extern void intelxl_msix_disable ( struct intelxl_nic *intelxl, - struct pci_device *pci ); + struct pci_device *pci, + unsigned int vector ); extern struct intelxl_admin_descriptor * intelxl_admin_command_descriptor ( struct intelxl_nic *intelxl ); extern union intelxl_admin_buffer * diff --git a/src/drivers/net/intelxlvf.c b/src/drivers/net/intelxlvf.c index d9922a7c6..7dedf0f71 100644 --- a/src/drivers/net/intelxlvf.c +++ b/src/drivers/net/intelxlvf.c @@ -399,6 +399,7 @@ static int intelxlvf_admin_irq_map ( struct net_device *netdev ) { buf = intelxl_admin_command_buffer ( intelxl ); buf->irq.count = cpu_to_le16 ( 1 ); buf->irq.vsi = cpu_to_le16 ( intelxl->vsi ); + buf->irq.vec = cpu_to_le16 ( INTELXLVF_MSIX_VECTOR ); buf->irq.rxmap = cpu_to_le16 ( 0x0001 ); buf->irq.txmap = cpu_to_le16 ( 0x0001 ); @@ -583,7 +584,7 @@ static int intelxlvf_probe ( struct pci_device *pci ) { pci_set_drvdata ( pci, netdev ); netdev->dev = &pci->dev; memset ( intelxl, 0, sizeof ( *intelxl ) ); - intelxl->intr = INTELXLVF_VFINT_DYN_CTL0; + intelxl->intr = INTELXLVF_VFINT_DYN_CTLN ( INTELXLVF_MSIX_VECTOR ); intelxl_init_admin ( &intelxl->command, INTELXLVF_ADMIN, &intelxlvf_admin_command_offsets ); intelxl_init_admin ( &intelxl->event, INTELXLVF_ADMIN, @@ -623,7 +624,8 @@ static int intelxlvf_probe ( struct pci_device *pci ) { pci_reset ( pci, intelxl->exp ); /* Enable MSI-X dummy interrupt */ - if ( ( rc = intelxl_msix_enable ( intelxl, pci ) ) != 0 ) + if ( ( rc = intelxl_msix_enable ( intelxl, pci, + INTELXLVF_MSIX_VECTOR ) ) != 0 ) goto err_msix; /* Open admin queues */ @@ -650,7 +652,7 @@ static int intelxlvf_probe ( struct pci_device *pci ) { err_reset_admin: intelxl_close_admin ( intelxl ); err_open_admin: - intelxl_msix_disable ( intelxl, pci ); + intelxl_msix_disable ( intelxl, pci, INTELXLVF_MSIX_VECTOR ); err_msix: pci_reset ( pci, intelxl->exp ); err_exp: @@ -681,7 +683,7 @@ static void intelxlvf_remove ( struct pci_device *pci ) { intelxl_close_admin ( intelxl ); /* Disable MSI-X dummy interrupt */ - intelxl_msix_disable ( intelxl, pci ); + intelxl_msix_disable ( intelxl, pci, INTELXLVF_MSIX_VECTOR ); /* Reset the function via PCIe FLR */ pci_reset ( pci, intelxl->exp ); diff --git a/src/drivers/net/intelxlvf.h b/src/drivers/net/intelxlvf.h index cf449a744..58ade11e9 100644 --- a/src/drivers/net/intelxlvf.h +++ b/src/drivers/net/intelxlvf.h @@ -14,12 +14,23 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** BAR size */ #define INTELXLVF_BAR_SIZE 0x10000 +/** MSI-X vector + * + * The 100 Gigabit physical function driver requires a virtual + * function driver to request that transmit and receive queues are + * mapped to MSI-X vector 1 or higher. + */ +#define INTELXLVF_MSIX_VECTOR 1 + /** Transmit Queue Tail Register */ #define INTELXLVF_QTX_TAIL 0x00000 /** Receive Queue Tail Register */ #define INTELXLVF_QRX_TAIL 0x02000 +/** VF Interrupt N Dynamic Control Register */ +#define INTELXLVF_VFINT_DYN_CTLN( x ) ( 0x3800 + ( 0x4 * ( (x) - 1 ) ) ) + /** VF Interrupt Zero Dynamic Control Register */ #define INTELXLVF_VFINT_DYN_CTL0 0x5c00 -- cgit v1.2.3-55-g7522 From 9f5b9e3abbeade8730811f22e385f120d36ab827 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 9 Mar 2022 00:30:14 +0000 Subject: [intelxl] Negotiate API version for virtual function via admin queue Do not attempt to use the admin commands to get the firmware version and report the driver version for the virtual function driver, since these will be rejected by the E810 firmware as invalid commands when issued by a virtual function. Instead, use the mailbox interface to negotiate the API version with the physical function driver. Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 20 ++++++++++---------- src/drivers/net/intelxl.h | 19 +++++++++++++++++++ src/drivers/net/intelxlvf.c | 46 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 10 deletions(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index a7fbd60f9..be4e9113b 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -847,18 +847,8 @@ int intelxl_open_admin ( struct intelxl_nic *intelxl ) { /* (Re)open admin queues */ intelxl_reopen_admin ( intelxl ); - /* Get firmware version */ - if ( ( rc = intelxl_admin_version ( intelxl ) ) != 0 ) - goto err_version; - - /* Report driver version */ - if ( ( rc = intelxl_admin_driver ( intelxl ) ) != 0 ) - goto err_driver; - return 0; - err_driver: - err_version: intelxl_disable_admin ( intelxl, &intelxl->command ); intelxl_disable_admin ( intelxl, &intelxl->event ); intelxl_free_admin ( intelxl, &intelxl->command ); @@ -1717,6 +1707,14 @@ static int intelxl_probe ( struct pci_device *pci ) { if ( ( rc = intelxl_open_admin ( intelxl ) ) != 0 ) goto err_open_admin; + /* Get firmware version */ + if ( ( rc = intelxl_admin_version ( intelxl ) ) != 0 ) + goto err_admin_version; + + /* Report driver version */ + if ( ( rc = intelxl_admin_driver ( intelxl ) ) != 0 ) + goto err_admin_driver; + /* Clear PXE mode */ if ( ( rc = intelxl_admin_clear_pxe ( intelxl ) ) != 0 ) goto err_admin_clear_pxe; @@ -1768,6 +1766,8 @@ static int intelxl_probe ( struct pci_device *pci ) { err_admin_vsi: err_admin_switch: err_admin_clear_pxe: + err_admin_driver: + err_admin_version: intelxl_close_admin ( intelxl ); err_open_admin: intelxl_msix_disable ( intelxl, pci, INTELXL_MSIX_VECTOR ); diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index 92d7f8882..2c7eb4a4f 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -311,6 +311,17 @@ struct intelxl_admin_link_params { /** Admin queue Send Message to VF command */ #define INTELXL_ADMIN_SEND_TO_VF 0x0802 +/** Admin Queue VF Version opcode */ +#define INTELXL_ADMIN_VF_VERSION 0x00000001 + +/** Admin Queue VF Version data buffer */ +struct intelxl_admin_vf_version_buffer { + /** Major version */ + uint32_t major; + /** Minor version */ + uint32_t minor; +} __attribute__ (( packed )); + /** Admin Queue VF Reset opcode */ #define INTELXL_ADMIN_VF_RESET 0x00000002 @@ -503,6 +514,8 @@ union intelxl_admin_buffer { struct intelxl_admin_switch_buffer sw; /** Get VSI Parameters data buffer */ struct intelxl_admin_vsi_buffer vsi; + /** VF Version data buffer */ + struct intelxl_admin_vf_version_buffer ver; /** VF Get Resources data buffer */ struct intelxl_admin_vf_get_resources_buffer res; /** VF Status Change Event data buffer */ @@ -598,6 +611,12 @@ intelxl_init_admin ( struct intelxl_admin *admin, unsigned int base, /** Admin queue API major version */ #define INTELXL_ADMIN_API_MAJOR 1 +/** Admin queue VF API major version */ +#define INTELXL_ADMIN_VF_API_MAJOR 1 + +/** Admin queue VF API minor version */ +#define INTELXL_ADMIN_VF_API_MINOR 0 + /****************************************************************************** * * Transmit and receive queue context diff --git a/src/drivers/net/intelxlvf.c b/src/drivers/net/intelxlvf.c index 7dedf0f71..8febbb2b9 100644 --- a/src/drivers/net/intelxlvf.c +++ b/src/drivers/net/intelxlvf.c @@ -305,6 +305,47 @@ void intelxlvf_admin_event ( struct net_device *netdev, } } +/** + * Negotiate API version + * + * @v netdev Network device + * @ret rc Return status code + */ +static int intelxlvf_admin_version ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct intelxl_admin_descriptor *cmd; + struct intelxl_admin_vf_version_buffer *ver; + union intelxl_admin_buffer *buf; + unsigned int api; + int rc; + + /* Populate descriptor */ + cmd = intelxl_admin_command_descriptor ( intelxl ); + cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_VERSION ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->ver ) ); + buf = intelxl_admin_command_buffer ( intelxl ); + buf->ver.major = cpu_to_le32 ( INTELXL_ADMIN_VF_API_MAJOR ); + buf->ver.minor = cpu_to_le32 ( INTELXL_ADMIN_VF_API_MINOR ); + + /* Issue command */ + if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 ) + return rc; + ver = &intelxl->vbuf.ver; + api = le32_to_cpu ( ver->major ); + DBGC ( intelxl, "INTELXL %p API v%d.%d\n", + intelxl, api, le32_to_cpu ( ver->minor ) ); + + /* Check for API compatibility */ + if ( api > INTELXL_ADMIN_VF_API_MAJOR ) { + DBGC ( intelxl, "INTELXL %p unsupported API v%d\n", + intelxl, api ); + return -ENOTSUP; + } + + return 0; +} + /** * Get resources * @@ -636,6 +677,10 @@ static int intelxlvf_probe ( struct pci_device *pci ) { if ( ( rc = intelxlvf_reset_admin ( intelxl ) ) != 0 ) goto err_reset_admin; + /* Negotiate API version */ + if ( ( rc = intelxlvf_admin_version ( netdev ) ) != 0 ) + goto err_version; + /* Get MAC address */ if ( ( rc = intelxlvf_admin_get_resources ( netdev ) ) != 0 ) goto err_get_resources; @@ -649,6 +694,7 @@ static int intelxlvf_probe ( struct pci_device *pci ) { unregister_netdev ( netdev ); err_register_netdev: err_get_resources: + err_version: err_reset_admin: intelxl_close_admin ( intelxl ); err_open_admin: -- cgit v1.2.3-55-g7522 From ef7066755720a5beef46a5e2e5cad888ef139314 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 9 Mar 2022 00:41:01 +0000 Subject: [intelxl] Increase receive descriptor ring size to 64 entries The E810 requires that receive descriptor rings have at least 64 entries (and are a multiple of 32 entries). Signed-off-by: Michael Brown --- src/drivers/net/intelxl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index 2c7eb4a4f..066eb0f82 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -940,9 +940,9 @@ intelxl_init_ring ( struct intelxl_ring *ring, unsigned int count, size_t len, /** Number of receive descriptors * - * Must be a multiple of 32. + * Must be a multiple of 32 and greater than or equal to 64. */ -#define INTELXL_RX_NUM_DESC 32 +#define INTELXL_RX_NUM_DESC 64 /** Receive descriptor ring fill level * -- cgit v1.2.3-55-g7522 From 9e46ffa924397e72f750a5a4848a42c597396742 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 13 Mar 2022 14:37:11 +0000 Subject: [intelxl] Rename 8086:1889 PCI ID to "iavf" The PCI device ID 8086:1889 is for the Intel Ethernet Adaptive Virtual Function, which is a generic virtual function that can be exposed by different generations of Intel hardware. Rename the PCI ID from "xl710-vf-ad" to "iavf" to reflect that the driver is not XL710-specific. Signed-off-by: Michael Brown --- src/drivers/net/intelxlvf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/intelxlvf.c b/src/drivers/net/intelxlvf.c index 8febbb2b9..ed785898a 100644 --- a/src/drivers/net/intelxlvf.c +++ b/src/drivers/net/intelxlvf.c @@ -744,7 +744,7 @@ static void intelxlvf_remove ( struct pci_device *pci ) { static struct pci_device_id intelxlvf_nics[] = { PCI_ROM ( 0x8086, 0x154c, "xl710-vf", "XL710 VF", 0 ), PCI_ROM ( 0x8086, 0x1571, "xl710-vf-hv", "XL710 VF (Hyper-V)", 0 ), - PCI_ROM ( 0x8086, 0x1889, "xl710-vf-ad", "XL710 VF (adaptive)", 0 ), + PCI_ROM ( 0x8086, 0x1889, "iavf", "Intel adaptive VF", 0 ), PCI_ROM ( 0x8086, 0x37cd, "x722-vf", "X722 VF", 0 ), PCI_ROM ( 0x8086, 0x37d9, "x722-vf-hv", "X722 VF (Hyper-V)", 0 ), }; -- cgit v1.2.3-55-g7522 From 67f8878e102accdff5f82130c0c0dd85ebc698d0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 8 Aug 2022 14:34:47 +0100 Subject: [intelxl] Handle admin events via a callback The physical and virtual function drivers each care about precisely one admin queue event type. Simplify event handling by using a per-driver callback instead of the existing weak function symbol. Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 39 ++++++++++++++++++--------------------- src/drivers/net/intelxl.h | 15 +++++++++++---- src/drivers/net/intelxlvf.c | 19 ++++++++++++++----- 3 files changed, 43 insertions(+), 30 deletions(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index be4e9113b..e31595e1c 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -743,18 +743,26 @@ static int intelxl_admin_link ( struct net_device *netdev ) { } /** - * Handle virtual function event (when VF driver is not present) + * Handle admin event * * @v netdev Network device - * @v evt Admin queue event descriptor - * @v buf Admin queue event data buffer + * @v evt Event descriptor + * @v buf Data buffer */ -__weak void -intelxlvf_admin_event ( struct net_device *netdev __unused, - struct intelxl_admin_descriptor *evt __unused, - union intelxl_admin_buffer *buf __unused ) { +static void intelxl_admin_event ( struct net_device *netdev, + struct intelxl_admin_descriptor *evt, + union intelxl_admin_buffer *buf __unused ) { + struct intelxl_nic *intelxl = netdev->priv; - /* Nothing to do */ + /* Ignore unrecognised events */ + if ( evt->opcode != cpu_to_le16 ( INTELXL_ADMIN_LINK ) ) { + DBGC ( intelxl, "INTELXL %p unrecognised event opcode " + "%#04x\n", intelxl, le16_to_cpu ( evt->opcode ) ); + return; + } + + /* Update link status */ + intelxl_admin_link ( netdev ); } /** @@ -806,19 +814,7 @@ void intelxl_poll_admin ( struct net_device *netdev ) { } /* Handle event */ - switch ( evt->opcode ) { - case cpu_to_le16 ( INTELXL_ADMIN_LINK ): - intelxl_admin_link ( netdev ); - break; - case cpu_to_le16 ( INTELXL_ADMIN_SEND_TO_VF ): - intelxlvf_admin_event ( netdev, evt, buf ); - break; - default: - DBGC ( intelxl, "INTELXL %p admin event %#x " - "unrecognised opcode %#04x\n", intelxl, - admin->index, le16_to_cpu ( evt->opcode ) ); - break; - } + intelxl->handle ( netdev, evt, buf ); /* Reset descriptor and refill queue */ intelxl_admin_event_init ( intelxl, admin->index ); @@ -1645,6 +1641,7 @@ static int intelxl_probe ( struct pci_device *pci ) { netdev->dev = &pci->dev; memset ( intelxl, 0, sizeof ( *intelxl ) ); intelxl->intr = INTELXL_PFINT_DYN_CTL0; + intelxl->handle = intelxl_admin_event; intelxl_init_admin ( &intelxl->command, INTELXL_ADMIN_CMD, &intelxl_admin_offsets ); intelxl_init_admin ( &intelxl->event, INTELXL_ADMIN_EVT, diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index 066eb0f82..cd0d2ee81 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -1103,6 +1103,17 @@ struct intelxl_nic { struct intelxl_ring rx; /** Receive I/O buffers */ struct io_buffer *rx_iobuf[INTELXL_RX_NUM_DESC]; + + /** + * Handle admin event + * + * @v netdev Network device + * @v evt Event descriptor + * @v buf Data buffer + */ + void ( * handle ) ( struct net_device *netdev, + struct intelxl_admin_descriptor *evt, + union intelxl_admin_buffer *buf ); }; extern int intelxl_msix_enable ( struct intelxl_nic *intelxl, @@ -1129,8 +1140,4 @@ extern int intelxl_transmit ( struct net_device *netdev, struct io_buffer *iobuf ); extern void intelxl_poll ( struct net_device *netdev ); -extern void intelxlvf_admin_event ( struct net_device *netdev, - struct intelxl_admin_descriptor *evt, - union intelxl_admin_buffer *buf ); - #endif /* _INTELXL_H */ diff --git a/src/drivers/net/intelxlvf.c b/src/drivers/net/intelxlvf.c index ed785898a..6f52f1393 100644 --- a/src/drivers/net/intelxlvf.c +++ b/src/drivers/net/intelxlvf.c @@ -261,19 +261,27 @@ intelxlvf_admin_status ( struct net_device *netdev, } /** - * Handle virtual function event + * Handle admin event * * @v netdev Network device * @v evt Admin queue event descriptor * @v buf Admin queue event data buffer */ -void intelxlvf_admin_event ( struct net_device *netdev, - struct intelxl_admin_descriptor *evt, - union intelxl_admin_buffer *buf ) { +static void intelxlvf_admin_event ( struct net_device *netdev, + struct intelxl_admin_descriptor *evt, + union intelxl_admin_buffer *buf ) { struct intelxl_nic *intelxl = netdev->priv; - unsigned int vopcode = le32_to_cpu ( evt->vopcode ); + unsigned int vopcode; + + /* Ignore unrecognised events */ + if ( evt->opcode != cpu_to_le16 ( INTELXL_ADMIN_SEND_TO_VF ) ) { + DBGC ( intelxl, "INTELXL %p unrecognised event opcode " + "%#04x\n", intelxl, le16_to_cpu ( evt->opcode ) ); + return; + } /* Record command response if applicable */ + vopcode = le32_to_cpu ( evt->vopcode ); if ( vopcode == intelxl->vopcode ) { memcpy ( &intelxl->vbuf, buf, sizeof ( intelxl->vbuf ) ); intelxl->vopcode = 0; @@ -626,6 +634,7 @@ static int intelxlvf_probe ( struct pci_device *pci ) { netdev->dev = &pci->dev; memset ( intelxl, 0, sizeof ( *intelxl ) ); intelxl->intr = INTELXLVF_VFINT_DYN_CTLN ( INTELXLVF_MSIX_VECTOR ); + intelxl->handle = intelxlvf_admin_event; intelxl_init_admin ( &intelxl->command, INTELXLVF_ADMIN, &intelxlvf_admin_command_offsets ); intelxl_init_admin ( &intelxl->event, INTELXLVF_ADMIN, -- cgit v1.2.3-55-g7522 From c220b93f3145b10cf9b2a10a52bac4ee1252992f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 8 Aug 2022 14:48:25 +0100 Subject: [intelxl] Reuse admin command descriptor and buffer for VF responses Remove the large static admin data buffer structure embedded within struct intelxl_nic, and instead copy the response received via the "send to VF" admin queue event to the (already consumed and completed) admin command descriptor and data buffer. Signed-off-by: Michael Brown --- src/drivers/net/intelxl.h | 4 ---- src/drivers/net/intelxlvf.c | 28 +++++++++++++++------------- 2 files changed, 15 insertions(+), 17 deletions(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index cd0d2ee81..771932c0b 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -1092,10 +1092,6 @@ struct intelxl_nic { /** Current VF opcode */ unsigned int vopcode; - /** Current VF return value */ - int vret; - /** Current VF event data buffer */ - union intelxl_admin_buffer vbuf; /** Transmit descriptor ring */ struct intelxl_ring tx; diff --git a/src/drivers/net/intelxlvf.c b/src/drivers/net/intelxlvf.c index 6f52f1393..8774b110a 100644 --- a/src/drivers/net/intelxlvf.c +++ b/src/drivers/net/intelxlvf.c @@ -201,7 +201,7 @@ static int intelxlvf_admin_command ( struct net_device *netdev ) { } /* Check for errors */ - if ( intelxl->vret != 0 ) + if ( cmd->vret != 0 ) return -EIO; return 0; @@ -271,7 +271,9 @@ static void intelxlvf_admin_event ( struct net_device *netdev, struct intelxl_admin_descriptor *evt, union intelxl_admin_buffer *buf ) { struct intelxl_nic *intelxl = netdev->priv; + struct intelxl_admin *admin = &intelxl->command; unsigned int vopcode; + unsigned int index; /* Ignore unrecognised events */ if ( evt->opcode != cpu_to_le16 ( INTELXL_ADMIN_SEND_TO_VF ) ) { @@ -283,12 +285,14 @@ static void intelxlvf_admin_event ( struct net_device *netdev, /* Record command response if applicable */ vopcode = le32_to_cpu ( evt->vopcode ); if ( vopcode == intelxl->vopcode ) { - memcpy ( &intelxl->vbuf, buf, sizeof ( intelxl->vbuf ) ); + index = ( ( admin->index - 1 ) % INTELXL_ADMIN_NUM_DESC ); + memcpy ( &admin->desc[index], evt, sizeof ( *evt ) ); + memcpy ( &admin->buf[index], buf, sizeof ( *buf ) ); intelxl->vopcode = 0; - intelxl->vret = le32_to_cpu ( evt->vret ); - if ( intelxl->vret != 0 ) { + if ( evt->vret != 0 ) { DBGC ( intelxl, "INTELXL %p admin VF command %#x " - "error %d\n", intelxl, vopcode, intelxl->vret ); + "error %d\n", intelxl, vopcode, + le32_to_cpu ( evt->vret ) ); DBGC_HDA ( intelxl, virt_to_phys ( evt ), evt, sizeof ( *evt ) ); DBGC_HDA ( intelxl, virt_to_phys ( buf ), buf, @@ -322,7 +326,6 @@ static void intelxlvf_admin_event ( struct net_device *netdev, static int intelxlvf_admin_version ( struct net_device *netdev ) { struct intelxl_nic *intelxl = netdev->priv; struct intelxl_admin_descriptor *cmd; - struct intelxl_admin_vf_version_buffer *ver; union intelxl_admin_buffer *buf; unsigned int api; int rc; @@ -339,10 +342,9 @@ static int intelxlvf_admin_version ( struct net_device *netdev ) { /* Issue command */ if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 ) return rc; - ver = &intelxl->vbuf.ver; - api = le32_to_cpu ( ver->major ); + api = le32_to_cpu ( buf->ver.major ); DBGC ( intelxl, "INTELXL %p API v%d.%d\n", - intelxl, api, le32_to_cpu ( ver->minor ) ); + intelxl, api, le32_to_cpu ( buf->ver.minor ) ); /* Check for API compatibility */ if ( api > INTELXL_ADMIN_VF_API_MAJOR ) { @@ -363,21 +365,21 @@ static int intelxlvf_admin_version ( struct net_device *netdev ) { static int intelxlvf_admin_get_resources ( struct net_device *netdev ) { struct intelxl_nic *intelxl = netdev->priv; struct intelxl_admin_descriptor *cmd; - struct intelxl_admin_vf_get_resources_buffer *res; + union intelxl_admin_buffer *buf; int rc; /* Populate descriptor */ cmd = intelxl_admin_command_descriptor ( intelxl ); cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_GET_RESOURCES ); + buf = intelxl_admin_command_buffer ( intelxl ); /* Issue command */ if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 ) return rc; /* Parse response */ - res = &intelxl->vbuf.res; - intelxl->vsi = le16_to_cpu ( res->vsi ); - memcpy ( netdev->hw_addr, res->mac, ETH_ALEN ); + intelxl->vsi = le16_to_cpu ( buf->res.vsi ); + memcpy ( netdev->hw_addr, buf->res.mac, ETH_ALEN ); DBGC ( intelxl, "INTELXL %p VSI %#04x\n", intelxl, intelxl->vsi ); return 0; -- cgit v1.2.3-55-g7522 From fb69d1400269b60d80fc949f84a519e403c58f0e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 8 Aug 2022 14:57:40 +0100 Subject: [intelxl] Separate virtual function driver definitions Move knowledge of the virtual function data structures and admin command definitions from intelxl.h to intelxlvf.h. Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 6 +- src/drivers/net/intelxl.h | 210 +----------------------------------- src/drivers/net/intelxlvf.c | 106 +++++++++--------- src/drivers/net/intelxlvf.h | 257 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 320 insertions(+), 259 deletions(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index e31595e1c..51e888a60 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -360,8 +360,8 @@ int intelxl_admin_command ( struct intelxl_nic *intelxl ) { buf = &admin->buf[ index % INTELXL_ADMIN_NUM_DESC ]; DBGC2 ( intelxl, "INTELXL %p admin command %#x opcode %#04x", intelxl, index, le16_to_cpu ( cmd->opcode ) ); - if ( cmd->vopcode ) - DBGC2 ( intelxl, "/%#08x", le32_to_cpu ( cmd->vopcode ) ); + if ( cmd->cookie ) + DBGC2 ( intelxl, "/%#08x", le32_to_cpu ( cmd->cookie ) ); DBGC2 ( intelxl, ":\n" ); /* Sanity checks */ @@ -378,7 +378,7 @@ int intelxl_admin_command ( struct intelxl_nic *intelxl ) { } /* Populate cookie, if not being (ab)used for VF opcode */ - if ( ! cmd->vopcode ) + if ( ! cmd->cookie ) cmd->cookie = cpu_to_le32 ( index ); /* Record cookie */ diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index 771932c0b..49a825bc7 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -305,183 +305,6 @@ struct intelxl_admin_link_params { /** Link is up */ #define INTELXL_ADMIN_LINK_UP 0x01 -/** Admin queue Send Message to PF command */ -#define INTELXL_ADMIN_SEND_TO_PF 0x0801 - -/** Admin queue Send Message to VF command */ -#define INTELXL_ADMIN_SEND_TO_VF 0x0802 - -/** Admin Queue VF Version opcode */ -#define INTELXL_ADMIN_VF_VERSION 0x00000001 - -/** Admin Queue VF Version data buffer */ -struct intelxl_admin_vf_version_buffer { - /** Major version */ - uint32_t major; - /** Minor version */ - uint32_t minor; -} __attribute__ (( packed )); - -/** Admin Queue VF Reset opcode */ -#define INTELXL_ADMIN_VF_RESET 0x00000002 - -/** Admin Queue VF Get Resources opcode */ -#define INTELXL_ADMIN_VF_GET_RESOURCES 0x00000003 - -/** Admin Queue VF Get Resources data buffer */ -struct intelxl_admin_vf_get_resources_buffer { - /** Reserved */ - uint8_t reserved_a[20]; - /** VSI switching element ID */ - uint16_t vsi; - /** Reserved */ - uint8_t reserved_b[8]; - /** MAC address */ - uint8_t mac[ETH_ALEN]; -} __attribute__ (( packed )); - -/** Admin Queue VF Status Change Event opcode */ -#define INTELXL_ADMIN_VF_STATUS 0x00000011 - -/** Link status change event type */ -#define INTELXL_ADMIN_VF_STATUS_LINK 0x00000001 - -/** Link status change event data */ -struct intelxl_admin_vf_status_link { - /** Link speed */ - uint32_t speed; - /** Link status */ - uint8_t status; - /** Reserved */ - uint8_t reserved[3]; -} __attribute__ (( packed )); - -/** Admin Queue VF Status Change Event data buffer */ -struct intelxl_admin_vf_status_buffer { - /** Event type */ - uint32_t event; - /** Event data */ - union { - /** Link change event data */ - struct intelxl_admin_vf_status_link link; - } data; - /** Reserved */ - uint8_t reserved[4]; -} __attribute__ (( packed )); - -/** Admin Queue VF Configure Queues opcode */ -#define INTELXL_ADMIN_VF_CONFIGURE 0x00000006 - -/** Admin Queue VF Configure Queues data buffer */ -struct intelxl_admin_vf_configure_buffer { - /** VSI switching element ID */ - uint16_t vsi; - /** Number of queue pairs */ - uint16_t count; - /** Reserved */ - uint8_t reserved_a[4]; - /** Transmit queue */ - struct { - /** VSI switching element ID */ - uint16_t vsi; - /** Queue ID */ - uint16_t id; - /** Queue count */ - uint16_t count; - /** Reserved */ - uint8_t reserved_a[2]; - /** Base address */ - uint64_t base; - /** Reserved */ - uint8_t reserved_b[8]; - } __attribute__ (( packed )) tx; - /** Receive queue */ - struct { - /** VSI switching element ID */ - uint16_t vsi; - /** Queue ID */ - uint16_t id; - /** Queue count */ - uint32_t count; - /** Reserved */ - uint8_t reserved_a[4]; - /** Data buffer length */ - uint32_t len; - /** Maximum frame size */ - uint32_t mfs; - /** Reserved */ - uint8_t reserved_b[4]; - /** Base address */ - uint64_t base; - /** Reserved */ - uint8_t reserved_c[8]; - } __attribute__ (( packed )) rx; - /** Reserved - * - * This field exists only due to a bug in the PF driver's - * message validation logic, which causes it to miscalculate - * the expected message length. - */ - uint8_t reserved_b[64]; -} __attribute__ (( packed )); - -/** Admin Queue VF IRQ Map opcode */ -#define INTELXL_ADMIN_VF_IRQ_MAP 0x00000007 - -/** Admin Queue VF IRQ Map data buffer */ -struct intelxl_admin_vf_irq_map_buffer { - /** Number of interrupt vectors */ - uint16_t count; - /** VSI switching element ID */ - uint16_t vsi; - /** Interrupt vector ID */ - uint16_t vec; - /** Receive queue bitmap */ - uint16_t rxmap; - /** Transmit queue bitmap */ - uint16_t txmap; - /** Receive interrupt throttling index */ - uint16_t rxitr; - /** Transmit interrupt throttling index */ - uint16_t txitr; - /** Reserved - * - * This field exists only due to a bug in the PF driver's - * message validation logic, which causes it to miscalculate - * the expected message length. - */ - uint8_t reserved[12]; -} __attribute__ (( packed )); - -/** Admin Queue VF Enable Queues opcode */ -#define INTELXL_ADMIN_VF_ENABLE 0x00000008 - -/** Admin Queue VF Disable Queues opcode */ -#define INTELXL_ADMIN_VF_DISABLE 0x00000009 - -/** Admin Queue VF Enable/Disable Queues data buffer */ -struct intelxl_admin_vf_queues_buffer { - /** VSI switching element ID */ - uint16_t vsi; - /** Reserved */ - uint8_t reserved[2]; - /** Receive queue bitmask */ - uint32_t rx; - /** Transmit queue bitmask */ - uint32_t tx; -} __attribute__ (( packed )); - -/** Admin Queue VF Configure Promiscuous Mode opcode */ -#define INTELXL_ADMIN_VF_PROMISC 0x0000000e - -/** Admin Queue VF Configure Promiscuous Mode data buffer */ -struct intelxl_admin_vf_promisc_buffer { - /** VSI switching element ID */ - uint16_t vsi; - /** Flags */ - uint16_t flags; -} __attribute__ (( packed )); - /** Admin queue command parameters */ union intelxl_admin_params { /** Additional data buffer command parameters */ @@ -514,20 +337,6 @@ union intelxl_admin_buffer { struct intelxl_admin_switch_buffer sw; /** Get VSI Parameters data buffer */ struct intelxl_admin_vsi_buffer vsi; - /** VF Version data buffer */ - struct intelxl_admin_vf_version_buffer ver; - /** VF Get Resources data buffer */ - struct intelxl_admin_vf_get_resources_buffer res; - /** VF Status Change Event data buffer */ - struct intelxl_admin_vf_status_buffer stat; - /** VF Configure Queues data buffer */ - struct intelxl_admin_vf_configure_buffer cfg; - /** VF Enable/Disable Queues data buffer */ - struct intelxl_admin_vf_queues_buffer queues; - /** VF Configure Promiscuous Mode data buffer */ - struct intelxl_admin_vf_promisc_buffer promisc; - /*** VF IRQ Map data buffer */ - struct intelxl_admin_vf_irq_map_buffer irq; /** Alignment padding */ uint8_t pad[INTELXL_ALIGN]; } __attribute__ (( packed )); @@ -542,15 +351,10 @@ struct intelxl_admin_descriptor { uint16_t len; /** Return value */ uint16_t ret; - /** Opaque cookie / VF opcode */ - union { - /** Cookie */ - uint32_t cookie; - /** VF opcode */ - uint32_t vopcode; - }; - /** VF return value */ - int32_t vret; + /** Opaque cookie */ + uint32_t cookie; + /** Reserved */ + uint8_t reserved[4]; /** Parameters */ union intelxl_admin_params params; } __attribute__ (( packed )); @@ -611,12 +415,6 @@ intelxl_init_admin ( struct intelxl_admin *admin, unsigned int base, /** Admin queue API major version */ #define INTELXL_ADMIN_API_MAJOR 1 -/** Admin queue VF API major version */ -#define INTELXL_ADMIN_VF_API_MAJOR 1 - -/** Admin queue VF API minor version */ -#define INTELXL_ADMIN_VF_API_MINOR 0 - /****************************************************************************** * * Transmit and receive queue context diff --git a/src/drivers/net/intelxlvf.c b/src/drivers/net/intelxlvf.c index 8774b110a..3567be456 100644 --- a/src/drivers/net/intelxlvf.c +++ b/src/drivers/net/intelxlvf.c @@ -109,13 +109,13 @@ static int intelxlvf_reset_wait_active ( struct intelxl_nic *intelxl ) { * @ret rc Return status code */ static int intelxlvf_reset_admin ( struct intelxl_nic *intelxl ) { - struct intelxl_admin_descriptor *cmd; + struct intelxlvf_admin_descriptor *cmd; int rc; /* Populate descriptor */ - cmd = intelxl_admin_command_descriptor ( intelxl ); - cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_SEND_TO_PF ); - cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_RESET ); + cmd = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXLVF_ADMIN_SEND_TO_PF ); + cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_RESET ); /* Issue command */ if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) @@ -173,13 +173,15 @@ static const struct intelxl_admin_offsets intelxlvf_admin_event_offsets = { static int intelxlvf_admin_command ( struct net_device *netdev ) { struct intelxl_nic *intelxl = netdev->priv; struct intelxl_admin *admin = &intelxl->command; - struct intelxl_admin_descriptor *cmd; + struct intelxl_admin_descriptor *xlcmd; + struct intelxlvf_admin_descriptor *cmd; unsigned int i; int rc; /* Populate descriptor */ - cmd = &admin->desc[ admin->index % INTELXL_ADMIN_NUM_DESC ]; - cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_SEND_TO_PF ); + xlcmd = &admin->desc[ admin->index % INTELXL_ADMIN_NUM_DESC ]; + cmd = container_of ( xlcmd, struct intelxlvf_admin_descriptor, xl ); + cmd->opcode = cpu_to_le16 ( INTELXLVF_ADMIN_SEND_TO_PF ); /* Record opcode */ intelxl->vopcode = le32_to_cpu ( cmd->vopcode ); @@ -222,7 +224,7 @@ static int intelxlvf_admin_command ( struct net_device *netdev ) { * @v link Link status */ static void intelxlvf_admin_link ( struct net_device *netdev, - struct intelxl_admin_vf_status_link *link ) { + struct intelxlvf_admin_status_link *link ) { struct intelxl_nic *intelxl = netdev->priv; DBGC ( intelxl, "INTELXL %p link %#02x speed %#02x\n", intelxl, @@ -244,12 +246,12 @@ static void intelxlvf_admin_link ( struct net_device *netdev, */ static void intelxlvf_admin_status ( struct net_device *netdev, - struct intelxl_admin_vf_status_buffer *stat ) { + struct intelxlvf_admin_status_buffer *stat ) { struct intelxl_nic *intelxl = netdev->priv; /* Handle event */ switch ( stat->event ) { - case cpu_to_le32 ( INTELXL_ADMIN_VF_STATUS_LINK ): + case cpu_to_le32 ( INTELXLVF_ADMIN_STATUS_LINK ): intelxlvf_admin_link ( netdev, &stat->data.link ); break; default: @@ -264,19 +266,23 @@ intelxlvf_admin_status ( struct net_device *netdev, * Handle admin event * * @v netdev Network device - * @v evt Admin queue event descriptor - * @v buf Admin queue event data buffer + * @v xlevt Admin queue event descriptor + * @v xlbuf Admin queue event data buffer */ static void intelxlvf_admin_event ( struct net_device *netdev, - struct intelxl_admin_descriptor *evt, - union intelxl_admin_buffer *buf ) { + struct intelxl_admin_descriptor *xlevt, + union intelxl_admin_buffer *xlbuf ) { struct intelxl_nic *intelxl = netdev->priv; struct intelxl_admin *admin = &intelxl->command; + struct intelxlvf_admin_descriptor *evt = + container_of ( xlevt, struct intelxlvf_admin_descriptor, xl ); + union intelxlvf_admin_buffer *buf = + container_of ( xlbuf, union intelxlvf_admin_buffer, xl ); unsigned int vopcode; unsigned int index; /* Ignore unrecognised events */ - if ( evt->opcode != cpu_to_le16 ( INTELXL_ADMIN_SEND_TO_VF ) ) { + if ( evt->opcode != cpu_to_le16 ( INTELXLVF_ADMIN_SEND_TO_VF ) ) { DBGC ( intelxl, "INTELXL %p unrecognised event opcode " "%#04x\n", intelxl, le16_to_cpu ( evt->opcode ) ); return; @@ -303,7 +309,7 @@ static void intelxlvf_admin_event ( struct net_device *netdev, /* Handle unsolicited events */ switch ( vopcode ) { - case INTELXL_ADMIN_VF_STATUS: + case INTELXLVF_ADMIN_STATUS: intelxlvf_admin_status ( netdev, &buf->stat ); break; default: @@ -325,19 +331,19 @@ static void intelxlvf_admin_event ( struct net_device *netdev, */ static int intelxlvf_admin_version ( struct net_device *netdev ) { struct intelxl_nic *intelxl = netdev->priv; - struct intelxl_admin_descriptor *cmd; - union intelxl_admin_buffer *buf; + struct intelxlvf_admin_descriptor *cmd; + union intelxlvf_admin_buffer *buf; unsigned int api; int rc; /* Populate descriptor */ - cmd = intelxl_admin_command_descriptor ( intelxl ); - cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_VERSION ); + cmd = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_VERSION ); cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); cmd->len = cpu_to_le16 ( sizeof ( buf->ver ) ); - buf = intelxl_admin_command_buffer ( intelxl ); - buf->ver.major = cpu_to_le32 ( INTELXL_ADMIN_VF_API_MAJOR ); - buf->ver.minor = cpu_to_le32 ( INTELXL_ADMIN_VF_API_MINOR ); + buf = intelxlvf_admin_command_buffer ( intelxl ); + buf->ver.major = cpu_to_le32 ( INTELXLVF_ADMIN_API_MAJOR ); + buf->ver.minor = cpu_to_le32 ( INTELXLVF_ADMIN_API_MINOR ); /* Issue command */ if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 ) @@ -347,7 +353,7 @@ static int intelxlvf_admin_version ( struct net_device *netdev ) { intelxl, api, le32_to_cpu ( buf->ver.minor ) ); /* Check for API compatibility */ - if ( api > INTELXL_ADMIN_VF_API_MAJOR ) { + if ( api > INTELXLVF_ADMIN_API_MAJOR ) { DBGC ( intelxl, "INTELXL %p unsupported API v%d\n", intelxl, api ); return -ENOTSUP; @@ -364,14 +370,14 @@ static int intelxlvf_admin_version ( struct net_device *netdev ) { */ static int intelxlvf_admin_get_resources ( struct net_device *netdev ) { struct intelxl_nic *intelxl = netdev->priv; - struct intelxl_admin_descriptor *cmd; - union intelxl_admin_buffer *buf; + struct intelxlvf_admin_descriptor *cmd; + union intelxlvf_admin_buffer *buf; int rc; /* Populate descriptor */ - cmd = intelxl_admin_command_descriptor ( intelxl ); - cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_GET_RESOURCES ); - buf = intelxl_admin_command_buffer ( intelxl ); + cmd = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_GET_RESOURCES ); + buf = intelxlvf_admin_command_buffer ( intelxl ); /* Issue command */ if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 ) @@ -400,16 +406,16 @@ static int intelxlvf_admin_get_resources ( struct net_device *netdev ) { */ static int intelxlvf_admin_configure ( struct net_device *netdev ) { struct intelxl_nic *intelxl = netdev->priv; - struct intelxl_admin_descriptor *cmd; - union intelxl_admin_buffer *buf; + struct intelxlvf_admin_descriptor *cmd; + union intelxlvf_admin_buffer *buf; int rc; /* Populate descriptor */ - cmd = intelxl_admin_command_descriptor ( intelxl ); - cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_CONFIGURE ); + cmd = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_CONFIGURE ); cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); cmd->len = cpu_to_le16 ( sizeof ( buf->cfg ) ); - buf = intelxl_admin_command_buffer ( intelxl ); + buf = intelxlvf_admin_command_buffer ( intelxl ); buf->cfg.vsi = cpu_to_le16 ( intelxl->vsi ); buf->cfg.count = cpu_to_le16 ( 1 ); buf->cfg.tx.vsi = cpu_to_le16 ( intelxl->vsi ); @@ -438,16 +444,16 @@ static int intelxlvf_admin_configure ( struct net_device *netdev ) { */ static int intelxlvf_admin_irq_map ( struct net_device *netdev ) { struct intelxl_nic *intelxl = netdev->priv; - struct intelxl_admin_descriptor *cmd; - union intelxl_admin_buffer *buf; + struct intelxlvf_admin_descriptor *cmd; + union intelxlvf_admin_buffer *buf; int rc; /* Populate descriptor */ - cmd = intelxl_admin_command_descriptor ( intelxl ); - cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_IRQ_MAP ); + cmd = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_IRQ_MAP ); cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); cmd->len = cpu_to_le16 ( sizeof ( buf->irq ) ); - buf = intelxl_admin_command_buffer ( intelxl ); + buf = intelxlvf_admin_command_buffer ( intelxl ); buf->irq.count = cpu_to_le16 ( 1 ); buf->irq.vsi = cpu_to_le16 ( intelxl->vsi ); buf->irq.vec = cpu_to_le16 ( INTELXLVF_MSIX_VECTOR ); @@ -470,17 +476,17 @@ static int intelxlvf_admin_irq_map ( struct net_device *netdev ) { */ static int intelxlvf_admin_queues ( struct net_device *netdev, int enable ) { struct intelxl_nic *intelxl = netdev->priv; - struct intelxl_admin_descriptor *cmd; - union intelxl_admin_buffer *buf; + struct intelxlvf_admin_descriptor *cmd; + union intelxlvf_admin_buffer *buf; int rc; /* Populate descriptor */ - cmd = intelxl_admin_command_descriptor ( intelxl ); - cmd->vopcode = ( enable ? cpu_to_le32 ( INTELXL_ADMIN_VF_ENABLE ) : - cpu_to_le32 ( INTELXL_ADMIN_VF_DISABLE ) ); + cmd = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->vopcode = ( enable ? cpu_to_le32 ( INTELXLVF_ADMIN_ENABLE ) : + cpu_to_le32 ( INTELXLVF_ADMIN_DISABLE ) ); cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); cmd->len = cpu_to_le16 ( sizeof ( buf->queues ) ); - buf = intelxl_admin_command_buffer ( intelxl ); + buf = intelxlvf_admin_command_buffer ( intelxl ); buf->queues.vsi = cpu_to_le16 ( intelxl->vsi ); buf->queues.rx = cpu_to_le32 ( 1 ); buf->queues.tx = cpu_to_le32 ( 1 ); @@ -500,16 +506,16 @@ static int intelxlvf_admin_queues ( struct net_device *netdev, int enable ) { */ static int intelxlvf_admin_promisc ( struct net_device *netdev ) { struct intelxl_nic *intelxl = netdev->priv; - struct intelxl_admin_descriptor *cmd; - union intelxl_admin_buffer *buf; + struct intelxlvf_admin_descriptor *cmd; + union intelxlvf_admin_buffer *buf; int rc; /* Populate descriptor */ - cmd = intelxl_admin_command_descriptor ( intelxl ); - cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_PROMISC ); + cmd = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_PROMISC ); cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); cmd->len = cpu_to_le16 ( sizeof ( buf->promisc ) ); - buf = intelxl_admin_command_buffer ( intelxl ); + buf = intelxlvf_admin_command_buffer ( intelxl ); buf->promisc.vsi = cpu_to_le16 ( intelxl->vsi ); buf->promisc.flags = cpu_to_le16 ( INTELXL_ADMIN_PROMISC_FL_UNICAST | INTELXL_ADMIN_PROMISC_FL_MULTICAST ); diff --git a/src/drivers/net/intelxlvf.h b/src/drivers/net/intelxlvf.h index 58ade11e9..5c2227577 100644 --- a/src/drivers/net/intelxlvf.h +++ b/src/drivers/net/intelxlvf.h @@ -70,6 +70,263 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Maximum time to wait for a VF admin request to complete */ #define INTELXLVF_ADMIN_MAX_WAIT_MS 2000 +/** Admin queue Send Message to PF command */ +#define INTELXLVF_ADMIN_SEND_TO_PF 0x0801 + +/** Admin queue Send Message to VF command */ +#define INTELXLVF_ADMIN_SEND_TO_VF 0x0802 + +/** Admin Queue VF Version opcode */ +#define INTELXLVF_ADMIN_VERSION 0x00000001 + +/** Admin Queue VF Version data buffer */ +struct intelxlvf_admin_version_buffer { + /** Major version */ + uint32_t major; + /** Minor version */ + uint32_t minor; +} __attribute__ (( packed )); + +/** Admin queue VF API major version */ +#define INTELXLVF_ADMIN_API_MAJOR 1 + +/** Admin queue VF API minor version */ +#define INTELXLVF_ADMIN_API_MINOR 0 + +/** Admin Queue VF Reset opcode */ +#define INTELXLVF_ADMIN_RESET 0x00000002 + +/** Admin Queue VF Get Resources opcode */ +#define INTELXLVF_ADMIN_GET_RESOURCES 0x00000003 + +/** Admin Queue VF Get Resources data buffer */ +struct intelxlvf_admin_get_resources_buffer { + /** Reserved */ + uint8_t reserved_a[20]; + /** VSI switching element ID */ + uint16_t vsi; + /** Reserved */ + uint8_t reserved_b[8]; + /** MAC address */ + uint8_t mac[ETH_ALEN]; +} __attribute__ (( packed )); + +/** Admin Queue VF Status Change Event opcode */ +#define INTELXLVF_ADMIN_STATUS 0x00000011 + +/** Link status change event type */ +#define INTELXLVF_ADMIN_STATUS_LINK 0x00000001 + +/** Link status change event data */ +struct intelxlvf_admin_status_link { + /** Link speed */ + uint32_t speed; + /** Link status */ + uint8_t status; + /** Reserved */ + uint8_t reserved[3]; +} __attribute__ (( packed )); + +/** Admin Queue VF Status Change Event data buffer */ +struct intelxlvf_admin_status_buffer { + /** Event type */ + uint32_t event; + /** Event data */ + union { + /** Link change event data */ + struct intelxlvf_admin_status_link link; + } data; + /** Reserved */ + uint8_t reserved[4]; +} __attribute__ (( packed )); + +/** Admin Queue VF Configure Queues opcode */ +#define INTELXLVF_ADMIN_CONFIGURE 0x00000006 + +/** Admin Queue VF Configure Queues data buffer */ +struct intelxlvf_admin_configure_buffer { + /** VSI switching element ID */ + uint16_t vsi; + /** Number of queue pairs */ + uint16_t count; + /** Reserved */ + uint8_t reserved_a[4]; + /** Transmit queue */ + struct { + /** VSI switching element ID */ + uint16_t vsi; + /** Queue ID */ + uint16_t id; + /** Queue count */ + uint16_t count; + /** Reserved */ + uint8_t reserved_a[2]; + /** Base address */ + uint64_t base; + /** Reserved */ + uint8_t reserved_b[8]; + } __attribute__ (( packed )) tx; + /** Receive queue */ + struct { + /** VSI switching element ID */ + uint16_t vsi; + /** Queue ID */ + uint16_t id; + /** Queue count */ + uint32_t count; + /** Reserved */ + uint8_t reserved_a[4]; + /** Data buffer length */ + uint32_t len; + /** Maximum frame size */ + uint32_t mfs; + /** Reserved */ + uint8_t reserved_b[4]; + /** Base address */ + uint64_t base; + /** Reserved */ + uint8_t reserved_c[8]; + } __attribute__ (( packed )) rx; + /** Reserved + * + * This field exists only due to a bug in the PF driver's + * message validation logic, which causes it to miscalculate + * the expected message length. + */ + uint8_t reserved_b[64]; +} __attribute__ (( packed )); + +/** Admin Queue VF IRQ Map opcode */ +#define INTELXLVF_ADMIN_IRQ_MAP 0x00000007 + +/** Admin Queue VF IRQ Map data buffer */ +struct intelxlvf_admin_irq_map_buffer { + /** Number of interrupt vectors */ + uint16_t count; + /** VSI switching element ID */ + uint16_t vsi; + /** Interrupt vector ID */ + uint16_t vec; + /** Receive queue bitmap */ + uint16_t rxmap; + /** Transmit queue bitmap */ + uint16_t txmap; + /** Receive interrupt throttling index */ + uint16_t rxitr; + /** Transmit interrupt throttling index */ + uint16_t txitr; + /** Reserved + * + * This field exists only due to a bug in the PF driver's + * message validation logic, which causes it to miscalculate + * the expected message length. + */ + uint8_t reserved[12]; +} __attribute__ (( packed )); + +/** Admin Queue VF Enable Queues opcode */ +#define INTELXLVF_ADMIN_ENABLE 0x00000008 + +/** Admin Queue VF Disable Queues opcode */ +#define INTELXLVF_ADMIN_DISABLE 0x00000009 + +/** Admin Queue VF Enable/Disable Queues data buffer */ +struct intelxlvf_admin_queues_buffer { + /** VSI switching element ID */ + uint16_t vsi; + /** Reserved */ + uint8_t reserved[2]; + /** Receive queue bitmask */ + uint32_t rx; + /** Transmit queue bitmask */ + uint32_t tx; +} __attribute__ (( packed )); + +/** Admin Queue VF Configure Promiscuous Mode opcode */ +#define INTELXLVF_ADMIN_PROMISC 0x0000000e + +/** Admin Queue VF Configure Promiscuous Mode data buffer */ +struct intelxlvf_admin_promisc_buffer { + /** VSI switching element ID */ + uint16_t vsi; + /** Flags */ + uint16_t flags; +} __attribute__ (( packed )); + +/** Admin queue data buffer */ +union intelxlvf_admin_buffer { + /** Original 40 Gigabit Ethernet data buffer */ + union intelxl_admin_buffer xl; + /** VF Version data buffer */ + struct intelxlvf_admin_version_buffer ver; + /** VF Get Resources data buffer */ + struct intelxlvf_admin_get_resources_buffer res; + /** VF Status Change Event data buffer */ + struct intelxlvf_admin_status_buffer stat; + /** VF Configure Queues data buffer */ + struct intelxlvf_admin_configure_buffer cfg; + /** VF Enable/Disable Queues data buffer */ + struct intelxlvf_admin_queues_buffer queues; + /** VF Configure Promiscuous Mode data buffer */ + struct intelxlvf_admin_promisc_buffer promisc; + /** VF IRQ Map data buffer */ + struct intelxlvf_admin_irq_map_buffer irq; +} __attribute__ (( packed )); + +/** Admin queue descriptor */ +struct intelxlvf_admin_descriptor { + /** Transparent union */ + union { + /** Original 40 Gigabit Ethernet descriptor */ + struct intelxl_admin_descriptor xl; + /** Transparent struct */ + struct { + /** Flags */ + uint16_t flags; + /** Opcode */ + uint16_t opcode; + /** Data length */ + uint16_t len; + /** Return value */ + uint16_t ret; + /** VF opcode */ + uint32_t vopcode; + /** VF return value */ + int32_t vret; + /** Parameters */ + union intelxl_admin_params params; + } __attribute__ (( packed )); + } __attribute__ (( packed )); +} __attribute__ (( packed )); + +/** + * Get next admin command queue descriptor + * + * @v intelxl Intel device + * @ret cmd Command descriptor + */ +struct intelxlvf_admin_descriptor * +intelxlvf_admin_command_descriptor ( struct intelxl_nic *intelxl ) { + struct intelxl_admin_descriptor *xlcmd = + intelxl_admin_command_descriptor ( intelxl ); + + return container_of ( xlcmd, struct intelxlvf_admin_descriptor, xl ); +} + +/** + * Get next admin command queue data buffer + * + * @v intelxl Intel device + * @ret buf Data buffer + */ +static inline __attribute__ (( always_inline )) union intelxlvf_admin_buffer * +intelxlvf_admin_command_buffer ( struct intelxl_nic *intelxl ) { + union intelxl_admin_buffer *xlbuf = + intelxl_admin_command_buffer ( intelxl ); + + return container_of ( xlbuf, union intelxlvf_admin_buffer, xl ); +} + /** VF Reset Status Register */ #define INTELXLVF_VFGEN_RSTAT 0x8800 #define INTELXLVF_VFGEN_RSTAT_VFR_STATE(x) ( (x) & 0x3 ) -- cgit v1.2.3-55-g7522 From f0ea19b23858bfe59f7ee060aa640820d0f65087 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 11 Aug 2022 15:21:44 +0100 Subject: [intelxl] Increase data buffer size to 4kB At least one E810 admin queue command (Query Default Scheduling Tree Topology) insists upon being provided with a 4kB data buffer, even when the data to be returned is much smaller. Work around this requirement by increasing the admin queue data buffer size to 4kB. Signed-off-by: Michael Brown --- src/drivers/net/intelxl.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index 49a825bc7..28893945d 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -329,6 +329,9 @@ union intelxl_admin_params { struct intelxl_admin_link_params link; } __attribute__ (( packed )); +/** Maximum size of a data buffer */ +#define INTELXL_ADMIN_BUFFER_SIZE 0x1000 + /** Admin queue data buffer */ union intelxl_admin_buffer { /** Driver Version data buffer */ @@ -337,8 +340,8 @@ union intelxl_admin_buffer { struct intelxl_admin_switch_buffer sw; /** Get VSI Parameters data buffer */ struct intelxl_admin_vsi_buffer vsi; - /** Alignment padding */ - uint8_t pad[INTELXL_ALIGN]; + /** Maximum buffer size */ + uint8_t pad[INTELXL_ADMIN_BUFFER_SIZE]; } __attribute__ (( packed )); /** Admin queue descriptor */ -- cgit v1.2.3-55-g7522 From faf26bf8b882a6d56cc76686e2f0f3b236541f9d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 17 Mar 2022 13:01:50 +0000 Subject: [intelxl] Allow expected admin queue command errors to be silenced The "clear PXE mode" admin queue command will return an EEXIST error if the device is already in non-PXE mode, but there is no other admin queue command that can be used to determine whether the device has already been switched into non-PXE mode. Provide a mechanism to allow expected errors from a command to be silenced, to allow the "clear PXE mode" command to be cleanly used without needing to first check the GLLAN_RCTL_0 register value. Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index 51e888a60..c73285a61 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -348,6 +348,7 @@ int intelxl_admin_command ( struct intelxl_nic *intelxl ) { union intelxl_admin_buffer *buf; uint64_t address; uint32_t cookie; + uint16_t silence; unsigned int index; unsigned int tail; unsigned int i; @@ -364,11 +365,14 @@ int intelxl_admin_command ( struct intelxl_nic *intelxl ) { DBGC2 ( intelxl, "/%#08x", le32_to_cpu ( cmd->cookie ) ); DBGC2 ( intelxl, ":\n" ); + /* Allow expected errors to be silenced */ + silence = cmd->ret; + cmd->ret = 0; + /* Sanity checks */ assert ( ! ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_DD ) ) ); assert ( ! ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_CMP ) ) ); assert ( ! ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_ERR ) ) ); - assert ( cmd->ret == 0 ); /* Populate data buffer address if applicable */ if ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ) ) { @@ -419,8 +423,8 @@ int intelxl_admin_command ( struct intelxl_nic *intelxl ) { goto err; } - /* Check for errors */ - if ( cmd->ret != 0 ) { + /* Check for unexpected errors */ + if ( ( cmd->ret != 0 ) && ( cmd->ret != silence ) ) { DBGC ( intelxl, "INTELXL %p admin command %#x error " "%d\n", intelxl, index, le16_to_cpu ( cmd->ret ) ); -- cgit v1.2.3-55-g7522 From 99242bbe2ead2d36eff65aefc2251e822cc4b2c6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 17 Mar 2022 13:11:21 +0000 Subject: [intelxl] Always issue "clear PXE mode" admin queue command Remove knowledge of the GLLAN_RCTL_0 register (which changes location between the XL810 and E810 register maps), and instead unconditionally issue the "clear PXE mode" command with the EEXIST error silenced. Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 17 ++++++++--------- src/drivers/net/intelxl.h | 7 +++---- 2 files changed, 11 insertions(+), 13 deletions(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index c73285a61..3cba4ccc3 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -544,20 +544,12 @@ static int intelxl_admin_shutdown ( struct intelxl_nic *intelxl ) { static int intelxl_admin_clear_pxe ( struct intelxl_nic *intelxl ) { struct intelxl_admin_descriptor *cmd; struct intelxl_admin_clear_pxe_params *pxe; - uint32_t gllan_rctl_0; int rc; - /* Do nothing if device is already out of PXE mode */ - gllan_rctl_0 = readl ( intelxl->regs + INTELXL_GLLAN_RCTL_0 ); - if ( ! ( gllan_rctl_0 & INTELXL_GLLAN_RCTL_0_PXE_MODE ) ) { - DBGC2 ( intelxl, "INTELXL %p already in non-PXE mode\n", - intelxl ); - return 0; - } - /* Populate descriptor */ cmd = intelxl_admin_command_descriptor ( intelxl ); cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_CLEAR_PXE ); + cmd->ret = cpu_to_le16 ( INTELXL_ADMIN_EEXIST ); pxe = &cmd->params.pxe; pxe->magic = INTELXL_ADMIN_CLEAR_PXE_MAGIC; @@ -565,6 +557,13 @@ static int intelxl_admin_clear_pxe ( struct intelxl_nic *intelxl ) { if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) return rc; + /* Check for expected errors */ + if ( cmd->ret == cpu_to_le16 ( INTELXL_ADMIN_EEXIST ) ) { + DBGC ( intelxl, "INTELXL %p already in non-PXE mode\n", + intelxl ); + return 0; + } + return 0; } diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index 28893945d..7e4932619 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -377,6 +377,9 @@ struct intelxl_admin_descriptor { /** Admin descriptor uses data buffer */ #define INTELXL_ADMIN_FL_BUF 0x1000 +/** Error: attempt to create something that already exists */ +#define INTELXL_ADMIN_EEXIST 13 + /** Admin queue */ struct intelxl_admin { /** Descriptors */ @@ -577,10 +580,6 @@ struct intelxl_context_rx { /** Queue Tail Pointer Register (offset) */ #define INTELXL_QXX_TAIL 0x8000 -/** Global RLAN Control 0 register */ -#define INTELXL_GLLAN_RCTL_0 0x12a500 -#define INTELXL_GLLAN_RCTL_0_PXE_MODE 0x00000001UL /**< PXE mode */ - /** Transmit data descriptor */ struct intelxl_tx_data_descriptor { /** Buffer address */ -- cgit v1.2.3-55-g7522 From d6e36a2d736efaa9e44784bc58f4d90356774971 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 12 Aug 2022 12:48:35 +0100 Subject: [intelxl] Set maximum frame size to 9728 bytes as per datasheet The PRTGL_SAH register contains the current maximum frame size, and is not guaranteed on reset to contain the actual maximum frame size supported by the hardware, which the datasheet specifies as 9728 bytes (including the 4-byte CRC). Set the maximum packet size to a hardcoded 9728 bytes instead of reading from the PRTGL_SAH register. Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 10 ++-------- src/drivers/net/intelxl.h | 6 ++++-- 2 files changed, 6 insertions(+), 10 deletions(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index 3cba4ccc3..0f5e70d76 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -63,8 +63,6 @@ static int intelxl_fetch_mac ( struct intelxl_nic *intelxl, union intelxl_receive_address mac; uint32_t prtpm_sal; uint32_t prtpm_sah; - uint32_t prtgl_sah; - size_t mfs; /* Read NVM-loaded address */ prtpm_sal = readl ( intelxl->regs + INTELXL_PRTPM_SAL ); @@ -84,11 +82,6 @@ static int intelxl_fetch_mac ( struct intelxl_nic *intelxl, intelxl, eth_ntoa ( mac.raw ) ); memcpy ( netdev->hw_addr, mac.raw, ETH_ALEN ); - /* Get maximum frame size */ - prtgl_sah = readl ( intelxl->regs + INTELXL_PRTGL_SAH ); - mfs = INTELXL_PRTGL_SAH_MFS_GET ( prtgl_sah ); - netdev->max_pkt_len = ( mfs - 4 /* CRC */ ); - return 0; } @@ -1363,7 +1356,7 @@ static int intelxl_open ( struct net_device *netdev ) { memcpy ( mac.raw, netdev->ll_addr, sizeof ( mac.raw ) ); prtgl_sal = le32_to_cpu ( mac.reg.low ); prtgl_sah = ( le32_to_cpu ( mac.reg.high ) | - INTELXL_PRTGL_SAH_MFS_SET ( intelxl->mfs ) ); + INTELXL_PRTGL_SAH_MFS ( intelxl->mfs ) ); writel ( prtgl_sal, intelxl->regs + INTELXL_PRTGL_SAL ); writel ( prtgl_sah, intelxl->regs + INTELXL_PRTGL_SAH ); @@ -1639,6 +1632,7 @@ static int intelxl_probe ( struct pci_device *pci ) { goto err_alloc; } netdev_init ( netdev, &intelxl_operations ); + netdev->max_pkt_len = INTELXL_MAX_PKT_LEN; intelxl = netdev->priv; pci_set_drvdata ( pci, netdev ); netdev->dev = &pci->dev; diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index 7e4932619..ddd3e888c 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -750,6 +750,9 @@ intelxl_init_ring ( struct intelxl_ring *ring, unsigned int count, size_t len, */ #define INTELXL_RX_FILL 16 +/** Maximum packet length (excluding CRC) */ +#define INTELXL_MAX_PKT_LEN ( 9728 - 4 /* CRC */ ) + /****************************************************************************** * * Top level @@ -826,8 +829,7 @@ intelxl_init_ring ( struct intelxl_ring *ring, unsigned int count, size_t len, /** Port MAC Address High Register */ #define INTELXL_PRTGL_SAH 0x1e2140 -#define INTELXL_PRTGL_SAH_MFS_GET(x) ( (x) >> 16 ) /**< Max frame size */ -#define INTELXL_PRTGL_SAH_MFS_SET(x) ( (x) << 16 ) /**< Max frame size */ +#define INTELXL_PRTGL_SAH_MFS(x) ( (x) << 16 ) /**< Max frame size */ /** Physical Function MAC Address Low Register */ #define INTELXL_PRTPM_SAL 0x1e4440 -- cgit v1.2.3-55-g7522 From 06467ee70fd4750ecd2ae324f66055ff261cb713 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 17 Mar 2022 12:37:45 +0000 Subject: [intelxl] Defer fetching MAC address until after opening admin queue Allow for the MAC address to be fetched using an admin queue command, instead of reading the PRTPM_SA[HL] registers directly. Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index 0f5e70d76..438657fd3 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -1688,10 +1688,6 @@ static int intelxl_probe ( struct pci_device *pci ) { intelxl, intelxl->pf, intelxl->port, intelxl->base, INTELXL_PFLAN_QALLOC_LASTQ ( pflan_qalloc ) ); - /* Fetch MAC address and maximum frame size */ - if ( ( rc = intelxl_fetch_mac ( intelxl, netdev ) ) != 0 ) - goto err_fetch_mac; - /* Enable MSI-X dummy interrupt */ if ( ( rc = intelxl_msix_enable ( intelxl, pci, INTELXL_MSIX_VECTOR ) ) != 0 ) @@ -1725,6 +1721,10 @@ static int intelxl_probe ( struct pci_device *pci ) { if ( ( rc = intelxl_admin_promisc ( intelxl ) ) != 0 ) goto err_admin_promisc; + /* Fetch MAC address and maximum frame size */ + if ( ( rc = intelxl_fetch_mac ( intelxl, netdev ) ) != 0 ) + goto err_fetch_mac; + /* Configure queue register addresses */ intelxl->tx.reg = INTELXL_QTX ( intelxl->queue ); intelxl->tx.tail = ( intelxl->tx.reg + INTELXL_QXX_TAIL ); @@ -1756,6 +1756,7 @@ static int intelxl_probe ( struct pci_device *pci ) { unregister_netdev ( netdev ); err_register_netdev: + err_fetch_mac: err_admin_promisc: err_admin_vsi: err_admin_switch: @@ -1766,7 +1767,6 @@ static int intelxl_probe ( struct pci_device *pci ) { err_open_admin: intelxl_msix_disable ( intelxl, pci, INTELXL_MSIX_VECTOR ); err_msix: - err_fetch_mac: pci_reset ( pci, intelxl->exp ); err_exp: iounmap ( intelxl->regs ); -- cgit v1.2.3-55-g7522 From 727b034f111ffbec26ecad0257871b8ade5cbb67 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 18 Mar 2022 12:41:33 +0000 Subject: [intelxl] Use admin queue to get port MAC address Remove knowledge of the PRTPM_SA[HL] registers, and instead use the admin queue to retrieve the MAC address. Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 97 +++++++++++++++++++++++++---------------------- src/drivers/net/intelxl.h | 36 +++++++++++++++--- 2 files changed, 82 insertions(+), 51 deletions(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index 438657fd3..dcc8db562 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -44,47 +44,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -/****************************************************************************** - * - * MAC address - * - ****************************************************************************** - */ - -/** - * Fetch initial MAC address and maximum frame size - * - * @v intelxl Intel device - * @v netdev Network device - * @ret rc Return status code - */ -static int intelxl_fetch_mac ( struct intelxl_nic *intelxl, - struct net_device *netdev ) { - union intelxl_receive_address mac; - uint32_t prtpm_sal; - uint32_t prtpm_sah; - - /* Read NVM-loaded address */ - prtpm_sal = readl ( intelxl->regs + INTELXL_PRTPM_SAL ); - prtpm_sah = readl ( intelxl->regs + INTELXL_PRTPM_SAH ); - mac.reg.low = cpu_to_le32 ( prtpm_sal ); - mac.reg.high = cpu_to_le32 ( prtpm_sah ); - - /* Check that address is valid */ - if ( ! is_valid_ether_addr ( mac.raw ) ) { - DBGC ( intelxl, "INTELXL %p has invalid MAC address (%s)\n", - intelxl, eth_ntoa ( mac.raw ) ); - return -ENOENT; - } - - /* Copy MAC address */ - DBGC ( intelxl, "INTELXL %p has autoloaded MAC address %s\n", - intelxl, eth_ntoa ( mac.raw ) ); - memcpy ( netdev->hw_addr, mac.raw, ETH_ALEN ); - - return 0; -} - /****************************************************************************** * * MSI-X interrupts @@ -528,6 +487,54 @@ static int intelxl_admin_shutdown ( struct intelxl_nic *intelxl ) { return 0; } +/** + * Get MAC address + * + * @v netdev Network device + * @ret rc Return status code + */ +static int intelxl_admin_mac_read ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct intelxl_admin_descriptor *cmd; + struct intelxl_admin_mac_read_params *read; + union intelxl_admin_buffer *buf; + uint8_t *mac; + int rc; + + /* Populate descriptor */ + cmd = intelxl_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_MAC_READ ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->mac_read ) ); + read = &cmd->params.mac_read; + buf = intelxl_admin_command_buffer ( intelxl ); + mac = buf->mac_read.pf; + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + /* Check that MAC address is present in response */ + if ( ! ( read->valid & INTELXL_ADMIN_MAC_READ_VALID_LAN ) ) { + DBGC ( intelxl, "INTELXL %p has no MAC address\n", intelxl ); + return -ENOENT; + } + + /* Check that address is valid */ + if ( ! is_valid_ether_addr ( mac ) ) { + DBGC ( intelxl, "INTELXL %p has invalid MAC address (%s)\n", + intelxl, eth_ntoa ( mac ) ); + return -ENOENT; + } + + /* Copy MAC address */ + DBGC ( intelxl, "INTELXL %p has MAC address %s\n", + intelxl, eth_ntoa ( mac ) ); + memcpy ( netdev->hw_addr, mac, ETH_ALEN ); + + return 0; +} + /** * Clear PXE mode * @@ -1721,9 +1728,9 @@ static int intelxl_probe ( struct pci_device *pci ) { if ( ( rc = intelxl_admin_promisc ( intelxl ) ) != 0 ) goto err_admin_promisc; - /* Fetch MAC address and maximum frame size */ - if ( ( rc = intelxl_fetch_mac ( intelxl, netdev ) ) != 0 ) - goto err_fetch_mac; + /* Get MAC address */ + if ( ( rc = intelxl_admin_mac_read ( netdev ) ) != 0 ) + goto err_admin_mac_read; /* Configure queue register addresses */ intelxl->tx.reg = INTELXL_QTX ( intelxl->queue ); @@ -1756,7 +1763,7 @@ static int intelxl_probe ( struct pci_device *pci ) { unregister_netdev ( netdev ); err_register_netdev: - err_fetch_mac: + err_admin_mac_read: err_admin_promisc: err_admin_vsi: err_admin_switch: diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index ddd3e888c..610b30140 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -145,6 +145,32 @@ struct intelxl_admin_shutdown_params { /** Driver is unloading */ #define INTELXL_ADMIN_SHUTDOWN_UNLOADING 0x01 +/** Admin queue Manage MAC Address Read command */ +#define INTELXL_ADMIN_MAC_READ 0x0107 + +/** Admin queue Manage MAC Address Read command parameters */ +struct intelxl_admin_mac_read_params { + /** Valid addresses */ + uint8_t valid; + /** Reserved */ + uint8_t reserved[15]; +} __attribute__ (( packed )); + +/** LAN MAC address is valid */ +#define INTELXL_ADMIN_MAC_READ_VALID_LAN 0x10 + +/** Admin queue Manage MAC Address Read data buffer */ +struct intelxl_admin_mac_read_buffer { + /** Physical function MAC address */ + uint8_t pf[ETH_ALEN]; + /** Reserved */ + uint8_t reserved[ETH_ALEN]; + /** Port MAC address */ + uint8_t port[ETH_ALEN]; + /** Physical function wake-on-LAN MAC address */ + uint8_t wol[ETH_ALEN]; +} __attribute__ (( packed )); + /** Admin queue Clear PXE Mode command */ #define INTELXL_ADMIN_CLEAR_PXE 0x0110 @@ -315,6 +341,8 @@ union intelxl_admin_params { struct intelxl_admin_driver_params driver; /** Shutdown command parameters */ struct intelxl_admin_shutdown_params shutdown; + /** Manage MAC Address Read command parameters */ + struct intelxl_admin_mac_read_params mac_read; /** Clear PXE Mode command parameters */ struct intelxl_admin_clear_pxe_params pxe; /** Get Switch Configuration command parameters */ @@ -336,6 +364,8 @@ union intelxl_admin_params { union intelxl_admin_buffer { /** Driver Version data buffer */ struct intelxl_admin_driver_buffer driver; + /** Manage MAC Address Read data buffer */ + struct intelxl_admin_mac_read_buffer mac_read; /** Get Switch Configuration data buffer */ struct intelxl_admin_switch_buffer sw; /** Get VSI Parameters data buffer */ @@ -831,12 +861,6 @@ intelxl_init_ring ( struct intelxl_ring *ring, unsigned int count, size_t len, #define INTELXL_PRTGL_SAH 0x1e2140 #define INTELXL_PRTGL_SAH_MFS(x) ( (x) << 16 ) /**< Max frame size */ -/** Physical Function MAC Address Low Register */ -#define INTELXL_PRTPM_SAL 0x1e4440 - -/** Physical Function MAC Address High Register */ -#define INTELXL_PRTPM_SAH 0x1e44c0 - /** Receive address */ union intelxl_receive_address { struct { -- cgit v1.2.3-55-g7522 From 6871a7de705b6f6a4046f0d19da9bcd689c3bc8e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 20 Mar 2022 15:00:18 +0000 Subject: [intelxl] Use admin queue to set port MAC address and maximum frame size Remove knowledge of the PRTGL_SA[HL] registers, and instead use the admin queue to set the MAC address and maximum frame size. Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 79 ++++++++++++++++++++++++++++++++++++++++------- src/drivers/net/intelxl.h | 53 +++++++++++++++++++++---------- 2 files changed, 105 insertions(+), 27 deletions(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index dcc8db562..c8c527d53 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -535,6 +535,40 @@ static int intelxl_admin_mac_read ( struct net_device *netdev ) { return 0; } +/** + * Set MAC address + * + * @v netdev Network device + * @ret rc Return status code + */ +static int intelxl_admin_mac_write ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct intelxl_admin_descriptor *cmd; + struct intelxl_admin_mac_write_params *write; + union { + uint8_t raw[ETH_ALEN]; + struct { + uint16_t high; + uint32_t low; + } __attribute__ (( packed )); + } mac; + int rc; + + /* Populate descriptor */ + cmd = intelxl_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_MAC_WRITE ); + write = &cmd->params.mac_write; + memcpy ( mac.raw, netdev->ll_addr, ETH_ALEN ); + write->high = bswap_16 ( mac.high ); + write->low = bswap_32 ( mac.low ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + return 0; +} + /** * Clear PXE mode * @@ -686,6 +720,31 @@ static int intelxl_admin_promisc ( struct intelxl_nic *intelxl ) { return 0; } +/** + * Set MAC configuration + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int intelxl_admin_mac_config ( struct intelxl_nic *intelxl ) { + struct intelxl_admin_descriptor *cmd; + struct intelxl_admin_mac_config_params *config; + int rc; + + /* Populate descriptor */ + cmd = intelxl_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_MAC_CONFIG ); + config = &cmd->params.mac_config; + config->mfs = cpu_to_le16 ( intelxl->mfs ); + config->flags = INTELXL_ADMIN_MAC_CONFIG_FL_CRC; + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + return 0; +} + /** * Restart autonegotiation * @@ -1348,24 +1407,20 @@ void intelxl_empty_rx ( struct intelxl_nic *intelxl ) { */ static int intelxl_open ( struct net_device *netdev ) { struct intelxl_nic *intelxl = netdev->priv; - union intelxl_receive_address mac; unsigned int queue; - uint32_t prtgl_sal; - uint32_t prtgl_sah; int rc; /* Calculate maximum frame size */ intelxl->mfs = ( ( ETH_HLEN + netdev->mtu + 4 /* CRC */ + INTELXL_ALIGN - 1 ) & ~( INTELXL_ALIGN - 1 ) ); - /* Program MAC address and maximum frame size */ - memset ( &mac, 0, sizeof ( mac ) ); - memcpy ( mac.raw, netdev->ll_addr, sizeof ( mac.raw ) ); - prtgl_sal = le32_to_cpu ( mac.reg.low ); - prtgl_sah = ( le32_to_cpu ( mac.reg.high ) | - INTELXL_PRTGL_SAH_MFS ( intelxl->mfs ) ); - writel ( prtgl_sal, intelxl->regs + INTELXL_PRTGL_SAL ); - writel ( prtgl_sah, intelxl->regs + INTELXL_PRTGL_SAH ); + /* Set MAC address */ + if ( ( rc = intelxl_admin_mac_write ( netdev ) ) != 0 ) + goto err_mac_write; + + /* Set maximum frame size */ + if ( ( rc = intelxl_admin_mac_config ( intelxl ) ) != 0 ) + goto err_mac_config; /* Associate transmit queue to PF */ writel ( ( INTELXL_QXX_CTL_PFVF_Q_PF | @@ -1408,6 +1463,8 @@ static int intelxl_open ( struct net_device *netdev ) { err_create_tx: intelxl_destroy_ring ( intelxl, &intelxl->rx ); err_create_rx: + err_mac_config: + err_mac_write: return rc; } diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index 610b30140..ad15ca99f 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -171,6 +171,23 @@ struct intelxl_admin_mac_read_buffer { uint8_t wol[ETH_ALEN]; } __attribute__ (( packed )); +/** Admin queue Manage MAC Address Write command */ +#define INTELXL_ADMIN_MAC_WRITE 0x0108 + +/** Admin queue Manage MAC Address Write command parameters */ +struct intelxl_admin_mac_write_params { + /** Reserved */ + uint8_t reserved_a[1]; + /** Write type */ + uint8_t type; + /** MAC address first 16 bits, byte-swapped */ + uint16_t high; + /** MAC address last 32 bits, byte-swapped */ + uint32_t low; + /** Reserved */ + uint8_t reserved_b[8]; +} __attribute__ (( packed )); + /** Admin queue Clear PXE Mode command */ #define INTELXL_ADMIN_CLEAR_PXE 0x0110 @@ -289,6 +306,22 @@ struct intelxl_admin_promisc_params { /** Promiscuous VLAN mode */ #define INTELXL_ADMIN_PROMISC_FL_VLAN 0x0010 +/** Admin queue Set MAC Configuration command */ +#define INTELXL_ADMIN_MAC_CONFIG 0x0603 + +/** Admin queue Set MAC Configuration command parameters */ +struct intelxl_admin_mac_config_params { + /** Maximum frame size */ + uint16_t mfs; + /** Flags */ + uint8_t flags; + /** Reserved */ + uint8_t reserved[13]; +} __attribute__ (( packed )); + +/** Append CRC on transmit */ +#define INTELXL_ADMIN_MAC_CONFIG_FL_CRC 0x04 + /** Admin queue Restart Autonegotiation command */ #define INTELXL_ADMIN_AUTONEG 0x0605 @@ -343,6 +376,8 @@ union intelxl_admin_params { struct intelxl_admin_shutdown_params shutdown; /** Manage MAC Address Read command parameters */ struct intelxl_admin_mac_read_params mac_read; + /** Manage MAC Address Write command parameters */ + struct intelxl_admin_mac_write_params mac_write; /** Clear PXE Mode command parameters */ struct intelxl_admin_clear_pxe_params pxe; /** Get Switch Configuration command parameters */ @@ -351,6 +386,8 @@ union intelxl_admin_params { struct intelxl_admin_vsi_params vsi; /** Set VSI Promiscuous Modes command parameters */ struct intelxl_admin_promisc_params promisc; + /** Set MAC Configuration command parameters */ + struct intelxl_admin_mac_config_params mac_config; /** Restart Autonegotiation command parameters */ struct intelxl_admin_autoneg_params autoneg; /** Get Link Status command parameters */ @@ -854,22 +891,6 @@ intelxl_init_ring ( struct intelxl_ring *ring, unsigned int count, size_t len, #define INTELXL_PFGEN_PORTNUM_PORT_NUM(x) \ ( ( (x) >> 0 ) & 0x3 ) /**< Port number */ -/** Port MAC Address Low Register */ -#define INTELXL_PRTGL_SAL 0x1e2120 - -/** Port MAC Address High Register */ -#define INTELXL_PRTGL_SAH 0x1e2140 -#define INTELXL_PRTGL_SAH_MFS(x) ( (x) << 16 ) /**< Max frame size */ - -/** Receive address */ -union intelxl_receive_address { - struct { - uint32_t low; - uint32_t high; - } __attribute__ (( packed )) reg; - uint8_t raw[ETH_ALEN]; -}; - /** MSI-X interrupt */ struct intelxl_msix { /** PCI capability */ -- cgit v1.2.3-55-g7522 From cad1cc6b449b63415ffdad8e12f13df4256106fb Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 8 Aug 2022 15:02:44 +0100 Subject: [intelxl] Add driver for Intel 100 Gigabit Ethernet NICs Add a driver for the E810 family of 100 Gigabit Ethernet NICs. The core datapath is identical to that of the 40 Gigabit XL710, and this part of the code is shared between both drivers. The admin queue mechanism is sufficiently similar to make it worth reusing substantial portions of the code, with separate implementations for several commands to handle the (unnecessarily) breaking changes in data structure layouts. The major differences are in the mechanisms for programming queue contexts (where the E810 abandons TX/RX symmetry) and for configuring the transmit scheduler and receive filters: these portions are sufficiently different to justify a separate driver. Signed-off-by: Michael Brown --- src/drivers/net/ice.c | 986 +++++++++++++++++++++++++++++++++++++++++++++ src/drivers/net/ice.h | 565 ++++++++++++++++++++++++++ src/drivers/net/intelxl.c | 14 +- src/drivers/net/intelxl.h | 10 + src/include/ipxe/errfile.h | 1 + 5 files changed, 1569 insertions(+), 7 deletions(-) create mode 100644 src/drivers/net/ice.c create mode 100644 src/drivers/net/ice.h (limited to 'src/drivers/net') diff --git a/src/drivers/net/ice.c b/src/drivers/net/ice.c new file mode 100644 index 000000000..b5d66f1bb --- /dev/null +++ b/src/drivers/net/ice.c @@ -0,0 +1,986 @@ +/* + * Copyright (C) 2022 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ice.h" + +/** @file + * + * Intel 100 Gigabit Ethernet network card driver + * + */ + +/** + * Magic MAC address + * + * Used as the source address and promiscuous unicast destination + * address in the "add switch rules" command. + */ +static uint8_t ice_magic_mac[ETH_HLEN] = { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +/****************************************************************************** + * + * Admin queue + * + ****************************************************************************** + */ + +/** + * Get firmware version + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int ice_admin_version ( struct intelxl_nic *intelxl ) { + struct ice_admin_descriptor *cmd; + struct ice_admin_version_params *version; + unsigned int api; + int rc; + + /* Populate descriptor */ + cmd = ice_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_VERSION ); + version = &cmd->params.version; + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + api = version->api.major; + DBGC ( intelxl, "ICE %p firmware v%d/%d.%d.%d API v%d/%d.%d.%d\n", + intelxl, version->firmware.branch, version->firmware.major, + version->firmware.minor, version->firmware.patch, + version->api.branch, version->api.major, version->api.minor, + version->api.patch ); + + /* Check for API compatibility */ + if ( api > INTELXL_ADMIN_API_MAJOR ) { + DBGC ( intelxl, "ICE %p unsupported API v%d\n", intelxl, api ); + return -ENOTSUP; + } + + return 0; +} + +/** + * Get MAC address + * + * @v netdev Network device + * @ret rc Return status code + */ +static int ice_admin_mac_read ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct ice_admin_descriptor *cmd; + struct ice_admin_mac_read_params *read; + struct ice_admin_mac_read_address *mac; + union ice_admin_buffer *buf; + unsigned int i; + int rc; + + /* Populate descriptor */ + cmd = ice_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_MAC_READ ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->mac_read ) ); + read = &cmd->params.mac_read; + buf = ice_admin_command_buffer ( intelxl ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + /* Check that MAC address is present in response */ + if ( ! ( read->valid & INTELXL_ADMIN_MAC_READ_VALID_LAN ) ) { + DBGC ( intelxl, "ICE %p has no MAC address\n", intelxl ); + return -ENOENT; + } + + /* Identify MAC address */ + for ( i = 0 ; i < read->count ; i++ ) { + + /* Check for a LAN MAC address */ + mac = &buf->mac_read.mac[i]; + if ( mac->type != ICE_ADMIN_MAC_READ_TYPE_LAN ) + continue; + + /* Check that address is valid */ + if ( ! is_valid_ether_addr ( mac->mac ) ) { + DBGC ( intelxl, "ICE %p has invalid MAC address " + "(%s)\n", intelxl, eth_ntoa ( mac->mac ) ); + return -EINVAL; + } + + /* Copy MAC address */ + DBGC ( intelxl, "ICE %p has MAC address %s\n", + intelxl, eth_ntoa ( mac->mac ) ); + memcpy ( netdev->hw_addr, mac->mac, ETH_ALEN ); + + return 0; + } + + /* Missing LAN MAC address */ + DBGC ( intelxl, "ICE %p has no LAN MAC address\n", + intelxl ); + return -ENOENT; +} + +/** + * Set MAC address + * + * @v netdev Network device + * @ret rc Return status code + */ +static int ice_admin_mac_write ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct ice_admin_descriptor *cmd; + struct ice_admin_mac_write_params *write; + int rc; + + /* Populate descriptor */ + cmd = ice_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_MAC_WRITE ); + write = &cmd->params.mac_write; + memcpy ( write->mac, netdev->ll_addr, ETH_ALEN ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Get switch configuration + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int ice_admin_switch ( struct intelxl_nic *intelxl ) { + struct ice_admin_descriptor *cmd; + struct ice_admin_switch_params *sw; + union ice_admin_buffer *buf; + uint16_t next = 0; + uint16_t seid; + uint16_t type; + int rc; + + /* Get each configuration in turn */ + do { + /* Populate descriptor */ + cmd = ice_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_SWITCH ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->sw ) ); + sw = &cmd->params.sw; + sw->next = next; + buf = ice_admin_command_buffer ( intelxl ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + seid = le16_to_cpu ( buf->sw.cfg[0].seid ); + + /* Dump raw configuration */ + DBGC2 ( intelxl, "ICE %p SEID %#04x:\n", intelxl, seid ); + DBGC2_HDA ( intelxl, 0, &buf->sw.cfg[0], + sizeof ( buf->sw.cfg[0] ) ); + + /* Parse response */ + type = ( seid & ICE_ADMIN_SWITCH_TYPE_MASK ); + if ( type == ICE_ADMIN_SWITCH_TYPE_VSI ) { + intelxl->vsi = ( seid & ~ICE_ADMIN_SWITCH_TYPE_MASK ); + DBGC ( intelxl, "ICE %p VSI %#04x uplink %#04x func " + "%#04x\n", intelxl, intelxl->vsi, + le16_to_cpu ( buf->sw.cfg[0].uplink ), + le16_to_cpu ( buf->sw.cfg[0].func ) ); + } + + } while ( ( next = sw->next ) ); + + /* Check that we found a VSI */ + if ( ! intelxl->vsi ) { + DBGC ( intelxl, "ICE %p has no VSI\n", intelxl ); + return -ENOENT; + } + + return 0; +} + +/** + * Add switch rules + * + * @v intelxl Intel device + * @v mac MAC address + * @ret rc Return status code + */ +static int ice_admin_rules ( struct intelxl_nic *intelxl, uint8_t *mac ) { + struct ice_admin_descriptor *cmd; + struct ice_admin_rules_params *rules; + union ice_admin_buffer *buf; + int rc; + + /* Populate descriptor */ + cmd = ice_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( ICE_ADMIN_ADD_RULES ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF | INTELXL_ADMIN_FL_RD ); + cmd->len = cpu_to_le16 ( sizeof ( buf->rules ) ); + rules = &cmd->params.rules; + rules->count = cpu_to_le16 ( 1 ); + buf = ice_admin_command_buffer ( intelxl ); + buf->rules.recipe = cpu_to_le16 ( ICE_ADMIN_RULES_RECIPE_PROMISC ); + buf->rules.port = cpu_to_le16 ( intelxl->port ); + buf->rules.action = + cpu_to_le32 ( ICE_ADMIN_RULES_ACTION_VALID | + ICE_ADMIN_RULES_ACTION_VSI ( intelxl->vsi ) ); + buf->rules.len = cpu_to_le16 ( sizeof ( buf->rules.hdr ) ); + memcpy ( buf->rules.hdr.eth.h_dest, mac, ETH_ALEN ); + memcpy ( buf->rules.hdr.eth.h_source, ice_magic_mac, ETH_ALEN ); + buf->rules.hdr.eth.h_protocol = htons ( ETH_P_8021Q ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Check if scheduler node is a parent (i.e. non-leaf) node + * + * @v branch Scheduler topology branch + * @v node Scheduler topology node + * @ret child Any child node, or NULL if not found + */ +static struct ice_admin_schedule_node * +ice_admin_schedule_is_parent ( struct ice_admin_schedule_branch *branch, + struct ice_admin_schedule_node *node ) { + unsigned int count = le16_to_cpu ( branch->count ); + struct ice_admin_schedule_node *child; + unsigned int i; + + /* Find a child element, if any */ + for ( i = 0 ; i < count ; i++ ) { + child = &branch->node[i]; + if ( child->parent == node->teid ) + return child; + } + + return NULL; +} + +/** + * Query default scheduling tree topology + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int ice_admin_schedule ( struct intelxl_nic *intelxl ) { + struct ice_admin_descriptor *cmd; + struct ice_admin_schedule_params *sched; + struct ice_admin_schedule_branch *branch; + struct ice_admin_schedule_node *node; + union ice_admin_buffer *buf; + int i; + int rc; + + /* Populate descriptor */ + cmd = ice_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( ICE_ADMIN_SCHEDULE ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->sched ) ); + sched = &cmd->params.sched; + buf = ice_admin_command_buffer ( intelxl ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + /* Sanity checks */ + if ( ! sched->branches ) { + DBGC ( intelxl, "ICE %p topology has no branches\n", intelxl ); + return -EINVAL; + } + branch = buf->sched.branch; + + /* Identify leaf node */ + for ( i = ( le16_to_cpu ( branch->count ) - 1 ) ; i >= 0 ; i-- ) { + node = &branch->node[i]; + if ( ! ice_admin_schedule_is_parent ( branch, node ) ) { + intelxl->teid = le32_to_cpu ( node->teid ); + DBGC2 ( intelxl, "ICE %p TEID %#08x type %d\n", + intelxl, intelxl->teid, node->config.type ); + break; + } + } + if ( ! intelxl->teid ) { + DBGC ( intelxl, "ICE %p found no leaf TEID\n", intelxl ); + return -EINVAL; + } + + return 0; +} + +/** + * Restart autonegotiation + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int ice_admin_autoneg ( struct intelxl_nic *intelxl ) { + struct ice_admin_descriptor *cmd; + struct ice_admin_autoneg_params *autoneg; + int rc; + + /* Populate descriptor */ + cmd = ice_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_AUTONEG ); + autoneg = &cmd->params.autoneg; + autoneg->flags = ( INTELXL_ADMIN_AUTONEG_FL_RESTART | + INTELXL_ADMIN_AUTONEG_FL_ENABLE ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Get link status + * + * @v netdev Network device + * @ret rc Return status code + */ +static int ice_admin_link ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct ice_admin_descriptor *cmd; + struct ice_admin_link_params *link; + union ice_admin_buffer *buf; + int rc; + + /* Populate descriptor */ + cmd = ice_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_LINK ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->link ) ); + link = &cmd->params.link; + link->notify = INTELXL_ADMIN_LINK_NOTIFY; + buf = ice_admin_command_buffer ( intelxl ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + DBGC ( intelxl, "ICE %p speed %#02x status %#02x\n", + intelxl, le16_to_cpu ( buf->link.speed ), buf->link.status ); + + /* Update network device */ + if ( buf->link.status & INTELXL_ADMIN_LINK_UP ) { + netdev_link_up ( netdev ); + } else { + netdev_link_down ( netdev ); + } + + return 0; +} + +/** + * Handle admin event + * + * @v netdev Network device + * @v xlevt Event descriptor + * @v xlbuf Data buffer + */ +static void ice_admin_event ( struct net_device *netdev, + struct intelxl_admin_descriptor *xlevt, + union intelxl_admin_buffer *xlbuf __unused ) { + struct intelxl_nic *intelxl = netdev->priv; + struct ice_admin_descriptor *evt = + container_of ( xlevt, struct ice_admin_descriptor, xl ); + + /* Ignore unrecognised events */ + if ( evt->opcode != cpu_to_le16 ( INTELXL_ADMIN_LINK ) ) { + DBGC ( intelxl, "INTELXL %p unrecognised event opcode " + "%#04x\n", intelxl, le16_to_cpu ( evt->opcode ) ); + return; + } + + /* Update link status */ + ice_admin_link ( netdev ); +} + +/** + * Add transmit queue + * + * @v intelxl Intel device + * @v ring Descriptor ring + * @ret rc Return status code + */ +static int ice_admin_add_txq ( struct intelxl_nic *intelxl, + struct intelxl_ring *ring ) { + struct ice_admin_descriptor *cmd; + struct ice_admin_add_txq_params *add_txq; + union ice_admin_buffer *buf; + struct ice_context_tx *ctx; + struct ice_schedule_tx *sched; + physaddr_t address; + int rc; + + /* Populate descriptor */ + cmd = ice_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( ICE_ADMIN_ADD_TXQ ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->add_txq ) ); + add_txq = &cmd->params.add_txq; + add_txq->count = 1; + buf = ice_admin_command_buffer ( intelxl ); + buf->add_txq.parent = cpu_to_le32 ( intelxl->teid ); + buf->add_txq.count = 1; + ctx = &buf->add_txq.ctx; + address = dma ( &ring->map, ring->desc.raw ); + ctx->base_port = + cpu_to_le64 ( ICE_TXQ_BASE_PORT ( address, intelxl->port ) ); + ctx->pf_type = cpu_to_le16 ( ICE_TXQ_PF_TYPE ( intelxl->pf ) ); + ctx->vsi = cpu_to_le16 ( intelxl->vsi ); + ctx->len = cpu_to_le16 ( ICE_TXQ_LEN ( INTELXL_TX_NUM_DESC ) ); + ctx->flags = cpu_to_le16 ( ICE_TXQ_FL_TSO | ICE_TXQ_FL_LEGACY ); + sched = &buf->add_txq.sched; + sched->sections = ( ICE_SCHEDULE_GENERIC | ICE_SCHEDULE_COMMIT | + ICE_SCHEDULE_EXCESS ); + sched->commit_weight = cpu_to_le16 ( ICE_SCHEDULE_WEIGHT ); + sched->excess_weight = cpu_to_le16 ( ICE_SCHEDULE_WEIGHT ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + DBGC ( intelxl, "ICE %p added TEID %#04x\n", + intelxl, le32_to_cpu ( buf->add_txq.teid ) ); + + return 0; +} + +/** + * Disable transmit queue + * + * @v intelxl Intel device + * @v ring Descriptor ring + * @ret rc Return status code + */ +static int ice_admin_disable_txq ( struct intelxl_nic *intelxl ) { + struct ice_admin_descriptor *cmd; + struct ice_admin_disable_txq_params *disable_txq; + union ice_admin_buffer *buf; + int rc; + + /* Populate descriptor */ + cmd = ice_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( ICE_ADMIN_DISABLE_TXQ ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->disable_txq ) ); + disable_txq = &cmd->params.disable_txq; + disable_txq->flags = ICE_TXQ_FL_FLUSH; + disable_txq->count = 1; + disable_txq->timeout = ICE_TXQ_TIMEOUT; + buf = ice_admin_command_buffer ( intelxl ); + buf->disable_txq.parent = cpu_to_le32 ( intelxl->teid ); + buf->disable_txq.count = 1; + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + return 0; +} + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Dump transmit queue context (for debugging) + * + * @v intelxl Intel device + */ +static void ice_dump_tx ( struct intelxl_nic *intelxl ) { + uint32_t ctx[ sizeof ( struct ice_context_tx ) / sizeof ( uint32_t ) ]; + uint32_t stat; + unsigned int i; + + /* Do nothing unless debug output is enabled */ + if ( ! DBG_EXTRA ) + return; + + /* Trigger reading of transmit context */ + writel ( ( ICE_GLCOMM_QTX_CNTX_CTL_CMD_READ | + ICE_GLCOMM_QTX_CNTX_CTL_EXEC ), + intelxl->regs + ICE_GLCOMM_QTX_CNTX_CTL ); + + /* Wait for operation to complete */ + for ( i = 0 ; i < INTELXL_CTX_MAX_WAIT_MS ; i++ ) { + + /* Check if operation is complete */ + stat = readl ( intelxl->regs + ICE_GLCOMM_QTX_CNTX_STAT ); + if ( ! ( stat & ICE_GLCOMM_QTX_CNTX_BUSY ) ) + break; + + /* Delay */ + mdelay ( 1 ); + } + + /* Read context registers */ + for ( i = 0 ; i < ( sizeof ( ctx ) / sizeof ( ctx[0] ) ) ; i++ ) { + ctx[i] = cpu_to_le32 ( readl ( intelxl->regs + + ICE_GLCOMM_QTX_CNTX_DATA ( i ))); + } + + /* Dump context */ + DBGC2 ( intelxl, "ICE %p TX context:\n", intelxl ); + DBGC2_HDA ( intelxl, 0, ctx, sizeof ( ctx ) ); +} + +/** + * Dump receive queue context (for debugging) + * + * @v intelxl Intel device + */ +static void ice_dump_rx ( struct intelxl_nic *intelxl ) { + uint32_t ctx[ sizeof ( struct intelxl_context_rx ) / + sizeof ( uint32_t ) ]; + unsigned int i; + + /* Do nothing unless debug output is enabled */ + if ( ! DBG_EXTRA ) + return; + + /* Read context registers */ + for ( i = 0 ; i < ( sizeof ( ctx ) / sizeof ( ctx[0] ) ) ; i++ ) { + ctx[i] = cpu_to_le32 ( readl ( intelxl->regs + + ICE_QRX_CONTEXT ( i ) ) ); + } + + /* Dump context */ + DBGC2 ( intelxl, "ICE %p RX context:\n", intelxl ); + DBGC2_HDA ( intelxl, 0, ctx, sizeof ( ctx ) ); +} + +/** + * Create transmit queue + * + * @v intelxl Intel device + * @v ring Descriptor ring + * @ret rc Return status code + */ +static int ice_create_tx ( struct intelxl_nic *intelxl, + struct intelxl_ring *ring ) { + int rc; + + /* Allocate descriptor ring */ + if ( ( rc = intelxl_alloc_ring ( intelxl, ring ) ) != 0 ) + goto err_alloc; + + /* Add transmit queue */ + if ( ( rc = ice_admin_add_txq ( intelxl, ring ) ) != 0 ) + goto err_add_txq; + + return 0; + + err_add_txq: + intelxl_free_ring ( intelxl, ring ); + err_alloc: + return rc; +} + +/** + * Destroy transmit queue + * + * @v intelxl Intel device + * @v ring Descriptor ring + * @ret rc Return status code + */ +static void ice_destroy_tx ( struct intelxl_nic *intelxl, + struct intelxl_ring *ring ) { + int rc; + + /* Disable transmit queue */ + if ( ( rc = ice_admin_disable_txq ( intelxl ) ) != 0 ) { + /* Leak memory; there's nothing else we can do */ + return; + } + + /* Free descriptor ring */ + intelxl_free_ring ( intelxl, ring ); +} + +/** + * Program receive queue context + * + * @v intelxl Intel device + * @v address Descriptor ring base address + * @ret rc Return status code + */ +static int ice_context_rx ( struct intelxl_nic *intelxl, + physaddr_t address ) { + union { + struct intelxl_context_rx rx; + uint32_t raw[ sizeof ( struct intelxl_context_rx ) / + sizeof ( uint32_t ) ]; + } ctx; + uint64_t base_count; + unsigned int i; + + /* Initialise context */ + memset ( &ctx, 0, sizeof ( ctx ) ); + base_count = INTELXL_CTX_RX_BASE_COUNT ( address, INTELXL_RX_NUM_DESC ); + ctx.rx.base_count = cpu_to_le64 ( base_count ); + ctx.rx.len = cpu_to_le16 ( INTELXL_CTX_RX_LEN ( intelxl->mfs ) ); + ctx.rx.flags = ( INTELXL_CTX_RX_FL_DSIZE | INTELXL_CTX_RX_FL_CRCSTRIP ); + ctx.rx.mfs = cpu_to_le16 ( INTELXL_CTX_RX_MFS ( intelxl->mfs ) ); + + /* Write context registers */ + for ( i = 0 ; i < ( sizeof ( ctx ) / sizeof ( ctx.raw[0] ) ) ; i++ ) { + writel ( le32_to_cpu ( ctx.raw[i] ), + ( intelxl->regs + ICE_QRX_CONTEXT ( i ) ) ); + } + + return 0; +} + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int ice_open ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + int rc; + + /* Calculate maximum frame size */ + intelxl->mfs = ( ( ETH_HLEN + netdev->mtu + 4 /* CRC */ + + INTELXL_ALIGN - 1 ) & ~( INTELXL_ALIGN - 1 ) ); + + /* Set MAC address */ + if ( ( rc = ice_admin_mac_write ( netdev ) ) != 0 ) + goto err_mac_write; + + /* Set maximum frame size */ + if ( ( rc = intelxl_admin_mac_config ( intelxl ) ) != 0 ) + goto err_mac_config; + + /* Create receive descriptor ring */ + if ( ( rc = intelxl_create_ring ( intelxl, &intelxl->rx ) ) != 0 ) + goto err_create_rx; + + /* Create transmit descriptor ring */ + if ( ( rc = ice_create_tx ( intelxl, &intelxl->tx ) ) != 0 ) + goto err_create_tx; + + /* Restart autonegotiation */ + ice_admin_autoneg ( intelxl ); + + /* Update link state */ + ice_admin_link ( netdev ); + + return 0; + + ice_destroy_tx ( intelxl, &intelxl->tx ); + err_create_tx: + intelxl_destroy_ring ( intelxl, &intelxl->rx ); + err_create_rx: + err_mac_config: + err_mac_write: + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void ice_close ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + + /* Dump contexts (for debugging) */ + ice_dump_tx ( intelxl ); + ice_dump_rx ( intelxl ); + + /* Destroy transmit descriptor ring */ + ice_destroy_tx ( intelxl, &intelxl->tx ); + + /* Destroy receive descriptor ring */ + intelxl_destroy_ring ( intelxl, &intelxl->rx ); + + /* Discard any unused receive buffers */ + intelxl_empty_rx ( intelxl ); +} + +/** Network device operations */ +static struct net_device_operations ice_operations = { + .open = ice_open, + .close = ice_close, + .transmit = intelxl_transmit, + .poll = intelxl_poll, +}; + +/****************************************************************************** + * + * PCI interface + * + ****************************************************************************** + */ + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int ice_probe ( struct pci_device *pci ) { + struct net_device *netdev; + struct intelxl_nic *intelxl; + uint32_t pffunc_rid; + uint32_t pfgen_portnum; + int rc; + + /* Allocate and initialise net device */ + netdev = alloc_etherdev ( sizeof ( *intelxl ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &ice_operations ); + netdev->max_pkt_len = INTELXL_MAX_PKT_LEN; + intelxl = netdev->priv; + pci_set_drvdata ( pci, netdev ); + netdev->dev = &pci->dev; + memset ( intelxl, 0, sizeof ( *intelxl ) ); + intelxl->intr = ICE_GLINT_DYN_CTL; + intelxl->handle = ice_admin_event; + intelxl_init_admin ( &intelxl->command, INTELXL_ADMIN_CMD, + &intelxl_admin_offsets ); + intelxl_init_admin ( &intelxl->event, INTELXL_ADMIN_EVT, + &intelxl_admin_offsets ); + intelxl_init_ring ( &intelxl->tx, INTELXL_TX_NUM_DESC, + sizeof ( intelxl->tx.desc.tx[0] ), NULL ); + intelxl_init_ring ( &intelxl->rx, INTELXL_RX_NUM_DESC, + sizeof ( intelxl->rx.desc.rx[0] ), + ice_context_rx ); + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + intelxl->regs = pci_ioremap ( pci, pci->membase, ICE_BAR_SIZE ); + if ( ! intelxl->regs ) { + rc = -ENODEV; + goto err_ioremap; + } + + /* Configure DMA */ + intelxl->dma = &pci->dma; + dma_set_mask_64bit ( intelxl->dma ); + netdev->dma = intelxl->dma; + + /* Locate PCI Express capability */ + intelxl->exp = pci_find_capability ( pci, PCI_CAP_ID_EXP ); + if ( ! intelxl->exp ) { + DBGC ( intelxl, "ICE %p missing PCIe capability\n", + intelxl ); + rc = -ENXIO; + goto err_exp; + } + + /* Reset the function via PCIe FLR */ + pci_reset ( pci, intelxl->exp ); + + /* Get function and port number */ + pffunc_rid = readl ( intelxl->regs + ICE_PFFUNC_RID ); + intelxl->pf = ICE_PFFUNC_RID_FUNC_NUM ( pffunc_rid ); + pfgen_portnum = readl ( intelxl->regs + ICE_PFGEN_PORTNUM ); + intelxl->port = ICE_PFGEN_PORTNUM_PORT_NUM ( pfgen_portnum ); + DBGC ( intelxl, "ICE %p PF %d using port %d\n", + intelxl, intelxl->pf, intelxl->port ); + + /* Enable MSI-X dummy interrupt */ + if ( ( rc = intelxl_msix_enable ( intelxl, pci, + INTELXL_MSIX_VECTOR ) ) != 0 ) + goto err_msix; + + /* Open admin queues */ + if ( ( rc = intelxl_open_admin ( intelxl ) ) != 0 ) + goto err_open_admin; + + /* Get firmware version */ + if ( ( rc = ice_admin_version ( intelxl ) ) != 0 ) + goto err_admin_version; + + /* Clear PXE mode */ + if ( ( rc = intelxl_admin_clear_pxe ( intelxl ) ) != 0 ) + goto err_admin_clear_pxe; + + /* Get switch configuration */ + if ( ( rc = ice_admin_switch ( intelxl ) ) != 0 ) + goto err_admin_switch; + + /* Add broadcast address */ + if ( ( rc = ice_admin_rules ( intelxl, eth_broadcast ) ) != 0 ) + goto err_admin_rules_broadcast; + + /* Add promiscuous unicast address */ + if ( ( rc = ice_admin_rules ( intelxl, ice_magic_mac ) ) != 0 ) + goto err_admin_rules_magic; + + /* Query scheduler topology */ + if ( ( rc = ice_admin_schedule ( intelxl ) ) != 0 ) + goto err_admin_schedule; + + /* Get MAC address */ + if ( ( rc = ice_admin_mac_read ( netdev ) ) != 0 ) + goto err_admin_mac_read; + + /* Configure queue register addresses */ + intelxl->tx.tail = ICE_QTX_COMM_DBELL; + intelxl->rx.reg = ICE_QRX_CTRL; + intelxl->rx.tail = ICE_QRX_TAIL; + + /* Configure interrupt causes */ + writel ( ( ICE_QINT_TQCTL_ITR_INDX_NONE | ICE_QINT_TQCTL_CAUSE_ENA ), + intelxl->regs + ICE_QINT_TQCTL ); + writel ( ( ICE_QINT_RQCTL_ITR_INDX_NONE | ICE_QINT_RQCTL_CAUSE_ENA ), + intelxl->regs + ICE_QINT_RQCTL ); + + /* Set a default value for the queue context flex extension, + * since this register erroneously retains its value across at + * least a PCIe FLR. + */ + writel ( ( ICE_QRX_FLXP_CNTXT_RXDID_IDX_LEGACY_32 | + ICE_QRX_FLXP_CNTXT_RXDID_PRIO_MAX ), + intelxl->regs + ICE_QRX_FLXP_CNTXT ); + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register_netdev; + + /* Set initial link state */ + ice_admin_link ( netdev ); + + return 0; + + unregister_netdev ( netdev ); + err_register_netdev: + err_admin_mac_read: + err_admin_schedule: + err_admin_rules_magic: + err_admin_rules_broadcast: + err_admin_switch: + err_admin_clear_pxe: + err_admin_version: + intelxl_close_admin ( intelxl ); + err_open_admin: + intelxl_msix_disable ( intelxl, pci, INTELXL_MSIX_VECTOR ); + err_msix: + pci_reset ( pci, intelxl->exp ); + err_exp: + iounmap ( intelxl->regs ); + err_ioremap: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void ice_remove ( struct pci_device *pci ) { + struct net_device *netdev = pci_get_drvdata ( pci ); + struct intelxl_nic *intelxl = netdev->priv; + + /* Unregister network device */ + unregister_netdev ( netdev ); + + /* Close admin queues */ + intelxl_close_admin ( intelxl ); + + /* Disable MSI-X dummy interrupt */ + intelxl_msix_disable ( intelxl, pci, INTELXL_MSIX_VECTOR ); + + /* Reset the NIC */ + pci_reset ( pci, intelxl->exp ); + + /* Free network device */ + iounmap ( intelxl->regs ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** PCI device IDs */ +static struct pci_device_id ice_nics[] = { + PCI_ROM ( 0x8086, 0x124c, "e823l-bp", "E823-L backplane", 0 ), + PCI_ROM ( 0x8086, 0x124d, "e823l-sfp", "E823-L SFP", 0 ), + PCI_ROM ( 0x8086, 0x124e, "e823l-10gt", "E823-L 10GBASE-T", 0 ), + PCI_ROM ( 0x8086, 0x124f, "e823l-1g", "E823-L 1GbE", 0 ), + PCI_ROM ( 0x8086, 0x151d, "e823l-qsfp", "E823-L QSFP", 0 ), + PCI_ROM ( 0x8086, 0x1591, "e810c-bp", "E810-C backplane", 0 ), + PCI_ROM ( 0x8086, 0x1592, "e810c-qsfp", "E810-C QSFP", 0 ), + PCI_ROM ( 0x8086, 0x1593, "e810c-sfp", "E810-C SFP", 0 ), + PCI_ROM ( 0x8086, 0x1599, "e810-xxv-bp", "E810-XXV backplane", 0 ), + PCI_ROM ( 0x8086, 0x159a, "e810-xxv-qsfp", "E810-XXV QSFP", 0 ), + PCI_ROM ( 0x8086, 0x159b, "e810-xxv-sfp", "E810-XXV SFP", 0 ), + PCI_ROM ( 0x8086, 0x188a, "e823c-bp", "E823-C backplane", 0 ), + PCI_ROM ( 0x8086, 0x188b, "e823c-qsfp", "E823-C QSFP", 0 ), + PCI_ROM ( 0x8086, 0x188c, "e823c-sfp", "E823-C SFP", 0 ), + PCI_ROM ( 0x8086, 0x188d, "e823c-10gt", "E823-C 10GBASE-T", 0 ), + PCI_ROM ( 0x8086, 0x188e, "e823c-1g", "E823-C 1GbE", 0 ), + PCI_ROM ( 0x8086, 0x1890, "e822c-bp", "E822-C backplane", 0 ), + PCI_ROM ( 0x8086, 0x1891, "e822c-qsfp", "E822-C QSFP", 0 ), + PCI_ROM ( 0x8086, 0x1892, "e822c-sfp", "E822-C SFP", 0 ), + PCI_ROM ( 0x8086, 0x1893, "e822c-10gt", "E822-C 10GBASE-T", 0 ), + PCI_ROM ( 0x8086, 0x1894, "e822c-1g", "E822-C 1GbE", 0 ), + PCI_ROM ( 0x8086, 0x1897, "e822l-bp", "E822-L backplane", 0 ), + PCI_ROM ( 0x8086, 0x1898, "e822l-sfp", "E822-L SFP", 0 ), + PCI_ROM ( 0x8086, 0x1899, "e822l-10gt", "E822-L 10GBASE-T", 0 ), + PCI_ROM ( 0x8086, 0x189a, "e822l-1g", "E822-L 1GbE", 0 ), +}; + +/** PCI driver */ +struct pci_driver ice_driver __pci_driver = { + .ids = ice_nics, + .id_count = ( sizeof ( ice_nics ) / sizeof ( ice_nics[0] ) ), + .probe = ice_probe, + .remove = ice_remove, +}; diff --git a/src/drivers/net/ice.h b/src/drivers/net/ice.h new file mode 100644 index 000000000..26291a7a1 --- /dev/null +++ b/src/drivers/net/ice.h @@ -0,0 +1,565 @@ +#ifndef _ICE_H +#define _ICE_H + +/** @file + * + * Intel 100 Gigabit Ethernet network card driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include "intelxl.h" + +/** BAR size */ +#define ICE_BAR_SIZE 0x800000 + +/****************************************************************************** + * + * Transmit and receive datapaths + * + ****************************************************************************** + */ + +/** Transmit queue context */ +struct ice_context_tx { + /** Base address */ + uint64_t base_port; + /** PF number and queue type */ + uint16_t pf_type; + /** Source VSI */ + uint16_t vsi; + /** Reserved */ + uint8_t reserved_c[5]; + /** Queue length */ + uint16_t len; + /** Flags */ + uint16_t flags; + /** Reserved */ + uint8_t reserved_d[3]; +} __attribute__ (( packed )); + +/** Transmit scheduler configuration */ +struct ice_schedule_tx { + /** Node type */ + uint8_t type; + /** Valid sections */ + uint8_t sections; + /** Generic information */ + uint8_t generic; + /** Flags */ + uint8_t flags; + /** Committed bandwidth profile ID */ + uint16_t commit_id; + /** Committeed bandwidth weight */ + uint16_t commit_weight; + /** Excess bandwidth profile ID */ + uint16_t excess_id; + /** Excess bandwidth weight */ + uint16_t excess_weight; + /** Shared rate limit profile ID */ + uint16_t shared; + /** Reserved */ + uint16_t reserved; +} __attribute__ (( packed )); + +/** Global Receive Queue Control Register */ +#define ICE_QRX_CTRL 0x120000 + +/** Receive Queue Context Registers */ +#define ICE_QRX_CONTEXT(x) ( 0x280000 + ( 0x2000 * (x) ) ) + +/** Receive Queue Tail Register */ +#define ICE_QRX_TAIL 0x290000 + +/** Transmit Comm Scheduler Queue Doorbell Register */ +#define ICE_QTX_COMM_DBELL 0x2c0000 + +/** Transmit Comm Scheduler Queue Context Data Registers */ +#define ICE_GLCOMM_QTX_CNTX_DATA(x) ( 0x2d2d40 + ( 0x4 * (x) ) ) + +/** Transmit Comm Scheduler Queue Context Control Register */ +#define ICE_GLCOMM_QTX_CNTX_CTL 0x2d2dc8 +#define ICE_GLCOMM_QTX_CNTX_CTL_CMD(x) ( (x) << 16 ) /**< Command */ +#define ICE_GLCOMM_QTX_CNTX_CTL_CMD_READ \ + ICE_GLCOMM_QTX_CNTX_CTL_CMD ( 0 ) /**< Read context */ +#define ICE_GLCOMM_QTX_CNTX_CTL_EXEC 0x00080000UL /**< Execute */ + +/** Transmit Comm Scheduler Queue Context Status Register */ +#define ICE_GLCOMM_QTX_CNTX_STAT 0x2d2dcc +#define ICE_GLCOMM_QTX_CNTX_BUSY 0x00000001UL /**< In progress */ + +/** Queue Context Flex Extension Register */ +#define ICE_QRX_FLXP_CNTXT 0x480000 +#define ICE_QRX_FLXP_CNTXT_RXDID_IDX(x) ( (x) << 0 ) /**< RX profile */ +#define ICE_QRX_FLXP_CNTXT_RXDID_IDX_LEGACY_32 \ + ICE_QRX_FLXP_CNTXT_RXDID_IDX ( 1 ) /**< 32-byte legacy */ +#define ICE_QRX_FLXP_CNTXT_RXDID_PRIO(x) ( (x) << 8 ) /**< Priority */ +#define ICE_QRX_FLXP_CNTXT_RXDID_PRIO_MAX \ + ICE_QRX_FLXP_CNTXT_RXDID_PRIO ( 7 ) /**< Maximum priority */ + +/****************************************************************************** + * + * Admin queue + * + ****************************************************************************** + */ + +/** Admin queue version number */ +struct ice_admin_version { + /** Branch identifier */ + uint8_t branch; + /** Major version number */ + uint8_t major; + /** Minor version number */ + uint8_t minor; + /** Patch level */ + uint8_t patch; +} __attribute__ (( packed )); + +/** Admin queue Get Version command parameters */ +struct ice_admin_version_params { + /** ROM version */ + uint32_t rom; + /** Firmware build ID */ + uint32_t build; + /** Firmware version */ + struct ice_admin_version firmware; + /** API version */ + struct ice_admin_version api; +} __attribute__ (( packed )); + +/** Admin queue Manage MAC Address Read command parameters */ +struct ice_admin_mac_read_params { + /** Valid addresses */ + uint8_t valid; + /** Reserved */ + uint8_t reserved_a[3]; + /** Number of addresses in response */ + uint8_t count; + /** Reserved */ + uint8_t reserved_b[11]; +} __attribute__ (( packed )); + +/** MAC Address description */ +struct ice_admin_mac_read_address { + /** Port number */ + uint8_t port; + /** Address type */ + uint8_t type; + /** MAC address */ + uint8_t mac[ETH_ALEN]; +} __attribute__ (( packed )); + +/** LAN MAC address type */ +#define ICE_ADMIN_MAC_READ_TYPE_LAN 0 + +/** Admin queue Manage MAC Address Read data buffer */ +struct ice_admin_mac_read_buffer { + /** MAC addresses */ + struct ice_admin_mac_read_address mac[4]; +} __attribute__ (( packed )); + +/** Admin queue Manage MAC Address Write command parameters */ +struct ice_admin_mac_write_params { + /** Reserved */ + uint8_t reserved_a[1]; + /** Write type */ + uint8_t type; + /** MAC address */ + uint8_t mac[ETH_ALEN]; + /** Reserved */ + uint8_t reserved_b[8]; +} __attribute__ (( packed )); + +/** Admin queue Get Switch Configuration command parameters */ +struct ice_admin_switch_params { + /** Reserved */ + uint8_t reserved_a[2]; + /** Starting switching element identifier */ + uint16_t next; + /** Reserved */ + uint8_t reserved[4]; + /** Data buffer address */ + uint64_t address; +} __attribute__ (( packed )); + +/** Switching element configuration */ +struct ice_admin_switch_config { + /** Switching element ID and flags */ + uint16_t seid; + /** Uplink switching element ID */ + uint16_t uplink; + /** PF/VF number */ + uint16_t func; +} __attribute__ (( packed )); + +/** Switching element ID type mask */ +#define ICE_ADMIN_SWITCH_TYPE_MASK 0xc000 + +/** Virtual Station Interface element type */ +#define ICE_ADMIN_SWITCH_TYPE_VSI 0x8000 + +/** Admin queue Get Switch Configuration data buffer */ +struct ice_admin_switch_buffer { + /** Switch configuration */ + struct ice_admin_switch_config cfg[1]; +}; + +/** Admin queue Add Switch Rules command */ +#define ICE_ADMIN_ADD_RULES 0x02a0 + +/** Admin queue Add Switch Rules command parameters */ +struct ice_admin_rules_params { + /** Number of rules */ + uint16_t count; + /** Reserved */ + uint8_t reserved[6]; +} __attribute__ (( packed )); + +/** Admin queue Add Switch Rules data buffer */ +struct ice_admin_rules_buffer { + /** Type */ + uint16_t type; + /** Return status */ + uint16_t status; + /** Receipt ID */ + uint16_t recipe; + /** Source port */ + uint16_t port; + /** Action */ + uint32_t action; + /** Lookup table index */ + uint16_t index; + /** Header length */ + uint16_t len; + /** Header data */ + union { + /** Ethernet header */ + struct ethhdr eth; + /** Raw data */ + uint8_t raw[16]; + } __attribute__ (( packed )) hdr; +} __attribute__ (( packed )); + +/** Switch rule promiscuous recipe ID */ +#define ICE_ADMIN_RULES_RECIPE_PROMISC 0x0003 + +/** Switch rule action valid */ +#define ICE_ADMIN_RULES_ACTION_VALID 0x00020000UL + +/** Switch rule VSI number */ +#define ICE_ADMIN_RULES_ACTION_VSI(x) ( (x) << 4 ) + +/** Admin queue Query Default Scheduling Tree Topology command */ +#define ICE_ADMIN_SCHEDULE 0x0400 + +/** Admin queue Query Default Scheduling Tree Topology command parameters */ +struct ice_admin_schedule_params { + /** Reserved */ + uint8_t reserved_a; + /** Total branches */ + uint8_t branches; + /** Reserved */ + uint8_t reserved_b[6]; +} __attribute__ (( packed )); + +/** Transmit scheduler configuration generic section is valid */ +#define ICE_SCHEDULE_GENERIC 0x01 + +/** Transmit scheduler configuration committed bandwidth section is valid */ +#define ICE_SCHEDULE_COMMIT 0x02 + +/** Transmit scheduler configuration excess bandwidth section is valid */ +#define ICE_SCHEDULE_EXCESS 0x04 + +/** Transmit scheduler configuration default weight */ +#define ICE_SCHEDULE_WEIGHT 0x0004 + +/** Admin queue Query Default Scheduling Tree Topology node */ +struct ice_admin_schedule_node { + /** Parent TEID */ + uint32_t parent; + /** Node TEID */ + uint32_t teid; + /** Scheduler configuration */ + struct ice_schedule_tx config; +} __attribute__ (( packed )); + +/** Admin queue Query Default Scheduling Tree Topology branch */ +struct ice_admin_schedule_branch { + /** Reserved */ + uint8_t reserved_a[4]; + /** Number of nodes */ + uint16_t count; + /** Reserved */ + uint8_t reserved_b[2]; + /** Nodes */ + struct ice_admin_schedule_node node[0]; +} __attribute__ (( packed )); + +/** Admin queue Query Default Scheduling Tree Topology data buffer */ +union ice_admin_schedule_buffer { + /** Branches */ + struct ice_admin_schedule_branch branch[0]; + /** Padding */ + uint8_t pad[INTELXL_ADMIN_BUFFER_SIZE]; +} __attribute__ (( packed )); + +/** Admin queue Restart Autonegotiation command parameters */ +struct ice_admin_autoneg_params { + /** Reserved */ + uint8_t reserved_a[2]; + /** Flags */ + uint8_t flags; + /** Reserved */ + uint8_t reserved_b[13]; +} __attribute__ (( packed )); + +/** Admin queue Get Link Status command parameters */ +struct ice_admin_link_params { + /** Logical port number */ + uint8_t port; + /** Reserved */ + uint8_t reserved_a; + /** Link status notification */ + uint8_t notify; + /** Reserved */ + uint8_t reserved_b[13]; +} __attribute__ (( packed )); + +/** Admin queue Get Link Status data buffer */ +struct ice_admin_link_buffer { + /** Topology conflicts */ + uint8_t conflict; + /** Configuration errors */ + uint8_t error; + /** Link status */ + uint8_t status; + /** Reserved */ + uint8_t reserved_a[7]; + /** Link speed */ + uint16_t speed; + /** Reserved */ + uint8_t reserved_b[20]; +} __attribute__ (( packed )); + +/** Admin queue Add Transmit Queues command */ +#define ICE_ADMIN_ADD_TXQ 0x0c30 + +/** Admin queue Add Transmit Queues command parameters */ +struct ice_admin_add_txq_params { + /** Number of queue groups */ + uint8_t count; + /** Reserved */ + uint8_t reserved[7]; +} __attribute__ (( packed )); + +/** Admin queue Add Transmit Queues data buffer */ +struct ice_admin_add_txq_buffer { + /** Parent TEID */ + uint32_t parent; + /** Number of queues */ + uint8_t count; + /** Reserved */ + uint8_t reserved_a[3]; + /** Transmit queue ID */ + uint16_t id; + /** Reserved */ + uint8_t reserved_b[2]; + /** Queue TEID */ + uint32_t teid; + /** Transmit queue context */ + struct ice_context_tx ctx; + /** Scheduler configuration */ + struct ice_schedule_tx sched; +} __attribute__ (( packed )); + +/** Transmit queue base address and port number */ +#define ICE_TXQ_BASE_PORT( addr, port ) \ + ( ( (addr) >> 7 ) | ( ( ( uint64_t ) (port) ) << 57 ) ) + +/** Transmit queue PF number */ +#define ICE_TXQ_PF_TYPE( pf ) ( ( (pf) << 1 ) | ( 0x2 << 14 ) ) + +/** Transmit queue length */ +#define ICE_TXQ_LEN( count ) ( (count) >> 1 ) + +/** Transmit queue uses TSO */ +#define ICE_TXQ_FL_TSO 0x0001 + +/** Transmit queue uses legacy mode*/ +#define ICE_TXQ_FL_LEGACY 0x1000 + +/** Admin queue Disable Transmit Queues command */ +#define ICE_ADMIN_DISABLE_TXQ 0x0c31 + +/** Admin queue Disable Transmit Queues command parameters */ +struct ice_admin_disable_txq_params { + /** Flags */ + uint8_t flags; + /** Number of queue groups */ + uint8_t count; + /** Reserved */ + uint8_t reserved_a; + /** Timeout */ + uint8_t timeout; + /** Reserved */ + uint8_t reserved_b[4]; +} __attribute__ (( packed )); + +/** Disable queue and flush pipe */ +#define ICE_TXQ_FL_FLUSH 0x08 + +/** Disable queue timeout */ +#define ICE_TXQ_TIMEOUT 0xc8 + +/** Admin queue Disable Transmit Queues data buffer */ +struct ice_admin_disable_txq_buffer { + /** Parent TEID */ + uint32_t parent; + /** Number of queues */ + uint8_t count; + /** Reserved */ + uint8_t reserved; + /** Transmit queue ID */ + uint16_t id; +} __attribute__ (( packed )); + +/** Admin queue command parameters */ +union ice_admin_params { + /** Additional data buffer command parameters */ + struct intelxl_admin_buffer_params buffer; + /** Get Version command parameters */ + struct ice_admin_version_params version; + /** Manage MAC Address Read command parameters */ + struct ice_admin_mac_read_params mac_read; + /** Manage MAC Address Write command parameters */ + struct ice_admin_mac_write_params mac_write; + /** Get Switch Configuration command parameters */ + struct ice_admin_switch_params sw; + /** Add Switch Rules command parameters */ + struct ice_admin_rules_params rules; + /** Query Default Scheduling Tree Topology command parameters */ + struct ice_admin_schedule_params sched; + /** Restart Autonegotiation command parameters */ + struct ice_admin_autoneg_params autoneg; + /** Get Link Status command parameters */ + struct ice_admin_link_params link; + /** Add Transmit Queue command parameters */ + struct ice_admin_add_txq_params add_txq; + /** Disable Transmit Queue command parameters */ + struct ice_admin_disable_txq_params disable_txq; +} __attribute__ (( packed )); + + +/** Admin queue data buffer */ +union ice_admin_buffer { + /** Original 40 Gigabit Ethernet data buffer */ + union intelxl_admin_buffer xl; + /** Manage MAC Address Read data buffer */ + struct ice_admin_mac_read_buffer mac_read; + /** Get Switch Configuration data buffer */ + struct ice_admin_switch_buffer sw; + /** Add Switch Rules data buffer */ + struct ice_admin_rules_buffer rules; + /** Query Default Scheduling Tree Topology data buffer */ + union ice_admin_schedule_buffer sched; + /** Get Link Status data buffer */ + struct ice_admin_link_buffer link; + /** Add Transmit Queue data buffer */ + struct ice_admin_add_txq_buffer add_txq; + /** Disable Transmit Queue data buffer */ + struct ice_admin_disable_txq_buffer disable_txq; +} __attribute__ (( packed )); + +/** Admin queue descriptor */ +struct ice_admin_descriptor { + /** Transparent union */ + union { + /** Original 40 Gigabit Ethernet descriptor */ + struct intelxl_admin_descriptor xl; + /** Transparent struct */ + struct { + /** Flags */ + uint16_t flags; + /** Opcode */ + uint16_t opcode; + /** Data length */ + uint16_t len; + /** Return value */ + uint16_t ret; + /** Opaque cookie */ + uint32_t cookie; + /** Reserved */ + uint8_t reserved[4]; + /** Parameters */ + union ice_admin_params params; + } __attribute__ (( packed )); + } __attribute__ (( packed )); +} __attribute__ (( packed )); + +/** + * Get next admin command queue descriptor + * + * @v intelxl Intel device + * @ret cmd Command descriptor + */ +struct ice_admin_descriptor * +ice_admin_command_descriptor ( struct intelxl_nic *intelxl ) { + struct intelxl_admin_descriptor *xlcmd = + intelxl_admin_command_descriptor ( intelxl ); + + return container_of ( xlcmd, struct ice_admin_descriptor, xl ); +} + +/** + * Get next admin command queue data buffer + * + * @v intelxl Intel device + * @ret buf Data buffer + */ +static inline __attribute__ (( always_inline )) union ice_admin_buffer * +ice_admin_command_buffer ( struct intelxl_nic *intelxl ) { + union intelxl_admin_buffer *xlbuf = + intelxl_admin_command_buffer ( intelxl ); + + return container_of ( xlbuf, union ice_admin_buffer, xl ); +} + +/****************************************************************************** + * + * Top level + * + ****************************************************************************** + */ + +/** Function Requester ID Information Register */ +#define ICE_PFFUNC_RID 0x09e880 +#define ICE_PFFUNC_RID_FUNC_NUM(x) \ + ( ( (x) >> 0 ) & 0x7 ) /**< Function number */ + +/** PF LAN Port Number Register */ +#define ICE_PFGEN_PORTNUM 0x1d2400 +#define ICE_PFGEN_PORTNUM_PORT_NUM(x) \ + ( ( (x) >> 0 ) & 0x7 ) /**< Port number */ + +/** Transmit Queue Interrupt Cause Control Register */ +#define ICE_QINT_TQCTL 0x140000 +#define ICE_QINT_TQCTL_ITR_INDX(x) ( (x) << 11 ) /**< Throttling */ +#define ICE_QINT_TQCTL_ITR_INDX_NONE \ + ICE_QINT_TQCTL_ITR_INDX ( 0x3 ) /**< No throttling */ +#define ICE_QINT_TQCTL_CAUSE_ENA 0x40000000UL /**< Enable */ + +/** Receive Queue Interrupt Cause Control Register */ +#define ICE_QINT_RQCTL 0x150000 +#define ICE_QINT_RQCTL_ITR_INDX(x) ( (x) << 11 ) /**< Throttling */ +#define ICE_QINT_RQCTL_ITR_INDX_NONE \ + ICE_QINT_RQCTL_ITR_INDX ( 0x3 ) /**< No throttling */ +#define ICE_QINT_RQCTL_CAUSE_ENA 0x40000000UL /**< Enable */ + +/** Global Interrupt Dynamic Control Register */ +#define ICE_GLINT_DYN_CTL 0x160000 + +#endif /* _ICE_H */ diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index c8c527d53..82b07833c 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -123,7 +123,7 @@ void intelxl_msix_disable ( struct intelxl_nic *intelxl, */ /** Admin queue register offsets */ -static const struct intelxl_admin_offsets intelxl_admin_offsets = { +const struct intelxl_admin_offsets intelxl_admin_offsets = { .bal = INTELXL_ADMIN_BAL, .bah = INTELXL_ADMIN_BAH, .len = INTELXL_ADMIN_LEN, @@ -575,7 +575,7 @@ static int intelxl_admin_mac_write ( struct net_device *netdev ) { * @v intelxl Intel device * @ret rc Return status code */ -static int intelxl_admin_clear_pxe ( struct intelxl_nic *intelxl ) { +int intelxl_admin_clear_pxe ( struct intelxl_nic *intelxl ) { struct intelxl_admin_descriptor *cmd; struct intelxl_admin_clear_pxe_params *pxe; int rc; @@ -726,7 +726,7 @@ static int intelxl_admin_promisc ( struct intelxl_nic *intelxl ) { * @v intelxl Intel device * @ret rc Return status code */ -static int intelxl_admin_mac_config ( struct intelxl_nic *intelxl ) { +int intelxl_admin_mac_config ( struct intelxl_nic *intelxl ) { struct intelxl_admin_descriptor *cmd; struct intelxl_admin_mac_config_params *config; int rc; @@ -1280,8 +1280,8 @@ static int intelxl_disable_ring ( struct intelxl_nic *intelxl, * @v ring Descriptor ring * @ret rc Return status code */ -static int intelxl_create_ring ( struct intelxl_nic *intelxl, - struct intelxl_ring *ring ) { +int intelxl_create_ring ( struct intelxl_nic *intelxl, + struct intelxl_ring *ring ) { physaddr_t address; int rc; @@ -1314,8 +1314,8 @@ static int intelxl_create_ring ( struct intelxl_nic *intelxl, * @v intelxl Intel device * @v ring Descriptor ring */ -static void intelxl_destroy_ring ( struct intelxl_nic *intelxl, - struct intelxl_ring *ring ) { +void intelxl_destroy_ring ( struct intelxl_nic *intelxl, + struct intelxl_ring *ring ) { int rc; /* Disable ring */ diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index ad15ca99f..0db16b581 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -925,6 +925,8 @@ struct intelxl_nic { unsigned int vsi; /** Queue set handle */ unsigned int qset; + /** Transmit element ID */ + uint32_t teid; /** Interrupt control register */ unsigned int intr; /** PCI Express capability offset */ @@ -959,6 +961,8 @@ struct intelxl_nic { union intelxl_admin_buffer *buf ); }; +extern const struct intelxl_admin_offsets intelxl_admin_offsets; + extern int intelxl_msix_enable ( struct intelxl_nic *intelxl, struct pci_device *pci, unsigned int vector ); @@ -970,6 +974,8 @@ intelxl_admin_command_descriptor ( struct intelxl_nic *intelxl ); extern union intelxl_admin_buffer * intelxl_admin_command_buffer ( struct intelxl_nic *intelxl ); extern int intelxl_admin_command ( struct intelxl_nic *intelxl ); +extern int intelxl_admin_clear_pxe ( struct intelxl_nic *intelxl ); +extern int intelxl_admin_mac_config ( struct intelxl_nic *intelxl ); extern void intelxl_poll_admin ( struct net_device *netdev ); extern int intelxl_open_admin ( struct intelxl_nic *intelxl ); extern void intelxl_reopen_admin ( struct intelxl_nic *intelxl ); @@ -978,6 +984,10 @@ extern int intelxl_alloc_ring ( struct intelxl_nic *intelxl, struct intelxl_ring *ring ); extern void intelxl_free_ring ( struct intelxl_nic *intelxl, struct intelxl_ring *ring ); +extern int intelxl_create_ring ( struct intelxl_nic *intelxl, + struct intelxl_ring *ring ); +extern void intelxl_destroy_ring ( struct intelxl_nic *intelxl, + struct intelxl_ring *ring ); extern void intelxl_empty_rx ( struct intelxl_nic *intelxl ); extern int intelxl_transmit ( struct net_device *netdev, struct io_buffer *iobuf ); diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 81f555725..359e4d2ce 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -215,6 +215,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_iphone ( ERRFILE_DRIVER | 0x00cf0000 ) #define ERRFILE_slirp ( ERRFILE_DRIVER | 0x00d00000 ) #define ERRFILE_rdc ( ERRFILE_DRIVER | 0x00d10000 ) +#define ERRFILE_ice ( ERRFILE_DRIVER | 0x00d20000 ) #define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) -- cgit v1.2.3-55-g7522 From b52ea208411e65a6583c70c9efdc0cd8eda2d7d7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 16 Aug 2022 13:21:49 +0100 Subject: [intelxl] Show virtual function packet statistics for debugging Signed-off-by: Michael Brown --- src/drivers/net/intelxlvf.c | 59 +++++++++++++++++++++++++++++++++++++++++++++ src/drivers/net/intelxlvf.h | 29 ++++++++++++++++++++++ 2 files changed, 88 insertions(+) (limited to 'src/drivers/net') diff --git a/src/drivers/net/intelxlvf.c b/src/drivers/net/intelxlvf.c index 3567be456..c58520a6f 100644 --- a/src/drivers/net/intelxlvf.c +++ b/src/drivers/net/intelxlvf.c @@ -391,6 +391,57 @@ static int intelxlvf_admin_get_resources ( struct net_device *netdev ) { return 0; } +/** + * Get statistics (for debugging) + * + * @v netdev Network device + * @ret rc Return status code + */ +static int intelxlvf_admin_stats ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct intelxlvf_admin_descriptor *cmd; + union intelxlvf_admin_buffer *buf; + struct intelxlvf_admin_stats *tx; + struct intelxlvf_admin_stats *rx; + int rc; + + /* Populate descriptor */ + cmd = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_GET_STATS ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->queues ) ); + buf = intelxlvf_admin_command_buffer ( intelxl ); + buf->queues.vsi = cpu_to_le16 ( intelxl->vsi ); + tx = &buf->stats.tx; + rx = &buf->stats.rx; + + /* Issue command */ + if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 ) + return rc; + DBGC ( intelxl, "INTELXL %p TX bytes %#llx discards %#llx errors " + "%#llx\n", intelxl, + ( ( unsigned long long ) le64_to_cpu ( tx->bytes ) ), + ( ( unsigned long long ) le64_to_cpu ( tx->discards ) ), + ( ( unsigned long long ) le64_to_cpu ( tx->errors ) ) ); + DBGC ( intelxl, "INTELXL %p TX unicasts %#llx multicasts %#llx " + "broadcasts %#llx\n", intelxl, + ( ( unsigned long long ) le64_to_cpu ( tx->unicasts ) ), + ( ( unsigned long long ) le64_to_cpu ( tx->multicasts ) ), + ( ( unsigned long long ) le64_to_cpu ( tx->broadcasts ) ) ); + DBGC ( intelxl, "INTELXL %p RX bytes %#llx discards %#llx errors " + "%#llx\n", intelxl, + ( ( unsigned long long ) le64_to_cpu ( rx->bytes ) ), + ( ( unsigned long long ) le64_to_cpu ( rx->discards ) ), + ( ( unsigned long long ) le64_to_cpu ( rx->errors ) ) ); + DBGC ( intelxl, "INTELXL %p RX unicasts %#llx multicasts %#llx " + "broadcasts %#llx\n", intelxl, + ( ( unsigned long long ) le64_to_cpu ( rx->unicasts ) ), + ( ( unsigned long long ) le64_to_cpu ( rx->multicasts ) ), + ( ( unsigned long long ) le64_to_cpu ( rx->broadcasts ) ) ); + + return 0; +} + /****************************************************************************** * * Network device interface @@ -565,6 +616,10 @@ static int intelxlvf_open ( struct net_device *netdev ) { if ( ( rc = intelxlvf_admin_promisc ( netdev ) ) != 0 ) goto err_promisc; + /* Reset statistics counters (if debugging) */ + if ( DBG_LOG ) + intelxlvf_admin_stats ( netdev ); + return 0; err_promisc: @@ -588,6 +643,10 @@ static void intelxlvf_close ( struct net_device *netdev ) { struct intelxl_nic *intelxl = netdev->priv; int rc; + /* Show statistics (if debugging) */ + if ( DBG_LOG ) + intelxlvf_admin_stats ( netdev ); + /* Disable queues */ if ( ( rc = intelxlvf_admin_queues ( netdev, 0 ) ) != 0 ) { /* Leak memory; there's nothing else we can do */ diff --git a/src/drivers/net/intelxlvf.h b/src/drivers/net/intelxlvf.h index 5c2227577..c537f6a15 100644 --- a/src/drivers/net/intelxlvf.h +++ b/src/drivers/net/intelxlvf.h @@ -253,6 +253,33 @@ struct intelxlvf_admin_promisc_buffer { uint16_t flags; } __attribute__ (( packed )); +/** Admin Queue VF Get Statistics opcode */ +#define INTELXLVF_ADMIN_GET_STATS 0x0000000f + +/** VF statistics */ +struct intelxlvf_admin_stats { + /** Bytes */ + uint64_t bytes; + /** Unicast packets */ + uint64_t unicasts; + /** Multicast packets */ + uint64_t multicasts; + /** Broadcast packets */ + uint64_t broadcasts; + /** Discarded packets */ + uint64_t discards; + /** Errors */ + uint64_t errors; +} __attribute__ (( packed )); + +/** Admin Queue VF Get Statistics data buffer */ +struct intelxlvf_admin_stats_buffer { + /** Receive statistics */ + struct intelxlvf_admin_stats rx; + /** Transmit statistics */ + struct intelxlvf_admin_stats tx; +} __attribute__ (( packed )); + /** Admin queue data buffer */ union intelxlvf_admin_buffer { /** Original 40 Gigabit Ethernet data buffer */ @@ -271,6 +298,8 @@ union intelxlvf_admin_buffer { struct intelxlvf_admin_promisc_buffer promisc; /** VF IRQ Map data buffer */ struct intelxlvf_admin_irq_map_buffer irq; + /** VF Get Statistics data buffer */ + struct intelxlvf_admin_stats_buffer stats; } __attribute__ (( packed )); /** Admin queue descriptor */ -- cgit v1.2.3-55-g7522 From 491c075f7f3ce3e7d58e969d8fcd0af1e102e515 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 16 Aug 2022 15:48:24 +0100 Subject: [intelxl] Negotiate virtual function API version 1.1 Negotiate API version 1.1 in order to allow access to virtual function opcodes that are disallowed by default on the E810. Signed-off-by: Michael Brown --- src/drivers/net/intelxl.h | 2 ++ src/drivers/net/intelxlvf.c | 7 ++++++- src/drivers/net/intelxlvf.h | 25 +++++++++++++++++++++++-- 3 files changed, 31 insertions(+), 3 deletions(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index 0db16b581..6c7865aa2 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -927,6 +927,8 @@ struct intelxl_nic { unsigned int qset; /** Transmit element ID */ uint32_t teid; + /** Device capabilities */ + uint32_t caps; /** Interrupt control register */ unsigned int intr; /** PCI Express capability offset */ diff --git a/src/drivers/net/intelxlvf.c b/src/drivers/net/intelxlvf.c index c58520a6f..79245b46f 100644 --- a/src/drivers/net/intelxlvf.c +++ b/src/drivers/net/intelxlvf.c @@ -377,16 +377,21 @@ static int intelxlvf_admin_get_resources ( struct net_device *netdev ) { /* Populate descriptor */ cmd = intelxlvf_admin_command_descriptor ( intelxl ); cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_GET_RESOURCES ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->caps ) ); buf = intelxlvf_admin_command_buffer ( intelxl ); + buf->caps.caps = cpu_to_le32 ( INTELXLVF_ADMIN_CAP_L2 ); /* Issue command */ if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 ) return rc; /* Parse response */ + intelxl->caps = le32_to_cpu ( buf->res.caps ); intelxl->vsi = le16_to_cpu ( buf->res.vsi ); memcpy ( netdev->hw_addr, buf->res.mac, ETH_ALEN ); - DBGC ( intelxl, "INTELXL %p VSI %#04x\n", intelxl, intelxl->vsi ); + DBGC ( intelxl, "INTELXL %p capabilities %#08x VSI %#04x\n", + intelxl, intelxl->caps, intelxl->vsi ); return 0; } diff --git a/src/drivers/net/intelxlvf.h b/src/drivers/net/intelxlvf.h index c537f6a15..783c85643 100644 --- a/src/drivers/net/intelxlvf.h +++ b/src/drivers/net/intelxlvf.h @@ -91,7 +91,7 @@ struct intelxlvf_admin_version_buffer { #define INTELXLVF_ADMIN_API_MAJOR 1 /** Admin queue VF API minor version */ -#define INTELXLVF_ADMIN_API_MINOR 0 +#define INTELXLVF_ADMIN_API_MINOR 1 /** Admin Queue VF Reset opcode */ #define INTELXLVF_ADMIN_RESET 0x00000002 @@ -99,10 +99,26 @@ struct intelxlvf_admin_version_buffer { /** Admin Queue VF Get Resources opcode */ #define INTELXLVF_ADMIN_GET_RESOURCES 0x00000003 +/** Admin Queue VF Capabilities data buffer */ +struct intelxlvf_admin_capabilities_buffer { + /** Capabilities */ + uint32_t caps; +} __attribute__ (( packed )); + /** Admin Queue VF Get Resources data buffer */ struct intelxlvf_admin_get_resources_buffer { + /** Number of VSIs */ + uint16_t vsis; + /** Number of queue pairs */ + uint16_t qps; + /** Number of MSI-X vectors */ + uint16_t vectors; + /** Maximum MTU */ + uint16_t mtu; + /** Capabilities */ + uint32_t caps; /** Reserved */ - uint8_t reserved_a[20]; + uint8_t reserved_a[8]; /** VSI switching element ID */ uint16_t vsi; /** Reserved */ @@ -111,6 +127,9 @@ struct intelxlvf_admin_get_resources_buffer { uint8_t mac[ETH_ALEN]; } __attribute__ (( packed )); +/** Layer 2 capabilities (add/remove MAC, configure promiscuous mode) */ +#define INTELXLVF_ADMIN_CAP_L2 0x00000001 + /** Admin Queue VF Status Change Event opcode */ #define INTELXLVF_ADMIN_STATUS 0x00000011 @@ -286,6 +305,8 @@ union intelxlvf_admin_buffer { union intelxl_admin_buffer xl; /** VF Version data buffer */ struct intelxlvf_admin_version_buffer ver; + /** VF Capabilities data buffer */ + struct intelxlvf_admin_capabilities_buffer caps; /** VF Get Resources data buffer */ struct intelxlvf_admin_get_resources_buffer res; /** VF Status Change Event data buffer */ -- cgit v1.2.3-55-g7522 From 04879352c4f5fbb4f1aeaf9b644c2b8408847894 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 16 Aug 2022 17:59:37 +0100 Subject: [intelxl] Allow for admin commands that trigger a VF reset The RESET_VF admin queue command does not complete via the usual mechanism, but instead requires us to poll registers to wait for the reset to take effect and then reopen the admin queue. Allow for the existence of other admin queue commands that also trigger a VF reset, by separating out the logic that waits for the reset to complete. Signed-off-by: Michael Brown --- src/drivers/net/intelxlvf.c | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/intelxlvf.c b/src/drivers/net/intelxlvf.c index 79245b46f..e30d8c6db 100644 --- a/src/drivers/net/intelxlvf.c +++ b/src/drivers/net/intelxlvf.c @@ -103,24 +103,14 @@ static int intelxlvf_reset_wait_active ( struct intelxl_nic *intelxl ) { } /** - * Reset hardware via admin queue + * Wait for reset to complete * * @v intelxl Intel device * @ret rc Return status code */ -static int intelxlvf_reset_admin ( struct intelxl_nic *intelxl ) { - struct intelxlvf_admin_descriptor *cmd; +static int intelxlvf_reset_wait ( struct intelxl_nic *intelxl ) { int rc; - /* Populate descriptor */ - cmd = intelxlvf_admin_command_descriptor ( intelxl ); - cmd->opcode = cpu_to_le16 ( INTELXLVF_ADMIN_SEND_TO_PF ); - cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_RESET ); - - /* Issue command */ - if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) - goto err_command; - /* Wait for minimum reset time */ mdelay ( INTELXLVF_RESET_DELAY_MS ); @@ -135,10 +125,35 @@ static int intelxlvf_reset_admin ( struct intelxl_nic *intelxl ) { err_active: err_teardown: intelxl_reopen_admin ( intelxl ); - err_command: return rc; } +/** + * Reset hardware via admin queue + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int intelxlvf_reset_admin ( struct intelxl_nic *intelxl ) { + struct intelxlvf_admin_descriptor *cmd; + int rc; + + /* Populate descriptor */ + cmd = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXLVF_ADMIN_SEND_TO_PF ); + cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_RESET ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + /* Wait for reset to complete */ + if ( ( rc = intelxlvf_reset_wait ( intelxl ) ) != 0 ) + return rc; + + return 0; +} + /****************************************************************************** * * Admin queue -- cgit v1.2.3-55-g7522 From c5af41a6f5b5f4a420b3e539f9e3a8dc9f8dd03e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 16 Aug 2022 15:53:41 +0100 Subject: [intelxl] Explicitly request a single queue pair for virtual functions Current versions of the E810 PF driver fail to set the number of in-use queue pairs in response to the CONFIG_VSI_QUEUES message. When the number of in-use queue pairs is less than the number of available queue pairs, this results in some packets being directed to nonexistent receive queues and hence silently dropped. Work around this PF driver bug by explicitly configuring the number of available queue pairs via the REQUEST_QUEUES message. This message triggers a VF reset that, in turn, requires us to reopen the admin queue and issue an additional GET_RESOURCES message to restore the VF to a functional state. Signed-off-by: Michael Brown --- src/drivers/net/intelxlvf.c | 45 ++++++++++++++++++++++++++++++++++++++++++++- src/drivers/net/intelxlvf.h | 14 ++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/intelxlvf.c b/src/drivers/net/intelxlvf.c index e30d8c6db..083195513 100644 --- a/src/drivers/net/intelxlvf.c +++ b/src/drivers/net/intelxlvf.c @@ -395,7 +395,8 @@ static int intelxlvf_admin_get_resources ( struct net_device *netdev ) { cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); cmd->len = cpu_to_le16 ( sizeof ( buf->caps ) ); buf = intelxlvf_admin_command_buffer ( intelxl ); - buf->caps.caps = cpu_to_le32 ( INTELXLVF_ADMIN_CAP_L2 ); + buf->caps.caps = cpu_to_le32 ( INTELXLVF_ADMIN_CAP_L2 | + INTELXLVF_ADMIN_CAP_RQPS ); /* Issue command */ if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 ) @@ -462,6 +463,42 @@ static int intelxlvf_admin_stats ( struct net_device *netdev ) { return 0; } +/** + * Configure number of queue pairs + * + * @v netdev Network device + * @ret rc Return status code + */ +static int intelxlvf_admin_request_qps ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct intelxlvf_admin_descriptor *cmd; + union intelxlvf_admin_buffer *buf; + int rc; + + /* Populate descriptor */ + cmd = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXLVF_ADMIN_SEND_TO_PF ); + cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_REQUEST_QPS ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->rqps ) ); + buf = intelxlvf_admin_command_buffer ( intelxl ); + buf->rqps.count = cpu_to_le16 ( 1 ); + + /* Issue command (which will trigger a reset) */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + /* Wait for reset to complete */ + if ( ( rc = intelxlvf_reset_wait ( intelxl ) ) != 0 ) + return rc; + + /* Reestablish capabilities to reactivate VF after reset */ + if ( ( rc = intelxlvf_admin_get_resources ( netdev ) ) != 0 ) + return rc; + + return 0; +} + /****************************************************************************** * * Network device interface @@ -781,6 +818,11 @@ static int intelxlvf_probe ( struct pci_device *pci ) { if ( ( rc = intelxlvf_admin_get_resources ( netdev ) ) != 0 ) goto err_get_resources; + /* Configure number of queue pairs, if applicable */ + if ( ( intelxl->caps & INTELXLVF_ADMIN_CAP_RQPS ) && + ( ( rc = intelxlvf_admin_request_qps ( netdev ) ) != 0 ) ) + goto err_rqps; + /* Register network device */ if ( ( rc = register_netdev ( netdev ) ) != 0 ) goto err_register_netdev; @@ -789,6 +831,7 @@ static int intelxlvf_probe ( struct pci_device *pci ) { unregister_netdev ( netdev ); err_register_netdev: + err_rqps: err_get_resources: err_version: err_reset_admin: diff --git a/src/drivers/net/intelxlvf.h b/src/drivers/net/intelxlvf.h index 783c85643..95ddf9474 100644 --- a/src/drivers/net/intelxlvf.h +++ b/src/drivers/net/intelxlvf.h @@ -130,6 +130,9 @@ struct intelxlvf_admin_get_resources_buffer { /** Layer 2 capabilities (add/remove MAC, configure promiscuous mode) */ #define INTELXLVF_ADMIN_CAP_L2 0x00000001 +/** Request Queues capabilities */ +#define INTELXLVF_ADMIN_CAP_RQPS 0x00000040 + /** Admin Queue VF Status Change Event opcode */ #define INTELXLVF_ADMIN_STATUS 0x00000011 @@ -299,6 +302,15 @@ struct intelxlvf_admin_stats_buffer { struct intelxlvf_admin_stats tx; } __attribute__ (( packed )); +/** Admin Queue VF Request Queues opcode */ +#define INTELXLVF_ADMIN_REQUEST_QPS 0x0000001d + +/** Admin Queue VF Request Queues data buffer */ +struct intelxlvf_admin_request_qps_buffer { + /** Number of queue pairs */ + uint16_t count; +} __attribute__ (( packed )); + /** Admin queue data buffer */ union intelxlvf_admin_buffer { /** Original 40 Gigabit Ethernet data buffer */ @@ -321,6 +333,8 @@ union intelxlvf_admin_buffer { struct intelxlvf_admin_irq_map_buffer irq; /** VF Get Statistics data buffer */ struct intelxlvf_admin_stats_buffer stats; + /** VF Request Queues data buffer */ + struct intelxlvf_admin_request_qps_buffer rqps; } __attribute__ (( packed )); /** Admin queue descriptor */ -- cgit v1.2.3-55-g7522 From 856ffe000e79a1af24ea11301447dd70b8d54ac2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 26 Aug 2022 14:13:52 +0100 Subject: [ena] Limit submission queue fill level to completion queue size The CREATE_CQ command is permitted to return a size smaller than requested, which could leave us in a situation where the completion queue could overflow. Avoid overflow by limiting the submission queue fill level to the actual size of the completion queue. Signed-off-by: Michael Brown --- src/drivers/net/ena.c | 13 +++++++++---- src/drivers/net/ena.h | 2 ++ 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/ena.c b/src/drivers/net/ena.c index 85da1c090..9f52a658e 100644 --- a/src/drivers/net/ena.c +++ b/src/drivers/net/ena.c @@ -391,11 +391,16 @@ static int ena_create_sq ( struct ena_nic *ena, struct ena_sq *sq, sq->prod = 0; sq->phase = ENA_SQE_PHASE; - DBGC ( ena, "ENA %p %s SQ%d at [%08lx,%08lx) db +%04x CQ%d\n", + /* Calculate fill level */ + sq->fill = sq->count; + if ( sq->fill > cq->actual ) + sq->fill = cq->actual; + + DBGC ( ena, "ENA %p %s SQ%d at [%08lx,%08lx) fill %d db +%04x CQ%d\n", ena, ena_direction ( sq->direction ), sq->id, virt_to_phys ( sq->sqe.raw ), ( virt_to_phys ( sq->sqe.raw ) + sq->len ), - sq->doorbell, cq->id ); + sq->fill, sq->doorbell, cq->id ); return 0; err_admin: @@ -658,7 +663,7 @@ static void ena_refill_rx ( struct net_device *netdev ) { unsigned int refilled = 0; /* Refill queue */ - while ( ( ena->rx.sq.prod - ena->rx.cq.cons ) < ENA_RX_COUNT ) { + while ( ( ena->rx.sq.prod - ena->rx.cq.cons ) < ena->rx.sq.fill ) { /* Allocate I/O buffer */ iobuf = alloc_iob ( len ); @@ -783,7 +788,7 @@ static int ena_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { size_t len; /* Get next submission queue entry */ - if ( ( ena->tx.sq.prod - ena->tx.cq.cons ) >= ENA_TX_COUNT ) { + if ( ( ena->tx.sq.prod - ena->tx.cq.cons ) >= ena->tx.sq.fill ) { DBGC ( ena, "ENA %p out of transmit descriptors\n", ena ); return -ENOBUFS; } diff --git a/src/drivers/net/ena.h b/src/drivers/net/ena.h index 676c5b878..2832b67e5 100644 --- a/src/drivers/net/ena.h +++ b/src/drivers/net/ena.h @@ -496,6 +496,8 @@ struct ena_sq { uint8_t direction; /** Number of entries */ uint8_t count; + /** Fill level (limited to completion queue size) */ + uint8_t fill; }; /** -- cgit v1.2.3-55-g7522 From 6d2cead461db7243330f3275ff9ea7ff4607c4f8 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 26 Aug 2022 15:48:52 +0100 Subject: [ena] Allow for out-of-order completions The ENA data path design has separate submission and completion queues. Submission queues must be refilled in strict order (since there is only a single linear tail pointer used to communicate the existence of new entries to the hardware), and completion queue entries include a request identifier copied verbatim from the submission queue entry. Once the submission queue doorbell has been rung, software never again reads from the submission queue entry and nothing ever needs to write back to the submission queue entry since completions are reported via the separate completion queue. This design allows the hardware to complete submission queue entries out of order, provided that it internally caches at least as many entries as it leaves gaps. Record and identify I/O buffers by request identifier (using a circular ring buffer of unique request identifiers), and remove the assumption that submission queue entries will be completed in order. Signed-off-by: Michael Brown --- src/drivers/net/ena.c | 65 +++++++++++++++++++++++++++++++++++++-------------- src/drivers/net/ena.h | 17 ++++++++++++-- 2 files changed, 62 insertions(+), 20 deletions(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/ena.c b/src/drivers/net/ena.c index 9f52a658e..46351dd7d 100644 --- a/src/drivers/net/ena.c +++ b/src/drivers/net/ena.c @@ -359,6 +359,7 @@ static int ena_create_sq ( struct ena_nic *ena, struct ena_sq *sq, struct ena_cq *cq ) { union ena_aq_req *req; union ena_acq_rsp *rsp; + unsigned int i; int rc; /* Allocate submission queue entries */ @@ -396,6 +397,10 @@ static int ena_create_sq ( struct ena_nic *ena, struct ena_sq *sq, if ( sq->fill > cq->actual ) sq->fill = cq->actual; + /* Initialise buffer ID ring */ + for ( i = 0 ; i < sq->count ; i++ ) + sq->ids[i] = i; + DBGC ( ena, "ENA %p %s SQ%d at [%08lx,%08lx) fill %d db +%04x CQ%d\n", ena, ena_direction ( sq->direction ), sq->id, virt_to_phys ( sq->sqe.raw ), @@ -657,10 +662,11 @@ static void ena_refill_rx ( struct net_device *netdev ) { struct ena_nic *ena = netdev->priv; struct io_buffer *iobuf; struct ena_rx_sqe *sqe; - unsigned int index; physaddr_t address; size_t len = netdev->max_pkt_len; unsigned int refilled = 0; + unsigned int index; + unsigned int id; /* Refill queue */ while ( ( ena->rx.sq.prod - ena->rx.cq.cons ) < ena->rx.sq.fill ) { @@ -672,14 +678,15 @@ static void ena_refill_rx ( struct net_device *netdev ) { break; } - /* Get next submission queue entry */ + /* Get next submission queue entry and buffer ID */ index = ( ena->rx.sq.prod % ENA_RX_COUNT ); sqe = &ena->rx.sq.sqe.rx[index]; + id = ena->rx_ids[index]; /* Construct submission queue entry */ address = virt_to_bus ( iobuf->data ); sqe->len = cpu_to_le16 ( len ); - sqe->id = cpu_to_le16 ( ena->rx.sq.prod ); + sqe->id = cpu_to_le16 ( id ); sqe->address = cpu_to_le64 ( address ); wmb(); sqe->flags = ( ENA_SQE_FIRST | ENA_SQE_LAST | ENA_SQE_CPL | @@ -691,10 +698,10 @@ static void ena_refill_rx ( struct net_device *netdev ) { ena->rx.sq.phase ^= ENA_SQE_PHASE; /* Record I/O buffer */ - assert ( ena->rx_iobuf[index] == NULL ); - ena->rx_iobuf[index] = iobuf; + assert ( ena->rx_iobuf[id] == NULL ); + ena->rx_iobuf[id] = iobuf; - DBGC2 ( ena, "ENA %p RX %d at [%08llx,%08llx)\n", ena, sqe->id, + DBGC2 ( ena, "ENA %p RX %d at [%08llx,%08llx)\n", ena, id, ( ( unsigned long long ) address ), ( ( unsigned long long ) address + len ) ); refilled++; @@ -783,8 +790,9 @@ static void ena_close ( struct net_device *netdev ) { static int ena_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { struct ena_nic *ena = netdev->priv; struct ena_tx_sqe *sqe; - unsigned int index; physaddr_t address; + unsigned int index; + unsigned int id; size_t len; /* Get next submission queue entry */ @@ -794,12 +802,13 @@ static int ena_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { } index = ( ena->tx.sq.prod % ENA_TX_COUNT ); sqe = &ena->tx.sq.sqe.tx[index]; + id = ena->tx_ids[index]; /* Construct submission queue entry */ address = virt_to_bus ( iobuf->data ); len = iob_len ( iobuf ); sqe->len = cpu_to_le16 ( len ); - sqe->id = ena->tx.sq.prod; + sqe->id = cpu_to_le16 ( id ); sqe->address = cpu_to_le64 ( address ); wmb(); sqe->flags = ( ENA_SQE_FIRST | ENA_SQE_LAST | ENA_SQE_CPL | @@ -811,10 +820,14 @@ static int ena_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { if ( ( ena->tx.sq.prod % ENA_TX_COUNT ) == 0 ) ena->tx.sq.phase ^= ENA_SQE_PHASE; + /* Record I/O buffer */ + assert ( ena->tx_iobuf[id] == NULL ); + ena->tx_iobuf[id] = iobuf; + /* Ring doorbell */ writel ( ena->tx.sq.prod, ( ena->regs + ena->tx.sq.doorbell ) ); - DBGC2 ( ena, "ENA %p TX %d at [%08llx,%08llx)\n", ena, sqe->id, + DBGC2 ( ena, "ENA %p TX %d at [%08llx,%08llx)\n", ena, id, ( ( unsigned long long ) address ), ( ( unsigned long long ) address + len ) ); return 0; @@ -828,7 +841,9 @@ static int ena_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { static void ena_poll_tx ( struct net_device *netdev ) { struct ena_nic *ena = netdev->priv; struct ena_tx_cqe *cqe; + struct io_buffer *iobuf; unsigned int index; + unsigned int id; /* Check for completed packets */ while ( ena->tx.cq.cons != ena->tx.sq.prod ) { @@ -840,16 +855,24 @@ static void ena_poll_tx ( struct net_device *netdev ) { /* Stop if completion queue entry is empty */ if ( ( cqe->flags ^ ena->tx.cq.phase ) & ENA_CQE_PHASE ) return; - DBGC2 ( ena, "ENA %p TX %d complete\n", ena, - ( le16_to_cpu ( cqe->id ) >> 2 /* Don't ask */ ) ); /* Increment consumer counter */ ena->tx.cq.cons++; if ( ! ( ena->tx.cq.cons & ena->tx.cq.mask ) ) ena->tx.cq.phase ^= ENA_CQE_PHASE; + /* Identify and free buffer ID */ + id = ENA_TX_CQE_ID ( le16_to_cpu ( cqe->id ) ); + ena->tx_ids[index] = id; + + /* Identify I/O buffer */ + iobuf = ena->tx_iobuf[id]; + assert ( iobuf != NULL ); + ena->tx_iobuf[id] = NULL; + /* Complete transmit */ - netdev_tx_complete_next ( netdev ); + DBGC2 ( ena, "ENA %p TX %d complete\n", ena, id ); + netdev_tx_complete ( netdev, iobuf ); } } @@ -863,13 +886,14 @@ static void ena_poll_rx ( struct net_device *netdev ) { struct ena_rx_cqe *cqe; struct io_buffer *iobuf; unsigned int index; + unsigned int id; size_t len; /* Check for received packets */ while ( ena->rx.cq.cons != ena->rx.sq.prod ) { /* Get next completion queue entry */ - index = ( ena->rx.cq.cons % ENA_RX_COUNT ); + index = ( ena->rx.cq.cons & ena->rx.cq.mask ); cqe = &ena->rx.cq.cqe.rx[index]; /* Stop if completion queue entry is empty */ @@ -881,15 +905,20 @@ static void ena_poll_rx ( struct net_device *netdev ) { if ( ! ( ena->rx.cq.cons & ena->rx.cq.mask ) ) ena->rx.cq.phase ^= ENA_CQE_PHASE; + /* Identify and free buffer ID */ + id = le16_to_cpu ( cqe->id ); + ena->rx_ids[index] = id; + /* Populate I/O buffer */ - iobuf = ena->rx_iobuf[index]; - ena->rx_iobuf[index] = NULL; + iobuf = ena->rx_iobuf[id]; + assert ( iobuf != NULL ); + ena->rx_iobuf[id] = NULL; len = le16_to_cpu ( cqe->len ); iob_put ( iobuf, len ); /* Hand off to network stack */ DBGC2 ( ena, "ENA %p RX %d complete (length %zd)\n", - ena, le16_to_cpu ( cqe->id ), len ); + ena, id, len ); netdev_rx ( netdev, iobuf ); } } @@ -952,11 +981,11 @@ static int ena_probe ( struct pci_device *pci ) { ena_cq_init ( &ena->tx.cq, ENA_TX_COUNT, sizeof ( ena->tx.cq.cqe.tx[0] ) ); ena_sq_init ( &ena->tx.sq, ENA_SQ_TX, ENA_TX_COUNT, - sizeof ( ena->tx.sq.sqe.tx[0] ) ); + sizeof ( ena->tx.sq.sqe.tx[0] ), ena->tx_ids ); ena_cq_init ( &ena->rx.cq, ENA_RX_COUNT, sizeof ( ena->rx.cq.cqe.rx[0] ) ); ena_sq_init ( &ena->rx.sq, ENA_SQ_RX, ENA_RX_COUNT, - sizeof ( ena->rx.sq.sqe.rx[0] ) ); + sizeof ( ena->rx.sq.sqe.rx[0] ), ena->rx_ids ); /* Fix up PCI device */ adjust_pci_device ( pci ); diff --git a/src/drivers/net/ena.h b/src/drivers/net/ena.h index 2832b67e5..c76598730 100644 --- a/src/drivers/net/ena.h +++ b/src/drivers/net/ena.h @@ -454,6 +454,9 @@ struct ena_tx_cqe { uint16_t cons; } __attribute__ (( packed )); +/** Transmit completion request identifier */ +#define ENA_TX_CQE_ID(id) ( (id) >> 2 ) + /** Receive completion queue entry */ struct ena_rx_cqe { /** Reserved */ @@ -482,6 +485,8 @@ struct ena_sq { /** Raw data */ void *raw; } sqe; + /** Buffer IDs */ + uint8_t *ids; /** Doorbell register offset */ unsigned int doorbell; /** Total length of entries */ @@ -507,14 +512,16 @@ struct ena_sq { * @v direction Direction * @v count Number of entries * @v size Size of each entry + * @v ids Buffer IDs */ static inline __attribute__ (( always_inline )) void ena_sq_init ( struct ena_sq *sq, unsigned int direction, unsigned int count, - size_t size ) { + size_t size, uint8_t *ids ) { sq->len = ( count * size ); sq->direction = direction; sq->count = count; + sq->ids = ids; } /** Completion queue */ @@ -583,7 +590,13 @@ struct ena_nic { struct ena_qp tx; /** Receive queue */ struct ena_qp rx; - /** Receive I/O buffers */ + /** Transmit buffer IDs */ + uint8_t tx_ids[ENA_TX_COUNT]; + /** Transmit I/O buffers, indexed by buffer ID */ + struct io_buffer *tx_iobuf[ENA_TX_COUNT]; + /** Receive buffer IDs */ + uint8_t rx_ids[ENA_RX_COUNT]; + /** Receive I/O buffers, indexed by buffer ID */ struct io_buffer *rx_iobuf[ENA_RX_COUNT]; }; -- cgit v1.2.3-55-g7522 From 9f81e97af5e3861ea2d0ad7b11f2c8746c2c52f9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 26 Aug 2022 13:15:07 +0100 Subject: [ena] Specify the unused completion queue MSI-X vector as 0xffffffff Some versions of the ENA firmware (observed on a c6i.large instance in eu-west-2) will complain if the completion queue's MSI-X vector field is left empty, even though the queue configuration specifies that interrupts are not used. Work around these firmware versions by passing in what appears to be the magic "no MSI-X vector" value in this field. Signed-off-by: Michael Brown --- src/drivers/net/ena.c | 1 + src/drivers/net/ena.h | 8 ++++++++ 2 files changed, 9 insertions(+) (limited to 'src/drivers/net') diff --git a/src/drivers/net/ena.c b/src/drivers/net/ena.c index 46351dd7d..bad0238fe 100644 --- a/src/drivers/net/ena.c +++ b/src/drivers/net/ena.c @@ -469,6 +469,7 @@ static int ena_create_cq ( struct ena_nic *ena, struct ena_cq *cq ) { req->header.opcode = ENA_CREATE_CQ; req->create_cq.size = cq->size; req->create_cq.count = cpu_to_le16 ( cq->requested ); + req->create_cq.vector = cpu_to_le32 ( ENA_MSIX_NONE ); req->create_cq.address = cpu_to_le64 ( virt_to_bus ( cq->cqe.raw ) ); /* Issue request */ diff --git a/src/drivers/net/ena.h b/src/drivers/net/ena.h index c76598730..78693e678 100644 --- a/src/drivers/net/ena.h +++ b/src/drivers/net/ena.h @@ -230,6 +230,14 @@ struct ena_create_cq_req { uint64_t address; } __attribute__ (( packed )); +/** Empty MSI-X vector + * + * Some versions of the ENA firmware will complain if the completion + * queue's MSI-X vector field is left empty, even though the queue + * configuration specifies that interrupts are not used. + */ +#define ENA_MSIX_NONE 0xffffffffUL + /** Create completion queue response */ struct ena_create_cq_rsp { /** Header */ -- cgit v1.2.3-55-g7522 From 3b81a4e2568cfca50893095638a03069785cd33e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 26 Aug 2022 13:37:23 +0100 Subject: [ena] Provide a host information page Some versions of the ENA firmware (observed on a c6i.large instance in eu-west-2) seem to require a host information page, without which the CREATE_CQ command will fail with ENA_ADMIN_UNKNOWN_ERROR. These firmware versions also seem to require us to claim that we are a Linux kernel with a specific driver major version number. This appears to be a firmware bug, as revealed by Linux kernel commit 1a63443af ("net/amazon: Ensure that driver version is aligned to the linux kernel"): this commit changed the value of the driver version number field to be the Linux kernel version, and was hastily reverted in commit 92040c6da ("net: ena: fix broken interface between ENA driver and FW") which clarified that the version number field does actually have some undocumented significance to some versions of the firmware. Fix by providing a host information page via the SET_FEATURE command, incorporating the apparently necessary lies about our identity. Signed-off-by: Michael Brown --- src/drivers/net/ena.c | 58 +++++++++++++++++++++++++++++ src/drivers/net/ena.h | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) (limited to 'src/drivers/net') diff --git a/src/drivers/net/ena.c b/src/drivers/net/ena.c index bad0238fe..c2a48a27d 100644 --- a/src/drivers/net/ena.c +++ b/src/drivers/net/ena.c @@ -24,6 +24,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include +#include #include #include #include @@ -34,6 +35,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include "ena.h" /** @file @@ -607,6 +609,32 @@ static int ena_get_device_attributes ( struct net_device *netdev ) { return 0; } +/** + * Set host attributes + * + * @v ena ENA device + * @ret rc Return status code + */ +static int ena_set_host_attributes ( struct ena_nic *ena ) { + union ena_aq_req *req; + union ena_acq_rsp *rsp; + union ena_feature *feature; + int rc; + + /* Construct request */ + req = ena_admin_req ( ena ); + req->header.opcode = ENA_SET_FEATURE; + req->set_feature.id = ENA_HOST_ATTRIBUTES; + feature = &req->set_feature.feature; + feature->host.info = cpu_to_le64 ( virt_to_bus ( ena->info ) ); + + /* Issue request */ + if ( ( rc = ena_admin ( ena, req, &rsp ) ) != 0 ) + return rc; + + return 0; +} + /** * Get statistics (for debugging) * @@ -965,6 +993,7 @@ static struct net_device_operations ena_operations = { static int ena_probe ( struct pci_device *pci ) { struct net_device *netdev; struct ena_nic *ena; + struct ena_host_info *info; int rc; /* Allocate and initialise net device */ @@ -998,6 +1027,25 @@ static int ena_probe ( struct pci_device *pci ) { goto err_ioremap; } + /* Allocate and initialise host info */ + info = malloc_phys ( PAGE_SIZE, PAGE_SIZE ); + if ( ! info ) { + rc = -ENOMEM; + goto err_info; + } + ena->info = info; + memset ( info, 0, PAGE_SIZE ); + info->type = cpu_to_le32 ( ENA_HOST_INFO_TYPE_LINUX ); + snprintf ( info->dist_str, sizeof ( info->dist_str ), "%s", + ( product_name[0] ? product_name : product_short_name ) ); + snprintf ( info->kernel_str, sizeof ( info->kernel_str ), "%s", + product_version ); + info->version = cpu_to_le32 ( ENA_HOST_INFO_VERSION_WTF ); + info->spec = cpu_to_le16 ( ENA_HOST_INFO_SPEC_2_0 ); + info->busdevfn = cpu_to_le16 ( pci->busdevfn ); + DBGC2 ( ena, "ENA %p host info:\n", ena ); + DBGC2_HDA ( ena, virt_to_phys ( info ), info, sizeof ( *info ) ); + /* Reset the NIC */ if ( ( rc = ena_reset ( ena ) ) != 0 ) goto err_reset; @@ -1006,6 +1054,10 @@ static int ena_probe ( struct pci_device *pci ) { if ( ( rc = ena_create_admin ( ena ) ) != 0 ) goto err_create_admin; + /* Set host attributes */ + if ( ( rc = ena_set_host_attributes ( ena ) ) != 0 ) + goto err_set_host_attributes; + /* Fetch MAC address */ if ( ( rc = ena_get_device_attributes ( netdev ) ) != 0 ) goto err_get_device_attributes; @@ -1024,10 +1076,13 @@ static int ena_probe ( struct pci_device *pci ) { unregister_netdev ( netdev ); err_register_netdev: err_get_device_attributes: + err_set_host_attributes: ena_destroy_admin ( ena ); err_create_admin: ena_reset ( ena ); err_reset: + free_phys ( ena->info, PAGE_SIZE ); + err_info: iounmap ( ena->regs ); err_ioremap: netdev_nullify ( netdev ); @@ -1054,6 +1109,9 @@ static void ena_remove ( struct pci_device *pci ) { /* Reset card */ ena_reset ( ena ); + /* Free host info */ + free_phys ( ena->info, PAGE_SIZE ); + /* Free network device */ iounmap ( ena->regs ); netdev_nullify ( netdev ); diff --git a/src/drivers/net/ena.h b/src/drivers/net/ena.h index 78693e678..cbee1e768 100644 --- a/src/drivers/net/ena.h +++ b/src/drivers/net/ena.h @@ -127,10 +127,86 @@ struct ena_device_attributes { uint32_t mtu; } __attribute__ (( packed )); +/** Host attributes */ +#define ENA_HOST_ATTRIBUTES 28 + +/** Host attributes */ +struct ena_host_attributes { + /** Host info base address */ + uint64_t info; + /** Debug area base address */ + uint64_t debug; + /** Debug area size */ + uint32_t debug_len; +} __attribute__ (( packed )); + +/** Host information */ +struct ena_host_info { + /** Operating system type */ + uint32_t type; + /** Operating system distribution (string) */ + char dist_str[128]; + /** Operating system distribution (numeric) */ + uint32_t dist; + /** Kernel version (string) */ + char kernel_str[32]; + /** Kernel version (numeric) */ + uint32_t kernel; + /** Driver version */ + uint32_t version; + /** Linux network device features */ + uint64_t linux_features; + /** ENA specification version */ + uint16_t spec; + /** PCI bus:dev.fn address */ + uint16_t busdevfn; + /** Number of CPUs */ + uint16_t cpus; + /** Reserved */ + uint8_t reserved_a[2]; + /** Supported features */ + uint32_t features; +} __attribute__ (( packed )); + +/** Linux operating system type + * + * There is a defined "iPXE" operating system type (with value 5). + * However, some very broken versions of the ENA firmware will refuse + * to allow a completion queue to be created if the "iPXE" type is + * used. + */ +#define ENA_HOST_INFO_TYPE_LINUX 1 + +/** Driver version + * + * The driver version field is nominally used to report a version + * number outside of the VM for consumption by humans (and potentially + * by automated monitoring tools that could e.g. check for outdated + * versions with known security flaws). + * + * However, at some point in the development of the ENA firmware, some + * unknown person at AWS thought it would be sensible to apply a + * machine interpretation to this field and adjust the behaviour of + * the firmware based on its value, thereby creating a maintenance and + * debugging nightmare for all existing and future drivers. + * + * Hint to engineers: if you ever find yourself writing code of the + * form "if (version == SOME_MAGIC_NUMBER)" then something has gone + * very, very wrong. This *always* indicates that something is + * broken, either in your own code or in the code with which you are + * forced to interact. + */ +#define ENA_HOST_INFO_VERSION_WTF 0x00000002UL + +/** ENA specification version */ +#define ENA_HOST_INFO_SPEC_2_0 0x0200 + /** Feature */ union ena_feature { /** Device attributes */ struct ena_device_attributes device; + /** Host attributes */ + struct ena_host_attributes host; }; /** Submission queue direction */ @@ -300,6 +376,27 @@ struct ena_get_feature_rsp { union ena_feature feature; } __attribute__ (( packed )); +/** Set feature */ +#define ENA_SET_FEATURE 9 + +/** Set feature request */ +struct ena_set_feature_req { + /** Header */ + struct ena_aq_header header; + /** Length */ + uint32_t len; + /** Address */ + uint64_t address; + /** Flags */ + uint8_t flags; + /** Feature identifier */ + uint8_t id; + /** Reserved */ + uint8_t reserved[2]; + /** Feature */ + union ena_feature feature; +} __attribute__ (( packed )); + /** Get statistics */ #define ENA_GET_STATS 11 @@ -360,6 +457,8 @@ union ena_aq_req { struct ena_destroy_cq_req destroy_cq; /** Get feature */ struct ena_get_feature_req get_feature; + /** Set feature */ + struct ena_set_feature_req set_feature; /** Get statistics */ struct ena_get_stats_req get_stats; /** Padding */ @@ -590,6 +689,8 @@ struct ena_qp { struct ena_nic { /** Registers */ void *regs; + /** Host info */ + struct ena_host_info *info; /** Admin queue */ struct ena_aq aq; /** Admin completion queue */ -- cgit v1.2.3-55-g7522 From a80124456ee9ade7a63b69f167c3b1348c8d93c0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 26 Aug 2022 13:17:48 +0100 Subject: [ena] Increase receive ring size to 128 entries Some versions of the ENA hardware (observed on a c6i.large instance in eu-west-2) seem to require a receive ring containing at least 128 entries: any smaller ring will never see receive completions or will stall after the first few completions. Increase the receive ring size to 128 entries (determined empirically) for compatibility with these hardware versions. Limit the receive ring fill level to 16 (as at present) to avoid consuming more memory than will typically be available in the internal heap. Signed-off-by: Michael Brown --- src/drivers/net/ena.c | 6 +++--- src/drivers/net/ena.h | 11 +++++++++-- 2 files changed, 12 insertions(+), 5 deletions(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/ena.c b/src/drivers/net/ena.c index c2a48a27d..400ae44fa 100644 --- a/src/drivers/net/ena.c +++ b/src/drivers/net/ena.c @@ -395,7 +395,7 @@ static int ena_create_sq ( struct ena_nic *ena, struct ena_sq *sq, sq->phase = ENA_SQE_PHASE; /* Calculate fill level */ - sq->fill = sq->count; + sq->fill = sq->max; if ( sq->fill > cq->actual ) sq->fill = cq->actual; @@ -1010,11 +1010,11 @@ static int ena_probe ( struct pci_device *pci ) { ena->acq.phase = ENA_ACQ_PHASE; ena_cq_init ( &ena->tx.cq, ENA_TX_COUNT, sizeof ( ena->tx.cq.cqe.tx[0] ) ); - ena_sq_init ( &ena->tx.sq, ENA_SQ_TX, ENA_TX_COUNT, + ena_sq_init ( &ena->tx.sq, ENA_SQ_TX, ENA_TX_COUNT, ENA_TX_COUNT, sizeof ( ena->tx.sq.sqe.tx[0] ), ena->tx_ids ); ena_cq_init ( &ena->rx.cq, ENA_RX_COUNT, sizeof ( ena->rx.cq.cqe.rx[0] ) ); - ena_sq_init ( &ena->rx.sq, ENA_SQ_RX, ENA_RX_COUNT, + ena_sq_init ( &ena->rx.sq, ENA_SQ_RX, ENA_RX_COUNT, ENA_RX_FILL, sizeof ( ena->rx.sq.sqe.rx[0] ), ena->rx_ids ); /* Fix up PCI device */ diff --git a/src/drivers/net/ena.h b/src/drivers/net/ena.h index cbee1e768..4e1896e86 100644 --- a/src/drivers/net/ena.h +++ b/src/drivers/net/ena.h @@ -28,7 +28,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ENA_TX_COUNT 16 /** Number of receive queue entries */ -#define ENA_RX_COUNT 16 +#define ENA_RX_COUNT 128 + +/** Receive queue maximum fill level */ +#define ENA_RX_FILL 16 /** Base address low register offset */ #define ENA_BASE_LO 0x0 @@ -608,6 +611,8 @@ struct ena_sq { uint8_t direction; /** Number of entries */ uint8_t count; + /** Maximum fill level */ + uint8_t max; /** Fill level (limited to completion queue size) */ uint8_t fill; }; @@ -618,16 +623,18 @@ struct ena_sq { * @v sq Submission queue * @v direction Direction * @v count Number of entries + * @v max Maximum fill level * @v size Size of each entry * @v ids Buffer IDs */ static inline __attribute__ (( always_inline )) void ena_sq_init ( struct ena_sq *sq, unsigned int direction, unsigned int count, - size_t size, uint8_t *ids ) { + unsigned int max, size_t size, uint8_t *ids ) { sq->len = ( count * size ); sq->direction = direction; sq->count = count; + sq->max = max; sq->ids = ids; } -- cgit v1.2.3-55-g7522 From 081b3eefc48953795122c4b1a38430498e2bb2b6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 19 Sep 2022 17:49:25 +0100 Subject: [ena] Assign memory BAR if left empty by BIOS Some BIOSes in AWS EC2 (observed with a c6i.metal instance in eu-west-2) will fail to assign an MMIO address to the ENA device, which causes ioremap() to fail. Experiments show that the ENA device is the only device behind its bridge, even when multiple ENA devices are present, and that the BIOS does assign a memory window to the bridge. We may therefore choose to assign the device an MMIO address at the start of the bridge's memory window. Signed-off-by: Michael Brown --- src/drivers/net/ena.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'src/drivers/net') diff --git a/src/drivers/net/ena.c b/src/drivers/net/ena.c index 400ae44fa..22e7e1e30 100644 --- a/src/drivers/net/ena.c +++ b/src/drivers/net/ena.c @@ -35,6 +35,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include "ena.h" @@ -984,6 +985,45 @@ static struct net_device_operations ena_operations = { ****************************************************************************** */ +/** + * Assign memory BAR + * + * @v ena ENA device + * @v pci PCI device + * @ret rc Return status code + * + * Some BIOSes in AWS EC2 are observed to fail to assign a base + * address to the ENA device. The device is the only device behind + * 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 ) { + struct pci_bridge *bridge; + + /* Locate PCI bridge */ + bridge = pcibridge_find ( pci ); + if ( ! bridge ) { + DBGC ( ena, "ENA %p found no PCI bridge\n", ena ); + return -ENOTCONN; + } + + /* Sanity check */ + if ( PCI_SLOT ( pci->busdevfn ) || PCI_FUNC ( pci->busdevfn ) ) { + DBGC ( ena, "ENA %p at " PCI_FMT " may not be only device " + "on bus\n", ena, PCI_ARGS ( pci ) ); + return -ENOTSUP; + } + + /* Place device at start of memory window */ + pci_write_config_dword ( 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 ); + + return 0; +} + /** * Probe PCI device * @@ -1020,6 +1060,10 @@ static int ena_probe ( struct pci_device *pci ) { /* Fix up PCI device */ 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; + /* Map registers */ ena->regs = pci_ioremap ( pci, pci->membase, ENA_BAR_SIZE ); if ( ! ena->regs ) { @@ -1085,6 +1129,7 @@ static int ena_probe ( struct pci_device *pci ) { err_info: iounmap ( ena->regs ); err_ioremap: + err_membase: netdev_nullify ( netdev ); netdev_put ( netdev ); err_alloc: -- cgit v1.2.3-55-g7522