Always color output and fold settled values into constants
This commit is contained in:
@@ -273,7 +273,7 @@ func buildDirection(label string, tx, rx endpoint, patIdx int, sizes []int, cfg
|
||||
}
|
||||
|
||||
for i := 0; i < cfg.txWorkers; i++ {
|
||||
fd, err := openTxSocket(tx.idx, cfg.sndbuf)
|
||||
fd, err := openTxSocket(tx.idx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s tx socket: %w", label, err)
|
||||
}
|
||||
@@ -281,7 +281,7 @@ func buildDirection(label string, tx, rx endpoint, patIdx int, sizes []int, cfg
|
||||
d.txStats = append(d.txStats, &txStats{})
|
||||
}
|
||||
for i := 0; i < cfg.rxWorkers; i++ {
|
||||
fd, err := openRxSocket(rx.idx, cfg.rcvbuf, cfg.fanoutID, fm)
|
||||
fd, err := openRxSocket(rx.idx, cfg.fanoutID, fm)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s rx socket: %w", label, err)
|
||||
}
|
||||
@@ -313,7 +313,6 @@ func (d *direction) start(wg *sync.WaitGroup, doneTx, doneRx *atomic.Bool, cfg c
|
||||
fd: fd,
|
||||
batch: cfg.batch,
|
||||
cpu: cpuAt(cfg.rxCPUs, i),
|
||||
verify: cfg.verify,
|
||||
spec: d.spec,
|
||||
stats: d.rxStats[i],
|
||||
streams: d.streams,
|
||||
@@ -341,9 +340,6 @@ type config struct {
|
||||
txWorkers int
|
||||
rxWorkers int
|
||||
batch int
|
||||
verify bool
|
||||
sndbuf int
|
||||
rcvbuf int
|
||||
fanout string
|
||||
fanoutID int
|
||||
txCPUs []int
|
||||
@@ -360,46 +356,25 @@ func main() {
|
||||
txN = flag.Int("tx", 4, "tx workers per direction")
|
||||
rxN = flag.Int("rx", 4, "rx workers per direction")
|
||||
batch = flag.Int("batch", 64, "frames per sendmmsg/recvmmsg call")
|
||||
verify = flag.Bool("verify", true, "verify payload CRC32C on receive")
|
||||
interval = flag.Duration("interval", time.Second, "report interval")
|
||||
drain = flag.Duration("drain", 500*time.Millisecond, "keep receiving this long after tx stops, so in-flight frames are not counted as lost")
|
||||
fanout = flag.String("fanout", "lb", "rx fanout mode: none, hash, lb, cpu, rollover")
|
||||
duplex = flag.Bool("duplex", true, "run both directions simultaneously")
|
||||
sndbuf = flag.Int("sndbuf", 8<<20, "SO_SNDBUFFORCE per tx socket")
|
||||
rcvbuf = flag.Int("rcvbuf", 64<<20, "SO_RCVBUFFORCE per rx socket")
|
||||
txCPUs = flag.String("txcpus", "", "comma-separated CPUs to pin tx workers to")
|
||||
rxCPUs = flag.String("rxcpus", "", "comma-separated CPUs to pin rx workers to")
|
||||
governor = flag.String("governor", "performance", "required cpufreq governor")
|
||||
coalesce = flag.Uint("coalesce-usecs", 25, "required fixed rx/tx coalesce usecs, with adaptive coalescing off")
|
||||
rxRing = flag.Uint("rx-ring", 8160, "required rx ring size, clamped to hardware maximum")
|
||||
txRing = flag.Uint("tx-ring", 4096, "required tx ring size, clamped to hardware maximum")
|
||||
color = flag.String("color", "auto", "colored output: auto, always, never")
|
||||
)
|
||||
flag.Parse()
|
||||
|
||||
if err := initColor(*color); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "error:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
tcfg := systemConfig{
|
||||
governor: *governor,
|
||||
rxUsecs: uint32(*coalesce),
|
||||
txUsecs: uint32(*coalesce),
|
||||
rxRing: uint32(*rxRing),
|
||||
txRing: uint32(*txRing),
|
||||
}
|
||||
|
||||
if err := run(*aName, *bName, *sizesArg, *patArg, *fanout, *txCPUs, *rxCPUs,
|
||||
*duration, *interval, *drain, *txN, *rxN, *batch, *sndbuf, *rcvbuf, *verify, *duplex, tcfg); err != nil {
|
||||
*duration, *interval, *txN, *rxN, *batch, *duplex); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "error:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
const drainTime = 500 * time.Millisecond
|
||||
|
||||
func run(aName, bName, sizesArg, patArg, fanout, txCPUsArg, rxCPUsArg string,
|
||||
duration, interval, drain time.Duration, txN, rxN, batch, sndbuf, rcvbuf int,
|
||||
verify, duplex bool, tcfg systemConfig) error {
|
||||
duration, interval time.Duration, txN, rxN, batch int, duplex bool) error {
|
||||
|
||||
if aName == "" || bName == "" {
|
||||
return fmt.Errorf("both -a and -b are required")
|
||||
@@ -441,7 +416,7 @@ func run(aName, bName, sizesArg, patArg, fanout, txCPUsArg, rxCPUsArg string,
|
||||
|
||||
var fatal []string
|
||||
var tuneRows [][]string
|
||||
for _, r := range configureSystem(tcfg, ifnames) {
|
||||
for _, r := range configureSystem(ifnames) {
|
||||
tuneRows = append(tuneRows, []string{r.item, r.status(), r.detail()})
|
||||
if r.fatal {
|
||||
fatal = append(fatal, r.item)
|
||||
@@ -458,9 +433,6 @@ func run(aName, bName, sizesArg, patArg, fanout, txCPUsArg, rxCPUsArg string,
|
||||
txWorkers: txN,
|
||||
rxWorkers: rxN,
|
||||
batch: batch,
|
||||
verify: verify,
|
||||
sndbuf: sndbuf,
|
||||
rcvbuf: rcvbuf,
|
||||
fanout: fanout,
|
||||
txCPUs: txCPUs,
|
||||
rxCPUs: rxCPUs,
|
||||
@@ -516,7 +488,7 @@ func run(aName, bName, sizesArg, patArg, fanout, txCPUsArg, rxCPUsArg string,
|
||||
{"ethertype", fmt.Sprintf("0x%04x", etherType)},
|
||||
{"workers", fmt.Sprintf("%d tx, %d rx per direction", txN, rxN)},
|
||||
{"batch", fmt.Sprintf("%d frames per syscall", batch)},
|
||||
{"payload verify", fmt.Sprintf("%v", verify)},
|
||||
{"payload verify", "crc32c on every frame"},
|
||||
{"rx fanout", fanout},
|
||||
{"duplex", fmt.Sprintf("%v", duplex)},
|
||||
{"socket buffers", fmt.Sprintf("sndbuf %s, rcvbuf %s (granted)",
|
||||
@@ -589,7 +561,7 @@ loop:
|
||||
|
||||
doneTx.Store(true)
|
||||
elapsed := time.Since(start).Seconds()
|
||||
time.Sleep(drain)
|
||||
time.Sleep(drainTime)
|
||||
doneRx.Store(true)
|
||||
wg.Wait()
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -17,29 +16,8 @@ const (
|
||||
cGrey = "\x1b[90m"
|
||||
)
|
||||
|
||||
var useColor bool
|
||||
|
||||
func initColor(mode string) error {
|
||||
switch mode {
|
||||
case "always":
|
||||
useColor = true
|
||||
case "never":
|
||||
useColor = false
|
||||
case "auto":
|
||||
if os.Getenv("NO_COLOR") != "" || os.Getenv("TERM") == "dumb" {
|
||||
useColor = false
|
||||
return nil
|
||||
}
|
||||
st, err := os.Stdout.Stat()
|
||||
useColor = err == nil && st.Mode()&os.ModeCharDevice != 0
|
||||
default:
|
||||
return fmt.Errorf("unknown color mode %q (want auto, always, never)", mode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func paint(s, code string) string {
|
||||
if !useColor || s == "" {
|
||||
if s == "" {
|
||||
return s
|
||||
}
|
||||
return code + s + cReset
|
||||
|
||||
@@ -28,7 +28,6 @@ type rxWorker struct {
|
||||
fd int
|
||||
batch int
|
||||
cpu int
|
||||
verify bool
|
||||
spec *frameSpec
|
||||
stats *rxStats
|
||||
streams []streamState
|
||||
@@ -79,9 +78,6 @@ func (w *rxWorker) run(done *atomic.Bool) {
|
||||
}
|
||||
}
|
||||
|
||||
if !w.verify {
|
||||
continue
|
||||
}
|
||||
if p.payLen > len(w.spec.ref) {
|
||||
w.stats.badLen.Add(1)
|
||||
continue
|
||||
|
||||
@@ -31,7 +31,12 @@ func sockBufSize(fd, opt int) int {
|
||||
return v
|
||||
}
|
||||
|
||||
func openTxSocket(ifindex, sndbuf int) (int, error) {
|
||||
const (
|
||||
sndbufBytes = 8 << 20
|
||||
rcvbufBytes = 64 << 20
|
||||
)
|
||||
|
||||
func openTxSocket(ifindex int) (int, error) {
|
||||
fd, err := unix.Socket(unix.AF_PACKET, unix.SOCK_RAW, 0)
|
||||
if err != nil {
|
||||
return -1, fmt.Errorf("socket: %w", err)
|
||||
@@ -44,20 +49,20 @@ func openTxSocket(ifindex, sndbuf int) (int, error) {
|
||||
unix.Close(fd)
|
||||
return -1, fmt.Errorf("qdisc bypass: %w", err)
|
||||
}
|
||||
if err := setBufForce(fd, unix.SO_SNDBUFFORCE, unix.SO_SNDBUF, sndbuf); err != nil {
|
||||
if err := setBufForce(fd, unix.SO_SNDBUFFORCE, unix.SO_SNDBUF, sndbufBytes); err != nil {
|
||||
unix.Close(fd)
|
||||
return -1, fmt.Errorf("sndbuf: %w", err)
|
||||
}
|
||||
return fd, nil
|
||||
}
|
||||
|
||||
func openRxSocket(ifindex, rcvbuf, fanoutID, fanoutMode int) (int, error) {
|
||||
func openRxSocket(ifindex, fanoutID, fanoutMode int) (int, error) {
|
||||
proto := int(htons(etherType))
|
||||
fd, err := unix.Socket(unix.AF_PACKET, unix.SOCK_RAW, proto)
|
||||
if err != nil {
|
||||
return -1, fmt.Errorf("socket: %w", err)
|
||||
}
|
||||
if err := setBufForce(fd, unix.SO_RCVBUFFORCE, unix.SO_RCVBUF, rcvbuf); err != nil {
|
||||
if err := setBufForce(fd, unix.SO_RCVBUFFORCE, unix.SO_RCVBUF, rcvbufBytes); err != nil {
|
||||
unix.Close(fd)
|
||||
return -1, fmt.Errorf("rcvbuf: %w", err)
|
||||
}
|
||||
|
||||
@@ -62,13 +62,12 @@ type ethtoolCoalesce struct {
|
||||
rateSampleInterval uint32
|
||||
}
|
||||
|
||||
type systemConfig struct {
|
||||
governor string
|
||||
rxUsecs uint32
|
||||
txUsecs uint32
|
||||
rxRing uint32
|
||||
txRing uint32
|
||||
}
|
||||
const (
|
||||
wantGovernor = "performance"
|
||||
wantCoalesceUsecs = 25
|
||||
wantRxRing = 8160
|
||||
wantTxRing = 4096
|
||||
)
|
||||
|
||||
type checkResult struct {
|
||||
item string
|
||||
@@ -311,15 +310,15 @@ func withIoctlSocket(fn func(fd int) []checkResult) []checkResult {
|
||||
return fn(fd)
|
||||
}
|
||||
|
||||
func configureSystem(cfg systemConfig, ifnames []string) []checkResult {
|
||||
func configureSystem(ifnames []string) []checkResult {
|
||||
return withIoctlSocket(func(fd int) []checkResult {
|
||||
out := []checkResult{checkGovernor(cfg.governor)}
|
||||
out := []checkResult{checkGovernor(wantGovernor)}
|
||||
for _, ifname := range ifnames {
|
||||
out = append(out, checkLinkUp(fd, ifname))
|
||||
out = append(out, checkCoalesce(fd, ifname, cfg.rxUsecs, cfg.txUsecs))
|
||||
out = append(out, checkCoalesce(fd, ifname, wantCoalesceUsecs, wantCoalesceUsecs))
|
||||
|
||||
carrierWait := 3 * time.Second
|
||||
r, reset := checkRings(fd, ifname, cfg.rxRing, cfg.txRing)
|
||||
r, reset := checkRings(fd, ifname, wantRxRing, wantTxRing)
|
||||
out = append(out, r)
|
||||
if reset {
|
||||
carrierWait = 10 * time.Second
|
||||
|
||||
Reference in New Issue
Block a user