Nothing lives outside the repo: the bench ixgbe driver source moves in tracked at kernel/ixgbe-sff/ (build artifacts gitignored, load-ixgbe repointed, module build verified from the new home — the reg_ops/ts_bench bench extras are finally versioned), the ice sff_i2c patch is vendored at docs/nics/e810/ice-sff-i2c.patch with its out-of-tree build recipe (TRACE_INCLUDE_PATH fixup, version-matched Makefile), the e810 NVM flash logistics land in the e810 README (exact package name, rollback -m B49691D578EC into the inactive bank, post-flash inventory failing until power cycle is the expected tell), and every ~/work/phydiag-work pointer becomes an inline statement; phydiag-work, ice-sff, e810nvm and the wiitekphy CLI (a duplicate of documented transport+protocol) are deleted
This commit is contained in:
@@ -47,7 +47,7 @@ Every MCIA open question resolves in favor of the transport, live against the FS
|
||||
|
||||
- **Firmware accepts arbitrary I2C device addresses.** `i2c_device_address=0x56` (the BCM bridge) transacts with status 0 in both directions; kernel paths only ever use 0x50/0x51 but the firmware doesn't care.
|
||||
- **MCIA writes emit exactly one offset byte**, so `device_address` = first SMI frame byte synthesizes the 3–5 byte command frame exactly.
|
||||
- **The bridge tolerates MCIA's write-offset-then-read framing live** (the offset byte injected before the read phase is ignored, as the X520 bit-bang emulation predicted — `~/work/phydiag-work/bcm_mcia_emu.py`); PHY ID 0x3590 reads back correctly.
|
||||
- **The bridge tolerates MCIA's write-offset-then-read framing live** (the offset byte injected before the read phase is ignored, as the X520 bit-bang emulation predicted); PHY ID 0x3590 reads back correctly.
|
||||
- **Handler mailbox ops work**: DATA1–5 writes, CMD write, STATUS poll (PASS observed), all through the same framing.
|
||||
- RollBall is plain offset addressing at 0x51 — fits MCIA by construction.
|
||||
|
||||
@@ -61,7 +61,7 @@ The recipe (one SMI transaction = one MCIA SET, plus one MCIA GET for reads):
|
||||
|
||||
There are two host paths to MCIA, and the split is decisive:
|
||||
|
||||
- **`/dev/fwctl` (the kernel's sanctioned firmware back-channel) is read-only for MCIA.** ACCESS_REG reads work at scope `DEBUG_READ_ONLY`; **writes are silently dropped** — the RPC returns success at every layer (`errno=0`, command status 0, MCIA status 0) yet the register never changes, at every scope including `DEBUG_WRITE_FULL`. The firmware honours the ACCESS_REG opcode under fwctl's restricted user context (uid 2, caps `0x04000000`) but no-ops the actual I2C write. Proven in `~/work/phydiag-work/fwctl_mcia/diag`. Since a RollBall register **read** requires **writes** (set page/devad/command), fwctl cannot run RollBall at all.
|
||||
- **`/dev/fwctl` (the kernel's sanctioned firmware back-channel) is read-only for MCIA.** ACCESS_REG reads work at scope `DEBUG_READ_ONLY`; **writes are silently dropped** — the RPC returns success at every layer (`errno=0`, command status 0, MCIA status 0) yet the register never changes, at every scope including `DEBUG_WRITE_FULL`. The firmware honours the ACCESS_REG opcode under fwctl's restricted user context (uid 2, caps `0x04000000`) but no-ops the actual I2C write. Proven live with full pre/post op logging. Since a RollBall register **read** requires **writes** (set page/devad/command), fwctl cannot run RollBall at all.
|
||||
- **The mstflint PCICONF path (VSC vendor capability → ICMD → FLASH_REG_ACCESS) can write**, so cabletest uses it. Per-dword gateway ops through the PCI config-space vendor capability reach ICMD space; a register-access command wrapped in operation+reg TLVs carries the MCIA transaction. Reimplemented in Go (`phy_mcia.go`), no mstflint dependency at runtime.
|
||||
|
||||
Two things that are not optional and one that is not needed:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Intel E810 (ice)
|
||||
|
||||
Out of the box. The measurement path was originally built against it — its datapath delivers per-packet MAC RX timestamps (`rx_filter=ALL`), which the committed rate design no longer needs ([../README.md](../README.md)). A patched ice + `sff_i2c` exists for read-side diagnostics work.
|
||||
Out of the box. The measurement path was originally built against it — its datapath delivers per-packet MAC RX timestamps (`rx_filter=ALL`), which the committed rate design no longer needs ([../README.md](../README.md)). The `sff_i2c` ice patch for read-side diagnostics is vendored here as [ice-sff-i2c.patch](ice-sff-i2c.patch); to run it, apply to a copy of the kernel's ice source and build out of tree (`make -C /usr/lib/modules/$(uname -r)/build M=<dir> modules`, then `rmmod irdma ice; insmod`), with two out-of-tree fixups: `ice_trace.h` needs `TRACE_INCLUDE_PATH` set to the build dir's absolute path and `TRACE_INCLUDE_FILE ice_trace`, and the Makefile's object list must match the source snapshot's kernel version.
|
||||
|
||||
## Host notes (if it returns)
|
||||
|
||||
@@ -26,8 +26,8 @@ Read-side transport details (the patched ice exists for read-side work, so these
|
||||
- No offset-less read mode exists, so the plain mdio-i2c clause-45 read phase (bcm84881-style at 0x56) cannot be formed. RollBall fits fully (≤4 B native multi-byte writes).
|
||||
- Latency is the wire-truth judge on this transport: on 3.10 firmware, silently-dropped 1-byte writes completed in ~63 µs while real wire traffic ran ~103 µs/byte (reads) and multi-ms ack-polling (writes), and `ok` returns even for writes at an empty address — protocol-level readback is the only delivery confirmation.
|
||||
|
||||
Firmware on the card: NVM 5.01 (release 31.2.2, EETRACK 0x80021FA2). The update package and logs live in `~/work/e810nvm/`; `nvmupdate64e` needs `-AllowMissingCert` on this platform (harmless — nothing boots from the NIC), writes no file backup, and rollback lives in the card's inactive flash bank (`nvmupdate64e -r -m <MAC>`).
|
||||
Firmware on the card: NVM 5.01 (release 31.2.2, EETRACK 0x80021FA2), flashed with `nvmupdate64e` 1.43.37.1a from Intel's `E810_NVMUpdatePackage_v5_01_Linux.tar.gz` (re-downloadable; nothing kept locally). Flash logistics: the tool needs `-AllowMissingCert` on this platform (the MS UEFI CA 2023 cert is absent — harmless, nothing boots from the NIC), writes no file backup even with `-b` ("Backup images created" means the card's inactive flash bank, where rollback lives: `nvmupdate64e -r -m B49691D578EC`), and the post-flash inventory fails until a power cycle activates the new bank — that failure is the expected tell, not an error.
|
||||
|
||||
## The bit-bang-below-the-AQ idea — retired with the timestamp requirement
|
||||
|
||||
The write block is at the **Admin Queue** (firmware) level — the `0x06E2/E3` topo-I2C commands are refused with EPERM. That is not the same as the module's SDA/SCL being unreachable: the 82599 reaches the module I2C **without firmware** by toggling SDA/SCL as GPIO bits in `I2CCTL` (the `~/work/ixgbe-sff/` patch bit-bangs arbitrary START…STOP from those primitives), and whether the E810 exposes an equivalent register-level / SDP-GPIO path below the AQ policy was never tested. The idea existed to marry all-packet stamps to raw I2C in one card; the smoothed-bucket rate plan ([../x520/README.md](../x520/README.md)) dissolves the stamps requirement, so the X520 covers everything and this investigation is not needed. It stays recorded as the E810's one open door if per-frame arrival times are ever wanted again — the investigation would start at the datasheet's SDP/GPIO and low-level I2C controller registers, asking whether the cage's SDA/SCL are host-addressable.
|
||||
The write block is at the **Admin Queue** (firmware) level — the `0x06E2/E3` topo-I2C commands are refused with EPERM. That is not the same as the module's SDA/SCL being unreachable: the 82599 reaches the module I2C **without firmware** by toggling SDA/SCL as GPIO bits in `I2CCTL` (the `kernel/ixgbe-sff/` bench driver bit-bangs arbitrary START…STOP from those primitives), and whether the E810 exposes an equivalent register-level / SDP-GPIO path below the AQ policy was never tested. The idea existed to marry all-packet stamps to raw I2C in one card; the smoothed-bucket rate plan ([../x520/README.md](../x520/README.md)) dissolves the stamps requirement, so the X520 covers everything and this investigation is not needed. It stays recorded as the E810's one open door if per-frame arrival times are ever wanted again — the investigation would start at the datasheet's SDP/GPIO and low-level I2C controller registers, asking whether the cage's SDA/SCL are host-addressable.
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
diff --git a/drivers/net/ethernet/intel/ice/ice_debugfs.c b/drivers/net/ethernet/intel/ice/ice_debugfs.c
|
||||
index f450250fc..f0205e58a 100644
|
||||
--- a/drivers/net/ethernet/intel/ice/ice_debugfs.c
|
||||
+++ b/drivers/net/ethernet/intel/ice/ice_debugfs.c
|
||||
@@ -2,10 +2,187 @@
|
||||
/* Copyright (c) 2022, Intel Corporation. */
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
+#include <linux/uaccess.h>
|
||||
#include "ice.h"
|
||||
|
||||
static struct dentry *ice_debugfs_root;
|
||||
|
||||
+#define ICE_SFF_I2C_MAX_DATA 16
|
||||
+
|
||||
+struct ice_sff_i2c_ctx {
|
||||
+ struct ice_pf *pf;
|
||||
+ char resp[ICE_SFF_I2C_MAX_DATA * 3 + 32];
|
||||
+ size_t resp_len;
|
||||
+};
|
||||
+
|
||||
+static int ice_sff_i2c_open(struct inode *inode, struct file *filp)
|
||||
+{
|
||||
+ struct ice_sff_i2c_ctx *ctx;
|
||||
+
|
||||
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
||||
+ if (!ctx)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ ctx->pf = inode->i_private;
|
||||
+ filp->private_data = ctx;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int ice_sff_i2c_release(struct inode *inode, struct file *filp)
|
||||
+{
|
||||
+ kfree(filp->private_data);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static ssize_t ice_sff_i2c_write(struct file *filp, const char __user *ubuf,
|
||||
+ size_t count, loff_t *ppos)
|
||||
+{
|
||||
+ struct ice_sff_i2c_ctx *ctx = filp->private_data;
|
||||
+ struct ice_hw *hw = &ctx->pf->hw;
|
||||
+ u8 data[ICE_SFF_I2C_MAX_DATA] = {};
|
||||
+ u8 bus, off, arg;
|
||||
+ char cmd[64];
|
||||
+ char op;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (!count || count >= sizeof(cmd))
|
||||
+ return -EINVAL;
|
||||
+ if (copy_from_user(cmd, ubuf, count))
|
||||
+ return -EFAULT;
|
||||
+ cmd[count] = '\0';
|
||||
+
|
||||
+ switch (cmd[0]) {
|
||||
+ case 'r':
|
||||
+ if (sscanf(cmd, "%c %hhx %hhx %hhx", &op, &bus, &off, &arg) != 4)
|
||||
+ return -EINVAL;
|
||||
+ if (arg < 1 || arg > ICE_SFF_I2C_MAX_DATA)
|
||||
+ return -EINVAL;
|
||||
+ ret = ice_aq_sff_eeprom(hw, 0, bus, off, 0, 0, data, arg,
|
||||
+ false, NULL);
|
||||
+ break;
|
||||
+ case 'w':
|
||||
+ if (sscanf(cmd, "%c %hhx %hhx %hhx", &op, &bus, &off, &arg) != 4)
|
||||
+ return -EINVAL;
|
||||
+ data[0] = arg;
|
||||
+ ret = ice_aq_sff_eeprom(hw, 0, bus, off, 0, 0, data, 1,
|
||||
+ true, NULL);
|
||||
+ break;
|
||||
+ case 't': {
|
||||
+ struct ice_aqc_get_link_topo tcmd = {};
|
||||
+ u8 part;
|
||||
+ u16 handle;
|
||||
+
|
||||
+ if (sscanf(cmd, "%c %hhx %hhx", &op,
|
||||
+ &tcmd.addr.topo_params.node_type_ctx,
|
||||
+ &tcmd.addr.topo_params.index) != 3)
|
||||
+ return -EINVAL;
|
||||
+ ret = ice_aq_get_netlist_node(hw, &tcmd, &part, &handle);
|
||||
+ if (!ret) {
|
||||
+ ctx->resp_len = scnprintf(ctx->resp, sizeof(ctx->resp),
|
||||
+ "ok %02x %04x\n", part,
|
||||
+ handle);
|
||||
+ *ppos = 0;
|
||||
+ return count;
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
+ case 'i': case 'j': {
|
||||
+ struct ice_aqc_link_topo_addr topo = {};
|
||||
+ u16 bus16, addr16, handle;
|
||||
+ u8 params, n;
|
||||
+ int fields;
|
||||
+
|
||||
+ fields = sscanf(cmd,
|
||||
+ "%c %hhx %hhx %hx %hx %hx %hhx %hhx %hhx %hhx %hhx",
|
||||
+ &op, &topo.topo_params.node_type_ctx,
|
||||
+ &topo.topo_params.index, &handle, &bus16,
|
||||
+ &addr16, ¶ms, &data[0], &data[1], &data[2],
|
||||
+ &data[3]);
|
||||
+ if (fields < 7)
|
||||
+ return -EINVAL;
|
||||
+ topo.handle = cpu_to_le16(handle);
|
||||
+ n = params & ICE_AQC_I2C_DATA_SIZE_M;
|
||||
+ arg = n;
|
||||
+ if (op == 'i') {
|
||||
+ if (fields != 7 || n < 1)
|
||||
+ return -EINVAL;
|
||||
+ ret = ice_aq_read_i2c(hw, topo, bus16,
|
||||
+ cpu_to_le16(addr16), params,
|
||||
+ data, NULL);
|
||||
+ } else {
|
||||
+ if (n < 1 || n > 4 || fields != 7 + n)
|
||||
+ return -EINVAL;
|
||||
+ ret = ice_aq_write_i2c(hw, topo, bus16,
|
||||
+ cpu_to_le16(addr16), params,
|
||||
+ data, NULL);
|
||||
+ }
|
||||
+ op = op == 'i' ? 'r' : 'w';
|
||||
+ break;
|
||||
+ }
|
||||
+ case 'x': {
|
||||
+ struct libie_aq_desc desc = {};
|
||||
+ u16 opcode, flags;
|
||||
+ u32 p[4] = {};
|
||||
+
|
||||
+ if (sscanf(cmd, "%c %hx %hx %x %x %x %x", &op, &flags, &opcode,
|
||||
+ &p[0], &p[1], &p[2], &p[3]) != 7)
|
||||
+ return -EINVAL;
|
||||
+ desc.flags = cpu_to_le16(flags);
|
||||
+ desc.opcode = cpu_to_le16(opcode);
|
||||
+ desc.params.generic.param0 = cpu_to_le32(p[0]);
|
||||
+ desc.params.generic.param1 = cpu_to_le32(p[1]);
|
||||
+ desc.params.generic.addr_high = cpu_to_le32(p[2]);
|
||||
+ desc.params.generic.addr_low = cpu_to_le32(p[3]);
|
||||
+ ret = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
|
||||
+ ctx->resp_len = scnprintf(ctx->resp, sizeof(ctx->resp),
|
||||
+ "%s %d aq %d wb %08x %08x %08x %08x\n",
|
||||
+ ret ? "err" : "ok", ret,
|
||||
+ hw->adminq.sq_last_status,
|
||||
+ le32_to_cpu(desc.params.generic.param0),
|
||||
+ le32_to_cpu(desc.params.generic.param1),
|
||||
+ le32_to_cpu(desc.params.generic.addr_high),
|
||||
+ le32_to_cpu(desc.params.generic.addr_low));
|
||||
+ *ppos = 0;
|
||||
+ return count;
|
||||
+ }
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ if (ret)
|
||||
+ ctx->resp_len = scnprintf(ctx->resp, sizeof(ctx->resp),
|
||||
+ "err %d aq %d\n", ret,
|
||||
+ hw->adminq.sq_last_status);
|
||||
+ else if (op == 'r')
|
||||
+ ctx->resp_len = scnprintf(ctx->resp, sizeof(ctx->resp),
|
||||
+ "ok %*ph\n", (int)arg, data);
|
||||
+ else
|
||||
+ ctx->resp_len = scnprintf(ctx->resp, sizeof(ctx->resp), "ok\n");
|
||||
+
|
||||
+ *ppos = 0;
|
||||
+
|
||||
+ return count;
|
||||
+}
|
||||
+
|
||||
+static ssize_t ice_sff_i2c_read(struct file *filp, char __user *ubuf,
|
||||
+ size_t count, loff_t *ppos)
|
||||
+{
|
||||
+ struct ice_sff_i2c_ctx *ctx = filp->private_data;
|
||||
+
|
||||
+ return simple_read_from_buffer(ubuf, count, ppos, ctx->resp,
|
||||
+ ctx->resp_len);
|
||||
+}
|
||||
+
|
||||
+static const struct file_operations ice_sff_i2c_fops = {
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .open = ice_sff_i2c_open,
|
||||
+ .release = ice_sff_i2c_release,
|
||||
+ .read = ice_sff_i2c_read,
|
||||
+ .write = ice_sff_i2c_write,
|
||||
+};
|
||||
+
|
||||
int ice_debugfs_pf_init(struct ice_pf *pf)
|
||||
{
|
||||
const char *name = pci_name(pf->pdev);
|
||||
@@ -14,6 +191,9 @@ int ice_debugfs_pf_init(struct ice_pf *pf)
|
||||
if (IS_ERR(pf->ice_debugfs_pf))
|
||||
return PTR_ERR(pf->ice_debugfs_pf);
|
||||
|
||||
+ debugfs_create_file("sff_i2c", 0600, pf->ice_debugfs_pf, pf,
|
||||
+ &ice_sff_i2c_fops);
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -28,16 +28,16 @@ What this design carries in practice:
|
||||
|
||||
## `sff_i2c` diagnostics transport (patched driver, validated)
|
||||
|
||||
Patched ixgbe (`~/work/ixgbe-sff/`) adds `ixgbe_i2c_raw_write`/`ixgbe_i2c_raw_read` (`ixgbe_phy.c`) — arbitrary START…STOP transactions from the driver's bit-bang primitives, swfw-semaphore bracketed — behind `/sys/kernel/debug/ixgbe/<pci>/sff_i2c` (`w <addr8> <bytes…>` / `r <addr8> <n>`). The product subset of the patch (this transport, the ETQF steering below, the FCoE slot guard) is versioned as `kernel/ixgbe-sff.patch` and applied by `mkimage` to the appliance kernel.
|
||||
The bench ixgbe (`kernel/ixgbe-sff/`, the full patched driver source) adds `ixgbe_i2c_raw_write`/`ixgbe_i2c_raw_read` (`ixgbe_phy.c`) — arbitrary START…STOP transactions from the driver's bit-bang primitives, swfw-semaphore bracketed — behind `/sys/kernel/debug/ixgbe/<pci>/sff_i2c` (`w <addr8> <bytes…>` / `r <addr8> <n>`). The product subset of the patch (this transport, the ETQF steering below, the FCoE slot guard) is versioned as `kernel/ixgbe-sff.patch` and applied by `mkimage` to the appliance kernel.
|
||||
|
||||
- **SDA-release fix (required)**: multi-byte reads returned only byte 0 — the master's ACK left SDA driven low and nothing released it (stock 82599 paths never clock in more than one byte, so the missing release was invisible). `raw_read` releases SDA after each ACK, mirroring stock's release block after clocking out a byte.
|
||||
- **Two-master windows closed by the compound op**: each simple `w`/`r` takes the semaphore separately, so multi-op protocol sequences have unlocked gaps where the driver's event-driven I2C (SFP identify, link setup, `ethtool -m`) can interleave — traffic that fires exactly around cable swaps, when diagnostics also run, so collisions are rare but correlated with the interesting moments. Serialization itself holds (driver SFP traffic uses the same byte primitives under the same `phy_semaphore_mask`), and AT24-style word pointers do persist across STOP — the hazard is the other master *moving* the pointer or consuming the BCM 0x56 bridge's pending read data. The `x <waddr> <raddr> <delay_us> <n> <wbytes…>` command (`ixgbe_i2c_raw_xfer`) runs write→STOP→in-kernel delay→read under a single swfw hold, closing the window by construction; the write/read primitives are split into locked/unlocked halves so `x` reuses them under one acquire.
|
||||
- **Compound op proven on the FS BCM** (`~/work/phydiag-work/compound_test.py`): PHY ID and cmd STATUS read identically through the single-hold op and the old split sequence (0x3590 / 0x5081 / 0x0004); a ~3 ms hold is shorter than the driver's own SFP-identify holds. The delay sweep corroborates the hazard the op closes: at delay 0 the bridged read returns stale data (the prior read's 0x0004), at ≥200 µs it reads correctly. The >1 ms BCM delay is validated at 3 ms; treat 0x0000 reads as retry-with-longer-delay — 0 is also the bridge's not-ready signature.
|
||||
- **Compound op proven on the FS BCM**: PHY ID and cmd STATUS read identically through the single-hold op and the old split sequence (0x3590 / 0x5081 / 0x0004); a ~3 ms hold is shorter than the driver's own SFP-identify holds. The delay sweep corroborates the hazard the op closes: at delay 0 the bridged read returns stale data (the prior read's 0x0004), at ≥200 µs it reads correctly. The >1 ms BCM delay is validated at 3 ms; treat 0x0000 reads as retry-with-longer-delay — 0 is also the bridge's not-ready signature.
|
||||
- **RollBall split transactions are field-proven** — the i40e oracle path drove RollBall entirely with single-byte, separate-STOP transactions on both the Fibergaga and the original Wiiteks; the kernel's combined form is not required by the modules.
|
||||
|
||||
## Register/timestamp bench transport (bench build only)
|
||||
|
||||
The out-of-tree bench build additionally carries `reg_ops` `read <reg>` returning the value through the file (not just dmesg) and a `ts_bench` command running the latch poll/re-arm loop in-kernel (`arm <ctl>`, `control`, `latchrate <ms>`) — needed because the BAR is unmappable from userspace under `IO_STRICT_DEVMEM`. These serve the concluded timestamp investigation and are deliberately not in the appliance patch. Poke driver: `~/work/phydiag-work/x520poke`.
|
||||
The out-of-tree bench build additionally carries `reg_ops` `read <reg>` returning the value through the file (not just dmesg) and a `ts_bench` command running the latch poll/re-arm loop in-kernel (`arm <ctl>`, `control`, `latchrate <ms>`) — needed because the BAR is unmappable from userspace under `IO_STRICT_DEVMEM`. These serve the concluded timestamp investigation and are deliberately not in the appliance patch; both live in the `kernel/ixgbe-sff/` bench driver.
|
||||
|
||||
## Timestamping facts
|
||||
|
||||
@@ -54,7 +54,7 @@ The out-of-tree bench build additionally carries `reg_ops` `read <reg>` returnin
|
||||
## RX steering facts
|
||||
|
||||
- Stock ntuple ETHER_FLOW is rejected (`ixgbe_flowspec_to_flow_type`: TCP/UDP/SCTP-v4 and IPv4 only). The patched driver maps exact-ethertype ETHER_FLOW inserts onto ETQF slots, so the tool runs unchanged; `ETHTOOL_GRXCLSRULE`/`GRXCLSRLALL` round-trip the rules so stale-rule cleanup sees and clears them.
|
||||
- **ETQF/ETQS L2 EtherType steering is proven on hardware** (`~/work/phydiag-work/etqfbench`): a dedicated ethertype→queue stage checked at L2, ahead of RSS/fdir — ETQF holds the 16-bit ethertype + FILTER_EN, ETQS holds RX_QUEUE + QUEUE_EN, and the RX descriptor reports the matched index (`RXDADV_PKTTYPE_ETQF`). 7 rules 0x88b5–0x88bb → queues 0–6 land exactly 1000 frames each; unfiltered ethertypes fall to queue 0 (non-IP frames hash to RSS 0); delete restores the fall-through.
|
||||
- **ETQF/ETQS L2 EtherType steering is proven on hardware**: a dedicated ethertype→queue stage checked at L2, ahead of RSS/fdir — ETQF holds the 16-bit ethertype + FILTER_EN, ETQS holds RX_QUEUE + QUEUE_EN, and the RX descriptor reports the matched index (`RXDADV_PKTTYPE_ETQF`). 7 rules 0x88b5–0x88bb → queues 0–6 land exactly 1000 frames each; unfiltered ethertypes fall to queue 0 (non-IP frames hash to RSS 0); delete restores the fall-through.
|
||||
- **Slot budget fits**: 8 filters; the patch reserves index 3 (1588) and refuses ethertype 0x88F7 → 7 free = exactly the 7 streams. Stock FCoE writes moved behind the FCoE-enabled guard (without that, FCoE CRC pre-claims slot 2). Restore-on-link-up (`ixgbe_etqf_filter_restore`) and clear-on-close (`ixgbe_etqf_filter_exit`) are hooked so a ring-resize reset re-applies the rules.
|
||||
- **Flow Director is a dead end for raw L2 — confirmed on hardware**: 82599 fdir classifies IPv4/IPv6 only; a flex-word rule at the ethertype offset (`FDIRCTRL_FLEX_SHIFT = 0x6`) on a raw 0x88b5 stream steers nothing (`etqfbench -mode fdirneg`) — the flex word only *narrows* an IP-typed match.
|
||||
- Unused fallbacks: MAC→VMDq pool steering (distinct dest MACs → queues, fully raw; ETQF even has a pool field); UDP encap acceptable — raw-L2 steering was never a hard NIC requirement. ETQF stays preferred (no framing change, no header overhead in the size mix).
|
||||
|
||||
Reference in New Issue
Block a user