Give each stream its own ethertype steered to its own rx queue
This commit is contained in:
@@ -16,13 +16,6 @@ import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
fanoutHash = 0
|
||||
fanoutLB = 1
|
||||
fanoutCPU = 2
|
||||
fanoutRollover = 3
|
||||
)
|
||||
|
||||
const wireOverhead = 24
|
||||
|
||||
type endpoint struct {
|
||||
@@ -44,7 +37,7 @@ type direction struct {
|
||||
short string
|
||||
tx endpoint
|
||||
rx endpoint
|
||||
spec *frameSpec
|
||||
specs []*frameSpec
|
||||
txStats []*txStats
|
||||
rxStats []*rxStats
|
||||
streams []lossWindow
|
||||
@@ -128,25 +121,6 @@ func cpuAt(cpus []int, i int) int {
|
||||
return cpus[i%len(cpus)]
|
||||
}
|
||||
|
||||
func fanoutMode(name string, nsock int) (int, error) {
|
||||
if nsock < 2 {
|
||||
return -1, nil
|
||||
}
|
||||
switch name {
|
||||
case "none":
|
||||
return -1, nil
|
||||
case "hash":
|
||||
return fanoutHash, nil
|
||||
case "lb":
|
||||
return fanoutLB, nil
|
||||
case "cpu":
|
||||
return fanoutCPU, nil
|
||||
case "rollover":
|
||||
return fanoutRollover, nil
|
||||
}
|
||||
return 0, fmt.Errorf("unknown fanout mode %q", name)
|
||||
}
|
||||
|
||||
func (d *direction) snapshot() sample {
|
||||
var s sample
|
||||
for _, t := range d.txStats {
|
||||
@@ -244,30 +218,26 @@ func buildDirection(label string, tx, rx endpoint, patIdx int, sizes []int, cfg
|
||||
short: tx.tag + "→" + rx.tag,
|
||||
tx: tx,
|
||||
rx: rx,
|
||||
spec: newFrameSpec(patIdx, rx.mac, tx.mac, sizes),
|
||||
streams: newLossWindows(cfg.txWorkers),
|
||||
streams: newLossWindows(cfg.streams),
|
||||
reports: make(chan string, 64),
|
||||
}
|
||||
d.nicTX = readNIC(tx.name)
|
||||
d.nicRX = readNIC(rx.name)
|
||||
|
||||
fm, err := fanoutMode(cfg.fanout, cfg.rxWorkers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := 0; i < cfg.streams; i++ {
|
||||
et := uint16(etherBase + i)
|
||||
d.specs = append(d.specs, newFrameSpec(patIdx, rx.mac, tx.mac, et, sizes))
|
||||
|
||||
for i := 0; i < cfg.txWorkers; i++ {
|
||||
fd, err := openTxSocket(tx.idx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s tx socket: %w", label, err)
|
||||
}
|
||||
d.txFDs = append(d.txFDs, fd)
|
||||
d.txStats = append(d.txStats, &txStats{})
|
||||
}
|
||||
for i := 0; i < cfg.rxWorkers; i++ {
|
||||
fd, err := openRxSocket(rx.idx, cfg.fanoutID, fm)
|
||||
|
||||
fd, err = openRxSocket(rx.idx, et)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s rx socket: %w", label, err)
|
||||
return nil, fmt.Errorf("%s rx socket for 0x%04x: %w", label, et, err)
|
||||
}
|
||||
d.rxFDs = append(d.rxFDs, fd)
|
||||
d.rxStats = append(d.rxStats, &rxStats{})
|
||||
@@ -280,7 +250,7 @@ func (d *direction) start(wg *sync.WaitGroup, doneTx, doneRx *atomic.Bool, cfg c
|
||||
w := &txWorker{
|
||||
fd: fd,
|
||||
stream: uint16(i),
|
||||
spec: d.spec,
|
||||
spec: d.specs[i],
|
||||
batch: cfg.batch,
|
||||
cpu: cpuAt(cfg.txCPUs, i),
|
||||
stats: d.txStats[i],
|
||||
@@ -297,7 +267,7 @@ func (d *direction) start(wg *sync.WaitGroup, doneTx, doneRx *atomic.Bool, cfg c
|
||||
fd: fd,
|
||||
batch: cfg.batch,
|
||||
cpu: cpuAt(cfg.rxCPUs, i),
|
||||
spec: d.spec,
|
||||
spec: d.specs[i],
|
||||
stats: d.rxStats[i],
|
||||
streams: d.streams,
|
||||
reports: d.reports,
|
||||
@@ -321,13 +291,10 @@ func (d *direction) close() {
|
||||
}
|
||||
|
||||
type config struct {
|
||||
txWorkers int
|
||||
rxWorkers int
|
||||
batch int
|
||||
fanout string
|
||||
fanoutID int
|
||||
txCPUs []int
|
||||
rxCPUs []int
|
||||
streams int
|
||||
batch int
|
||||
txCPUs []int
|
||||
rxCPUs []int
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -336,18 +303,16 @@ func main() {
|
||||
bName = flag.String("b", "", "second interface")
|
||||
sizesArg = flag.String("sizes", "1024,1280,1514", "frame sizes in bytes, excluding FCS, cycled per packet; below ~900 the host cannot keep up and loss stops meaning anything")
|
||||
patArg = flag.String("pattern", "prbs", "payload pattern")
|
||||
txN = flag.Int("tx", 1, "tx workers per direction; one saturates 10G at these sizes")
|
||||
rxN = flag.Int("rx", 2, "rx workers per direction; more is slower, RSS cannot spread a raw ethertype across queues")
|
||||
streams = flag.Int("streams", 4, "independent streams per direction; each gets its own ethertype, steered by a flow rule to its own rx queue")
|
||||
batch = flag.Int("batch", 64, "frames per sendmmsg/recvmmsg call")
|
||||
fanout = flag.String("fanout", "lb", "rx fanout mode: none, hash, lb, cpu, rollover")
|
||||
duplex = flag.Bool("duplex", true, "run both directions simultaneously")
|
||||
txCPUs = flag.String("txcpus", "", "comma-separated CPUs to pin tx workers to")
|
||||
rxCPUs = flag.String("rxcpus", "", "comma-separated CPUs to pin rx workers to")
|
||||
)
|
||||
flag.Parse()
|
||||
|
||||
if err := run(*aName, *bName, *sizesArg, *patArg, *fanout, *txCPUs, *rxCPUs,
|
||||
*txN, *rxN, *batch, *duplex); err != nil {
|
||||
if err := run(*aName, *bName, *sizesArg, *patArg, *txCPUs, *rxCPUs,
|
||||
*streams, *batch, *duplex); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "error:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -355,8 +320,8 @@ func main() {
|
||||
|
||||
const reportInterval = 500 * time.Millisecond
|
||||
|
||||
func run(aName, bName, sizesArg, patArg, fanout, txCPUsArg, rxCPUsArg string,
|
||||
txN, rxN, batch int, duplex bool) error {
|
||||
func run(aName, bName, sizesArg, patArg, txCPUsArg, rxCPUsArg string,
|
||||
nStreams, batch int, duplex bool) error {
|
||||
|
||||
if aName == "" || bName == "" {
|
||||
return fmt.Errorf("both -a and -b are required")
|
||||
@@ -396,9 +361,17 @@ func run(aName, bName, sizesArg, patArg, fanout, txCPUsArg, rxCPUsArg string,
|
||||
a.tag, b.tag = "A", "B"
|
||||
ifnames := []string{a.name, b.name}
|
||||
|
||||
if nStreams < 1 {
|
||||
return fmt.Errorf("need at least one stream")
|
||||
}
|
||||
ethertypes := make([]uint16, nStreams)
|
||||
for i := range ethertypes {
|
||||
ethertypes[i] = uint16(etherBase + i)
|
||||
}
|
||||
|
||||
var fatal []string
|
||||
var tuneRows [][]string
|
||||
for _, r := range configureSystem(ifnames) {
|
||||
for _, r := range configureSystem(ifnames, ethertypes) {
|
||||
tuneRows = append(tuneRows, []string{r.item, r.status(), r.detail()})
|
||||
if r.fatal {
|
||||
fatal = append(fatal, r.item)
|
||||
@@ -412,26 +385,20 @@ func run(aName, bName, sizesArg, patArg, fanout, txCPUsArg, rxCPUsArg string,
|
||||
}
|
||||
|
||||
cfg := config{
|
||||
txWorkers: txN,
|
||||
rxWorkers: rxN,
|
||||
batch: batch,
|
||||
fanout: fanout,
|
||||
txCPUs: txCPUs,
|
||||
rxCPUs: rxCPUs,
|
||||
streams: nStreams,
|
||||
batch: batch,
|
||||
txCPUs: txCPUs,
|
||||
rxCPUs: rxCPUs,
|
||||
}
|
||||
|
||||
var dirs []*direction
|
||||
cfgA := cfg
|
||||
cfgA.fanoutID = 0x4341
|
||||
d0, err := buildDirection(a.name+"->"+b.name, a, b, patIdx, sizes, cfgA)
|
||||
d0, err := buildDirection(a.name+"->"+b.name, a, b, patIdx, sizes, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dirs = append(dirs, d0)
|
||||
if duplex {
|
||||
cfgB := cfg
|
||||
cfgB.fanoutID = 0x4342
|
||||
d1, err := buildDirection(b.name+"->"+a.name, b, a, patIdx, sizes, cfgB)
|
||||
d1, err := buildDirection(b.name+"->"+a.name, b, a, patIdx, sizes, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -467,11 +434,10 @@ func run(aName, bName, sizesArg, patArg, fanout, txCPUsArg, rxCPUsArg string,
|
||||
[]bool{false, false}, [][]string{
|
||||
{"pattern", patterns[patIdx].name},
|
||||
{"frame sizes", strings.Join(sizeStrs, " ")},
|
||||
{"ethertype", fmt.Sprintf("0x%04x", etherType)},
|
||||
{"workers", fmt.Sprintf("%d tx, %d rx per direction", txN, rxN)},
|
||||
{"streams", fmt.Sprintf("%d per direction, ethertypes 0x%04x-0x%04x, one rx queue each",
|
||||
nStreams, ethertypes[0], ethertypes[len(ethertypes)-1])},
|
||||
{"batch", fmt.Sprintf("%d frames per syscall", batch)},
|
||||
{"payload verify", "crc32c on every frame"},
|
||||
{"rx fanout", fanout},
|
||||
{"duplex", fmt.Sprintf("%v", duplex)},
|
||||
{"socket buffers", fmt.Sprintf("sndbuf %s, rcvbuf %s (granted)",
|
||||
humanBytes(uint64(sockBufSize(dirs[0].txFDs[0], unix.SO_SNDBUF))),
|
||||
|
||||
Reference in New Issue
Block a user