Capturing the XGS 3300 while it runs its own firmware¶
Read this before switching the XGS 3300 back to its own firmware. Switching is expensive, so this is written so that one trip is enough. There is a script that does the whole thing:
tools/sfos-capture.sh
It only reads. It never passes C to usfp_table_print.sh, which clears a counter array; it does not
run xgs-ftw, xgs-led*, xgs-eeprom -w, xgs-cpld <reg> <value> or fw_setenv; it loads and
unloads nothing and brings no interface up or down. The single exception is section 9, a traffic
generator that adds an address and a neighbour entry to PortF1 and removes both afterwards - it is
skipped unless RUN_TRAFFIC=1 is set, and it is the reason section 2a is worth anything.
sh sfos-capture.sh 2>&1 | tee /tmp/sfos-capture.txt
RUN_TRAFFIC=1 sh sfos-capture.sh 2>&1 | tee /tmp/sfos-capture-load.txt # second pass, with load
Get the files off with xgs-scp-from.sh, or let the box ftpput them out - SFOS key auth is
structurally impossible, so do not try to scp in.
The one thing that matters most¶
A packet capture on oct0 while a front port receives traffic.
tcpdump -i oct0 -s 0 -c 20 -xx -nn
tcpdump -i oct0 -s 0 -c 20 -w /tmp/oct0.pcap
Every byte of the host-to-coprocessor direction was worked out from binaries: the two-byte port tag,
the sixty-four metadata bytes, meta[0] = 1, the EtherType 0xEFEF on tag 254, the sixty-six byte
private header.
The return direction has since been observed, once. A frame was read out of a host receive
buffer byte for byte, and the prefix in front of it is 82 bytes, not the 66 this page assumed:
eight bytes of SDP info carrying the length, eight more holding 0x8003000000000000, then the
two-byte port tag and the sixty-four metadata bytes. The rate this page went on to worry about
was answered by the capture itself rather than by a comparison: the doorbell counts sixteen per
buffer, so the grant had been a sixteenth of the ring. (The grant was too small, and that is all
that was right in this: the block spends one for a buffer and stops under a doorbell of 1,024 -
a ring that fills.)
Twenty frames with -xx settles it. Run the traffic generator in another shell at the same time, or
the capture has nothing in it.
If only one command from this whole document can be run, run that one.
Why the trip is needed at all¶
This page's reason has since been met, and the trip was worth making: what it brought back is what answered the question. It is kept as written, because the reasoning that decided what to capture is more useful than a summary of the result.
The rate limit described below was real. Its cause was not a missing mechanism but a unit: the
output doorbell counts sixteen per buffer - the size of one scatter-list entry - so granting one
credit per entry handed the block a sixteenth of the ring. Granted properly, 300 paced frames give
rx_done +308. (Sixteen times the ring was a grant over the coprocessor's watermark, not the block's unit:
a ring that fills.) The disassembly this trip produced is what made the unit findable, and the same
disassembly later gave the switch's DSA tag parser, which is what opened the panel ports.
What the page said at the time:
The datapath now works in every direction, and what is left is the rate. Frames leave PortF1 and arrive on PortF2, counted on the fast path's own per-port counters in both directions; the frame matches a LIF; the fast path raises the counter for handing it to the host; and the block writes it into a host buffer, where one was read out byte for byte. Each armed ring then delivers one packet and never another - not after it is acknowledged, not after its credits are returned, not after its latched status is cleared.
The "all 256 buffers are still the poison byte" reading on this page belongs to the era before the LIFs were installed, and it is what dates the change: buffer 0 of a ring is written today.
Thirteen things have been tried against the last hop and all thirteen are negative; they are listed in the driver's own family page and in issue #115, so that none of them is retried. Each was grounded in the vendor's own binary and each produced nothing.
What is left cannot be settled from the binaries, because the binaries describe intent and the question is about behaviour. That is what this capture is for.
The list, in order of what it settles¶
1. The last hop¶
| what | why it earns the trip |
|---|---|
tcpdump -i oct0 -xx and a .pcap, under load |
above. The only unobserved direction |
tcpdump -i Port1 -xx under the same load |
shows what mv_pport strips, so the two can be differenced |
every file under /sys/module/{octeon_drv,slipf,octnic,mv_pport,usfp_firewall}/parameters/ |
pci_port is what the fast path reads to decide its host port count; the rest have never been listed |
/proc/interrupts, before and after load |
whether the SDP path raises an interrupt at all, and how many |
ethtool -S oct0, ethtool -g oct0, ethtool -l oct0 |
the host NIC's own receive counters and ring geometry |
ps and the usfp command line from /proc/*/cmdline |
confirms -t 8, nr_port, pci_port as actually run on this board |
anything under /sys/kernel/debug/octeon* or /proc/octeon* |
the vendor driver may expose a register dump; a working OQ's RSIZE, DBELL, CONTROL, CNTS is worth a great deal |
2. The counters, which every previous capture truncated¶
Every worker_sys_cnt capture in the inventory was cut with head. The whole
FPCNTR_FROM_WIRE_DROP_LIF_* family - the five counters that gate the wire-to-host direction - has
never been on screen. And the D flag, which prints the vendor's own description of every counter,
has never been used once.
usfp_table_print.sh worker_sys_cnt D # descriptions. Never run.
usfp_table_print.sh worker_sys_cnt I # including zeros, so the shape shows
usfp_table_print.sh worker_port_cnt D
usfp_table_print.sh worker_port_cnt I # confirms the 256 stride worked out from the binary
usfp_table_print.sh worker_dbg_cnt D
usfp_table_print.sh worker_dbg_cnt I
usfp_table_print.sh worker_dragonfly_cnt I
usfp_table_print.sh platform_info
Never pass C. It clears the array.
3. The state tables, complete¶
usfp_table_print.sh lif I
usfp_table_print.sh nhop I
usfp_table_print.sh conn I
usfp_table_print.sh mflow I
usfp_table_print.sh luid I
usfp_table_print.sh sa I
The LIF dump already answered what a LIF looks like; nhop, conn and mflow have only ever been
seen empty or with one entry, and luid and sa never at all.
4. The mappings nobody dumped¶
xgs-ports -b # MAC with label - the only one ever run
xgs-ports -f # MAC with LIFPORT - never run, and it is the port-to-interface map
xgs-ports -d # devnames with labels
xgs-ports -g # grand ridge index with lifport and PCIe id
plus every file under /sys/kernel/usfp_firewall/control/, /sys/kernel/nwa_ports_info/ and
/sys/kernel/nwa_pports/. The first of those carries set_netdev_lif_port, which the boot script
feeds from xgs-ports -f - that is the host telling the fast path which netdev is which interface,
and it has never been seen.
5. The peripheral channel¶
The CPLD's bus is named in the key store as spi:0:1:3, in the coprocessor's namespace, and it
carries the sensors, the SFP cage pins and the fail-to-wire relay. Five roadmap items are one channel.
- the CPLD swept to
0x7f, not0x5f. Registers0x30,0x31and0x38-0x3cwere found by a second sweep nobody read; nothing has looked above0x5fat all. - is there an
spidevice, and on which side -/dev/spidev*,/sys/bus/spi/devices/,/sys/class/spi_master/, on the host and on the coprocessor. This settles whether the CPLD is reachable from a non-vendor host at all. xgs-1us-sensors -a,xgs-dt-sensors,xgs-phy-temperature, andxgs-nctwith no arguments so its refusal is on the record.xgs-sff-eventandxgs-sff-mq, which exist only on the coprocessor and have never been run. Ideally across a module reseat, which is what would settle whatcpld:0x38's bits mean: read, pull a module, read, replace, read.- the switch's registers beyond 0 and 3 -
21,22,26,27per port, which carry the per-port control and status. - the 88X5113 at
mdio45:0:7, slices 0 and 2, more registers than the eight already read.
6. Structure that earlier captures cut off¶
lspci -vvv -s 01:00.0in full - the SR-IOV capability body was cut byhead -45, so First VF Offset, VF Stride, TotalVFs and NumVFs are all unrecorded./proc/iomemin full on both sides - the coprocessor's was cut athead -24, and the eleven vfio-bound functions it showed were a floor, not a total.lsmodanddmesgin full on both sides.
What to do with the result¶
Keep it beside the existing capture as a sibling, not merged into it: the two are different runs, and the value of the first is that it is what the appliance looked like on one particular day. The captures themselves are held outside this repository, because they identify one physical unit.
The rule that goes with them applies to the new one from the moment it lands: read it before every step, and again when stuck. The existing capture has three times answered a question nobody asked it, and twice shown that a page in this repository was wrong.
Two habits that cost real time, so the new capture should avoid them¶
headtruncated something valuable in at least four files. Print tables whole and let the file be large.- A command echo is not output. Several captures record a tool's usage message because the invocation was wrong, and the usage message was then read as data. Check each section has real output before leaving the box.
The discussion of this page is held in the project's GitHub Discussions. Showing it here loads giscus from giscus.app and GitHub, and writing in it needs a GitHub account - what that means.