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++ {
|
for i := 0; i < cfg.txWorkers; i++ {
|
||||||
fd, err := openTxSocket(tx.idx, cfg.sndbuf)
|
fd, err := openTxSocket(tx.idx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s tx socket: %w", label, err)
|
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{})
|
d.txStats = append(d.txStats, &txStats{})
|
||||||
}
|
}
|
||||||
for i := 0; i < cfg.rxWorkers; i++ {
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s rx socket: %w", label, err)
|
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,
|
fd: fd,
|
||||||
batch: cfg.batch,
|
batch: cfg.batch,
|
||||||
cpu: cpuAt(cfg.rxCPUs, i),
|
cpu: cpuAt(cfg.rxCPUs, i),
|
||||||
verify: cfg.verify,
|
|
||||||
spec: d.spec,
|
spec: d.spec,
|
||||||
stats: d.rxStats[i],
|
stats: d.rxStats[i],
|
||||||
streams: d.streams,
|
streams: d.streams,
|
||||||
@@ -341,9 +340,6 @@ type config struct {
|
|||||||
txWorkers int
|
txWorkers int
|
||||||
rxWorkers int
|
rxWorkers int
|
||||||
batch int
|
batch int
|
||||||
verify bool
|
|
||||||
sndbuf int
|
|
||||||
rcvbuf int
|
|
||||||
fanout string
|
fanout string
|
||||||
fanoutID int
|
fanoutID int
|
||||||
txCPUs []int
|
txCPUs []int
|
||||||
@@ -360,46 +356,25 @@ func main() {
|
|||||||
txN = flag.Int("tx", 4, "tx workers per direction")
|
txN = flag.Int("tx", 4, "tx workers per direction")
|
||||||
rxN = flag.Int("rx", 4, "rx workers per direction")
|
rxN = flag.Int("rx", 4, "rx workers per direction")
|
||||||
batch = flag.Int("batch", 64, "frames per sendmmsg/recvmmsg call")
|
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")
|
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")
|
fanout = flag.String("fanout", "lb", "rx fanout mode: none, hash, lb, cpu, rollover")
|
||||||
duplex = flag.Bool("duplex", true, "run both directions simultaneously")
|
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")
|
txCPUs = flag.String("txcpus", "", "comma-separated CPUs to pin tx workers to")
|
||||||
rxCPUs = flag.String("rxcpus", "", "comma-separated CPUs to pin rx 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()
|
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,
|
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)
|
fmt.Fprintln(os.Stderr, "error:", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const drainTime = 500 * time.Millisecond
|
||||||
|
|
||||||
func run(aName, bName, sizesArg, patArg, fanout, txCPUsArg, rxCPUsArg string,
|
func run(aName, bName, sizesArg, patArg, fanout, txCPUsArg, rxCPUsArg string,
|
||||||
duration, interval, drain time.Duration, txN, rxN, batch, sndbuf, rcvbuf int,
|
duration, interval time.Duration, txN, rxN, batch int, duplex bool) error {
|
||||||
verify, duplex bool, tcfg systemConfig) error {
|
|
||||||
|
|
||||||
if aName == "" || bName == "" {
|
if aName == "" || bName == "" {
|
||||||
return fmt.Errorf("both -a and -b are required")
|
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 fatal []string
|
||||||
var tuneRows [][]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()})
|
tuneRows = append(tuneRows, []string{r.item, r.status(), r.detail()})
|
||||||
if r.fatal {
|
if r.fatal {
|
||||||
fatal = append(fatal, r.item)
|
fatal = append(fatal, r.item)
|
||||||
@@ -458,9 +433,6 @@ func run(aName, bName, sizesArg, patArg, fanout, txCPUsArg, rxCPUsArg string,
|
|||||||
txWorkers: txN,
|
txWorkers: txN,
|
||||||
rxWorkers: rxN,
|
rxWorkers: rxN,
|
||||||
batch: batch,
|
batch: batch,
|
||||||
verify: verify,
|
|
||||||
sndbuf: sndbuf,
|
|
||||||
rcvbuf: rcvbuf,
|
|
||||||
fanout: fanout,
|
fanout: fanout,
|
||||||
txCPUs: txCPUs,
|
txCPUs: txCPUs,
|
||||||
rxCPUs: rxCPUs,
|
rxCPUs: rxCPUs,
|
||||||
@@ -516,7 +488,7 @@ func run(aName, bName, sizesArg, patArg, fanout, txCPUsArg, rxCPUsArg string,
|
|||||||
{"ethertype", fmt.Sprintf("0x%04x", etherType)},
|
{"ethertype", fmt.Sprintf("0x%04x", etherType)},
|
||||||
{"workers", fmt.Sprintf("%d tx, %d rx per direction", txN, rxN)},
|
{"workers", fmt.Sprintf("%d tx, %d rx per direction", txN, rxN)},
|
||||||
{"batch", fmt.Sprintf("%d frames per syscall", batch)},
|
{"batch", fmt.Sprintf("%d frames per syscall", batch)},
|
||||||
{"payload verify", fmt.Sprintf("%v", verify)},
|
{"payload verify", "crc32c on every frame"},
|
||||||
{"rx fanout", fanout},
|
{"rx fanout", fanout},
|
||||||
{"duplex", fmt.Sprintf("%v", duplex)},
|
{"duplex", fmt.Sprintf("%v", duplex)},
|
||||||
{"socket buffers", fmt.Sprintf("sndbuf %s, rcvbuf %s (granted)",
|
{"socket buffers", fmt.Sprintf("sndbuf %s, rcvbuf %s (granted)",
|
||||||
@@ -589,7 +561,7 @@ loop:
|
|||||||
|
|
||||||
doneTx.Store(true)
|
doneTx.Store(true)
|
||||||
elapsed := time.Since(start).Seconds()
|
elapsed := time.Since(start).Seconds()
|
||||||
time.Sleep(drain)
|
time.Sleep(drainTime)
|
||||||
doneRx.Store(true)
|
doneRx.Store(true)
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,29 +16,8 @@ const (
|
|||||||
cGrey = "\x1b[90m"
|
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 {
|
func paint(s, code string) string {
|
||||||
if !useColor || s == "" {
|
if s == "" {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
return code + s + cReset
|
return code + s + cReset
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ type rxWorker struct {
|
|||||||
fd int
|
fd int
|
||||||
batch int
|
batch int
|
||||||
cpu int
|
cpu int
|
||||||
verify bool
|
|
||||||
spec *frameSpec
|
spec *frameSpec
|
||||||
stats *rxStats
|
stats *rxStats
|
||||||
streams []streamState
|
streams []streamState
|
||||||
@@ -79,9 +78,6 @@ func (w *rxWorker) run(done *atomic.Bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !w.verify {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if p.payLen > len(w.spec.ref) {
|
if p.payLen > len(w.spec.ref) {
|
||||||
w.stats.badLen.Add(1)
|
w.stats.badLen.Add(1)
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -31,7 +31,12 @@ func sockBufSize(fd, opt int) int {
|
|||||||
return v
|
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)
|
fd, err := unix.Socket(unix.AF_PACKET, unix.SOCK_RAW, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, fmt.Errorf("socket: %w", err)
|
return -1, fmt.Errorf("socket: %w", err)
|
||||||
@@ -44,20 +49,20 @@ func openTxSocket(ifindex, sndbuf int) (int, error) {
|
|||||||
unix.Close(fd)
|
unix.Close(fd)
|
||||||
return -1, fmt.Errorf("qdisc bypass: %w", err)
|
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)
|
unix.Close(fd)
|
||||||
return -1, fmt.Errorf("sndbuf: %w", err)
|
return -1, fmt.Errorf("sndbuf: %w", err)
|
||||||
}
|
}
|
||||||
return fd, nil
|
return fd, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func openRxSocket(ifindex, rcvbuf, fanoutID, fanoutMode int) (int, error) {
|
func openRxSocket(ifindex, fanoutID, fanoutMode int) (int, error) {
|
||||||
proto := int(htons(etherType))
|
proto := int(htons(etherType))
|
||||||
fd, err := unix.Socket(unix.AF_PACKET, unix.SOCK_RAW, proto)
|
fd, err := unix.Socket(unix.AF_PACKET, unix.SOCK_RAW, proto)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, fmt.Errorf("socket: %w", err)
|
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)
|
unix.Close(fd)
|
||||||
return -1, fmt.Errorf("rcvbuf: %w", err)
|
return -1, fmt.Errorf("rcvbuf: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,13 +62,12 @@ type ethtoolCoalesce struct {
|
|||||||
rateSampleInterval uint32
|
rateSampleInterval uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
type systemConfig struct {
|
const (
|
||||||
governor string
|
wantGovernor = "performance"
|
||||||
rxUsecs uint32
|
wantCoalesceUsecs = 25
|
||||||
txUsecs uint32
|
wantRxRing = 8160
|
||||||
rxRing uint32
|
wantTxRing = 4096
|
||||||
txRing uint32
|
)
|
||||||
}
|
|
||||||
|
|
||||||
type checkResult struct {
|
type checkResult struct {
|
||||||
item string
|
item string
|
||||||
@@ -311,15 +310,15 @@ func withIoctlSocket(fn func(fd int) []checkResult) []checkResult {
|
|||||||
return fn(fd)
|
return fn(fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func configureSystem(cfg systemConfig, ifnames []string) []checkResult {
|
func configureSystem(ifnames []string) []checkResult {
|
||||||
return withIoctlSocket(func(fd int) []checkResult {
|
return withIoctlSocket(func(fd int) []checkResult {
|
||||||
out := []checkResult{checkGovernor(cfg.governor)}
|
out := []checkResult{checkGovernor(wantGovernor)}
|
||||||
for _, ifname := range ifnames {
|
for _, ifname := range ifnames {
|
||||||
out = append(out, checkLinkUp(fd, ifname))
|
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
|
carrierWait := 3 * time.Second
|
||||||
r, reset := checkRings(fd, ifname, cfg.rxRing, cfg.txRing)
|
r, reset := checkRings(fd, ifname, wantRxRing, wantTxRing)
|
||||||
out = append(out, r)
|
out = append(out, r)
|
||||||
if reset {
|
if reset {
|
||||||
carrierWait = 10 * time.Second
|
carrierWait = 10 * time.Second
|
||||||
|
|||||||
Reference in New Issue
Block a user