Path: blob/master/tools/testing/selftests/drivers/net/hw/rss_ctx.py
29271 views
#!/usr/bin/env python31# SPDX-License-Identifier: GPL-2.023import datetime4import random5import re6from lib.py import ksft_run, ksft_pr, ksft_exit7from lib.py import ksft_eq, ksft_ne, ksft_ge, ksft_in, ksft_lt, ksft_true, ksft_raises8from lib.py import NetDrvEpEnv9from lib.py import EthtoolFamily, NetdevFamily10from lib.py import KsftSkipEx, KsftFailEx11from lib.py import rand_port12from lib.py import ethtool, ip, defer, GenerateTraffic, CmdExitFailure131415def _rss_key_str(key):16return ":".join(["{:02x}".format(x) for x in key])171819def _rss_key_rand(length):20return [random.randint(0, 255) for _ in range(length)]212223def _rss_key_check(cfg, data=None, context=0):24if data is None:25data = get_rss(cfg, context=context)26if 'rss-hash-key' not in data:27return28non_zero = [x for x in data['rss-hash-key'] if x != 0]29ksft_eq(bool(non_zero), True, comment=f"RSS key is all zero {data['rss-hash-key']}")303132def get_rss(cfg, context=0):33return ethtool(f"-x {cfg.ifname} context {context}", json=True)[0]343536def get_drop_err_sum(cfg):37stats = ip("-s -s link show dev " + cfg.ifname, json=True)[0]38cnt = 039for key in ['errors', 'dropped', 'over_errors', 'fifo_errors',40'length_errors', 'crc_errors', 'missed_errors',41'frame_errors']:42cnt += stats["stats64"]["rx"][key]43return cnt, stats["stats64"]["tx"]["carrier_changes"]444546def ethtool_create(cfg, act, opts):47output = ethtool(f"{act} {cfg.ifname} {opts}").stdout48# Output will be something like: "New RSS context is 1" or49# "Added rule with ID 7", we want the integer from the end50return int(output.split()[-1])515253def require_ntuple(cfg):54features = ethtool(f"-k {cfg.ifname}", json=True)[0]55if not features["ntuple-filters"]["active"]:56# ntuple is more of a capability than a config knob, don't bother57# trying to enable it (until some driver actually needs it).58raise KsftSkipEx("Ntuple filters not enabled on the device: " + str(features["ntuple-filters"]))596061def require_context_cnt(cfg, need_cnt):62# There's no good API to get the context count, so the tests63# which try to add a lot opportunisitically set the count they64# discovered. Careful with test ordering!65if need_cnt and cfg.context_cnt and cfg.context_cnt < need_cnt:66raise KsftSkipEx(f"Test requires at least {need_cnt} contexts, but device only has {cfg.context_cnt}")676869# Get Rx packet counts for all queues, as a simple list of integers70# if @prev is specified the prev counts will be subtracted71def _get_rx_cnts(cfg, prev=None):72cfg.wait_hw_stats_settle()73data = cfg.netdevnl.qstats_get({"ifindex": cfg.ifindex, "scope": ["queue"]}, dump=True)74data = [x for x in data if x['queue-type'] == "rx"]75max_q = max([x["queue-id"] for x in data])76queue_stats = [0] * (max_q + 1)77for q in data:78queue_stats[q["queue-id"]] = q["rx-packets"]79if prev and q["queue-id"] < len(prev):80queue_stats[q["queue-id"]] -= prev[q["queue-id"]]81return queue_stats828384def _send_traffic_check(cfg, port, name, params):85# params is a dict with 3 possible keys:86# - "target": required, which queues we expect to get iperf traffic87# - "empty": optional, which queues should see no traffic at all88# - "noise": optional, which queues we expect to see low traffic;89# used for queues of the main context, since some background90# OS activity may use those queues while we're testing91# the value for each is a list, or some other iterable containing queue ids.9293cnts = _get_rx_cnts(cfg)94GenerateTraffic(cfg, port=port).wait_pkts_and_stop(20000)95cnts = _get_rx_cnts(cfg, prev=cnts)9697directed = sum(cnts[i] for i in params['target'])9899ksft_ge(directed, 20000, f"traffic on {name}: " + str(cnts))100if params.get('noise'):101ksft_lt(sum(cnts[i] for i in params['noise']), directed / 2,102f"traffic on other queues ({name})':" + str(cnts))103if params.get('empty'):104ksft_eq(sum(cnts[i] for i in params['empty']), 0,105f"traffic on inactive queues ({name}): " + str(cnts))106107108def _ntuple_rule_check(cfg, rule_id, ctx_id):109"""Check that ntuple rule references RSS context ID"""110text = ethtool(f"-n {cfg.ifname} rule {rule_id}").stdout111pattern = f"RSS Context (ID: )?{ctx_id}"112ksft_true(re.search(pattern, text), "RSS context not referenced in ntuple rule")113114115def test_rss_key_indir(cfg):116"""Test basics like updating the main RSS key and indirection table."""117118qcnt = len(_get_rx_cnts(cfg))119if qcnt < 3:120raise KsftSkipEx("Device has fewer than 3 queues (or doesn't support queue stats)")121122data = get_rss(cfg)123want_keys = ['rss-hash-key', 'rss-hash-function', 'rss-indirection-table']124for k in want_keys:125if k not in data:126raise KsftFailEx("ethtool results missing key: " + k)127if not data[k]:128raise KsftFailEx(f"ethtool results empty for '{k}': {data[k]}")129130_rss_key_check(cfg, data=data)131key_len = len(data['rss-hash-key'])132133# Set the key134key = _rss_key_rand(key_len)135ethtool(f"-X {cfg.ifname} hkey " + _rss_key_str(key))136137data = get_rss(cfg)138ksft_eq(key, data['rss-hash-key'])139140# Set the indirection table and the key together141key = _rss_key_rand(key_len)142ethtool(f"-X {cfg.ifname} equal 3 hkey " + _rss_key_str(key))143reset_indir = defer(ethtool, f"-X {cfg.ifname} default")144145data = get_rss(cfg)146_rss_key_check(cfg, data=data)147ksft_eq(0, min(data['rss-indirection-table']))148ksft_eq(2, max(data['rss-indirection-table']))149150# Reset indirection table and set the key151key = _rss_key_rand(key_len)152ethtool(f"-X {cfg.ifname} default hkey " + _rss_key_str(key))153data = get_rss(cfg)154_rss_key_check(cfg, data=data)155ksft_eq(0, min(data['rss-indirection-table']))156ksft_eq(qcnt - 1, max(data['rss-indirection-table']))157158# Set the indirection table159ethtool(f"-X {cfg.ifname} equal 2")160data = get_rss(cfg)161ksft_eq(0, min(data['rss-indirection-table']))162ksft_eq(1, max(data['rss-indirection-table']))163164# Check we only get traffic on the first 2 queues165cnts = _get_rx_cnts(cfg)166GenerateTraffic(cfg).wait_pkts_and_stop(20000)167cnts = _get_rx_cnts(cfg, prev=cnts)168# 2 queues, 20k packets, must be at least 5k per queue169ksft_ge(cnts[0], 5000, "traffic on main context (1/2): " + str(cnts))170ksft_ge(cnts[1], 5000, "traffic on main context (2/2): " + str(cnts))171# The other queues should be unused172ksft_eq(sum(cnts[2:]), 0, "traffic on unused queues: " + str(cnts))173174# Restore, and check traffic gets spread again175reset_indir.exec()176177cnts = _get_rx_cnts(cfg)178GenerateTraffic(cfg).wait_pkts_and_stop(20000)179cnts = _get_rx_cnts(cfg, prev=cnts)180if qcnt > 4:181# First two queues get less traffic than all the rest182ksft_lt(sum(cnts[:2]), sum(cnts[2:]),183"traffic distributed: " + str(cnts))184else:185# When queue count is low make sure third queue got significant pkts186ksft_ge(cnts[2], 3500, "traffic distributed: " + str(cnts))187188189def test_rss_queue_reconfigure(cfg, main_ctx=True):190"""Make sure queue changes can't override requested RSS config.191192By default main RSS table should change to include all queues.193When user sets a specific RSS config the driver should preserve it,194even when queue count changes. Driver should refuse to deactivate195queues used in the user-set RSS config.196"""197198if not main_ctx:199require_ntuple(cfg)200201# Start with 4 queues, an arbitrary known number.202try:203qcnt = len(_get_rx_cnts(cfg))204ethtool(f"-L {cfg.ifname} combined 4")205defer(ethtool, f"-L {cfg.ifname} combined {qcnt}")206except:207raise KsftSkipEx("Not enough queues for the test or qstat not supported")208209if main_ctx:210ctx_id = 0211ctx_ref = ""212else:213ctx_id = ethtool_create(cfg, "-X", "context new")214ctx_ref = f"context {ctx_id}"215defer(ethtool, f"-X {cfg.ifname} {ctx_ref} delete")216217# Indirection table should be distributing to all queues.218data = get_rss(cfg, context=ctx_id)219ksft_eq(0, min(data['rss-indirection-table']))220ksft_eq(3, max(data['rss-indirection-table']))221222# Increase queues, indirection table should be distributing to all queues.223# It's unclear whether tables of additional contexts should be reset, too.224if main_ctx:225ethtool(f"-L {cfg.ifname} combined 5")226data = get_rss(cfg)227ksft_eq(0, min(data['rss-indirection-table']))228ksft_eq(4, max(data['rss-indirection-table']))229ethtool(f"-L {cfg.ifname} combined 4")230231# Configure the table explicitly232port = rand_port()233ethtool(f"-X {cfg.ifname} {ctx_ref} weight 1 0 0 1")234if main_ctx:235other_key = 'empty'236defer(ethtool, f"-X {cfg.ifname} default")237else:238other_key = 'noise'239flow = f"flow-type tcp{cfg.addr_ipver} dst-ip {cfg.addr} dst-port {port} context {ctx_id}"240ntuple = ethtool_create(cfg, "-N", flow)241defer(ethtool, f"-N {cfg.ifname} delete {ntuple}")242243_send_traffic_check(cfg, port, ctx_ref, { 'target': (0, 3),244other_key: (1, 2) })245246# We should be able to increase queues, but table should be left untouched247ethtool(f"-L {cfg.ifname} combined 5")248data = get_rss(cfg, context=ctx_id)249ksft_eq({0, 3}, set(data['rss-indirection-table']))250251_send_traffic_check(cfg, port, ctx_ref, { 'target': (0, 3),252other_key: (1, 2, 4) })253254# Setting queue count to 3 should fail, queue 3 is used255try:256ethtool(f"-L {cfg.ifname} combined 3")257except CmdExitFailure:258pass259else:260raise Exception(f"Driver didn't prevent us from deactivating a used queue (context {ctx_id})")261262if not main_ctx:263ethtool(f"-L {cfg.ifname} combined 4")264flow = f"flow-type tcp{cfg.addr_ipver} dst-ip {cfg.addr} dst-port {port} context {ctx_id} action 1"265try:266# this targets queue 4, which doesn't exist267ntuple2 = ethtool_create(cfg, "-N", flow)268defer(ethtool, f"-N {cfg.ifname} delete {ntuple2}")269except CmdExitFailure:270pass271else:272raise Exception(f"Driver didn't prevent us from targeting a nonexistent queue (context {ctx_id})")273# change the table to target queues 0 and 2274ethtool(f"-X {cfg.ifname} {ctx_ref} weight 1 0 1 0")275# ntuple rule therefore targets queues 1 and 3276try:277ntuple2 = ethtool_create(cfg, "-N", flow)278except CmdExitFailure:279ksft_pr("Driver does not support rss + queue offset")280return281282defer(ethtool, f"-N {cfg.ifname} delete {ntuple2}")283# should replace existing filter284ksft_eq(ntuple, ntuple2)285_send_traffic_check(cfg, port, ctx_ref, { 'target': (1, 3),286'noise' : (0, 2) })287# Setting queue count to 3 should fail, queue 3 is used288try:289ethtool(f"-L {cfg.ifname} combined 3")290except CmdExitFailure:291pass292else:293raise Exception(f"Driver didn't prevent us from deactivating a used queue (context {ctx_id})")294295296def test_rss_resize(cfg):297"""Test resizing of the RSS table.298299Some devices dynamically increase and decrease the size of the RSS300indirection table based on the number of enabled queues.301When that happens driver must maintain the balance of entries302(preferably duplicating the smaller table).303"""304305channels = cfg.ethnl.channels_get({'header': {'dev-index': cfg.ifindex}})306ch_max = channels['combined-max']307qcnt = channels['combined-count']308309if ch_max < 2:310raise KsftSkipEx(f"Not enough queues for the test: {ch_max}")311312ethtool(f"-L {cfg.ifname} combined 2")313defer(ethtool, f"-L {cfg.ifname} combined {qcnt}")314315ethtool(f"-X {cfg.ifname} weight 1 7")316defer(ethtool, f"-X {cfg.ifname} default")317318ethtool(f"-L {cfg.ifname} combined {ch_max}")319data = get_rss(cfg)320ksft_eq(0, min(data['rss-indirection-table']))321ksft_eq(1, max(data['rss-indirection-table']))322323ksft_eq(7,324data['rss-indirection-table'].count(1) /325data['rss-indirection-table'].count(0),326f"Table imbalance after resize: {data['rss-indirection-table']}")327328329def test_hitless_key_update(cfg):330"""Test that flows may be rehashed without impacting traffic.331332Some workloads may want to rehash the flows in response to an imbalance.333Most effective way to do that is changing the RSS key. Check that changing334the key does not cause link flaps or traffic disruption.335336Disrupting traffic for key update is not a bug, but makes the key337update unusable for rehashing under load.338"""339data = get_rss(cfg)340key_len = len(data['rss-hash-key'])341342ethnl = EthtoolFamily()343key = random.randbytes(key_len)344345tgen = GenerateTraffic(cfg)346try:347errors0, carrier0 = get_drop_err_sum(cfg)348t0 = datetime.datetime.now()349ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, "hkey": key})350t1 = datetime.datetime.now()351errors1, carrier1 = get_drop_err_sum(cfg)352finally:353tgen.wait_pkts_and_stop(5000)354355ksft_lt((t1 - t0).total_seconds(), 0.15)356ksft_eq(errors1 - errors1, 0)357ksft_eq(carrier1 - carrier0, 0)358359360def test_rss_context_dump(cfg):361"""362Test dumping RSS contexts. This tests mostly exercises the kernel APIs.363"""364365# Get a random key of the right size366data = get_rss(cfg)367if 'rss-hash-key' in data:368key_data = _rss_key_rand(len(data['rss-hash-key']))369key = _rss_key_str(key_data)370else:371key_data = []372key = "ba:ad"373374ids = []375try:376ids.append(ethtool_create(cfg, "-X", f"context new"))377defer(ethtool, f"-X {cfg.ifname} context {ids[-1]} delete")378379ids.append(ethtool_create(cfg, "-X", f"context new weight 1 1"))380defer(ethtool, f"-X {cfg.ifname} context {ids[-1]} delete")381382ids.append(ethtool_create(cfg, "-X", f"context new hkey {key}"))383defer(ethtool, f"-X {cfg.ifname} context {ids[-1]} delete")384except CmdExitFailure:385if not ids:386raise KsftSkipEx("Unable to add any contexts")387ksft_pr(f"Added only {len(ids)} out of 3 contexts")388389expect_tuples = set([(cfg.ifname, -1)] + [(cfg.ifname, ctx_id) for ctx_id in ids])390391# Dump all392ctxs = cfg.ethnl.rss_get({}, dump=True)393tuples = [(c['header']['dev-name'], c.get('context', -1)) for c in ctxs]394ksft_eq(len(tuples), len(set(tuples)), "duplicates in context dump")395ctx_tuples = set([ctx for ctx in tuples if ctx[0] == cfg.ifname])396ksft_eq(expect_tuples, ctx_tuples)397398# Sanity-check the results399for data in ctxs:400ksft_ne(set(data.get('indir', [1])), {0}, "indir table is all zero")401ksft_ne(set(data.get('hkey', [1])), {0}, "key is all zero")402403# More specific checks404if len(ids) > 1 and data.get('context') == ids[1]:405ksft_eq(set(data['indir']), {0, 1},406"ctx1 - indir table mismatch")407if len(ids) > 2 and data.get('context') == ids[2]:408ksft_eq(data['hkey'], bytes(key_data), "ctx2 - key mismatch")409410# Ifindex filter411ctxs = cfg.ethnl.rss_get({'header': {'dev-name': cfg.ifname}}, dump=True)412tuples = [(c['header']['dev-name'], c.get('context', -1)) for c in ctxs]413ctx_tuples = set(tuples)414ksft_eq(len(tuples), len(ctx_tuples), "duplicates in context dump")415ksft_eq(expect_tuples, ctx_tuples)416417# Skip ctx 0418expect_tuples.remove((cfg.ifname, -1))419420ctxs = cfg.ethnl.rss_get({'start-context': 1}, dump=True)421tuples = [(c['header']['dev-name'], c.get('context', -1)) for c in ctxs]422ksft_eq(len(tuples), len(set(tuples)), "duplicates in context dump")423ctx_tuples = set([ctx for ctx in tuples if ctx[0] == cfg.ifname])424ksft_eq(expect_tuples, ctx_tuples)425426# And finally both with ifindex and skip main427ctxs = cfg.ethnl.rss_get({'header': {'dev-name': cfg.ifname}, 'start-context': 1}, dump=True)428ctx_tuples = set([(c['header']['dev-name'], c.get('context', -1)) for c in ctxs])429ksft_eq(expect_tuples, ctx_tuples)430431432def test_rss_context(cfg, ctx_cnt=1, create_with_cfg=None):433"""434Test separating traffic into RSS contexts.435The queues will be allocated 2 for each context:436ctx0 ctx1 ctx2 ctx3437[0 1] [2 3] [4 5] [6 7] ...438"""439440require_ntuple(cfg)441442requested_ctx_cnt = ctx_cnt443444# Try to allocate more queues when necessary445qcnt = len(_get_rx_cnts(cfg))446if qcnt < 2 + 2 * ctx_cnt:447try:448ksft_pr(f"Increasing queue count {qcnt} -> {2 + 2 * ctx_cnt}")449ethtool(f"-L {cfg.ifname} combined {2 + 2 * ctx_cnt}")450defer(ethtool, f"-L {cfg.ifname} combined {qcnt}")451except:452raise KsftSkipEx("Not enough queues for the test")453454ports = []455456# Use queues 0 and 1 for normal traffic457ethtool(f"-X {cfg.ifname} equal 2")458defer(ethtool, f"-X {cfg.ifname} default")459460for i in range(ctx_cnt):461want_cfg = f"start {2 + i * 2} equal 2"462create_cfg = want_cfg if create_with_cfg else ""463464try:465ctx_id = ethtool_create(cfg, "-X", f"context new {create_cfg}")466defer(ethtool, f"-X {cfg.ifname} context {ctx_id} delete")467except CmdExitFailure:468# try to carry on and skip at the end469if i == 0:470raise471ksft_pr(f"Failed to create context {i + 1}, trying to test what we got")472ctx_cnt = i473if cfg.context_cnt is None:474cfg.context_cnt = ctx_cnt475break476477_rss_key_check(cfg, context=ctx_id)478479if not create_with_cfg:480ethtool(f"-X {cfg.ifname} context {ctx_id} {want_cfg}")481_rss_key_check(cfg, context=ctx_id)482483# Sanity check the context we just created484data = get_rss(cfg, ctx_id)485ksft_eq(min(data['rss-indirection-table']), 2 + i * 2, "Unexpected context cfg: " + str(data))486ksft_eq(max(data['rss-indirection-table']), 2 + i * 2 + 1, "Unexpected context cfg: " + str(data))487488ports.append(rand_port())489flow = f"flow-type tcp{cfg.addr_ipver} dst-ip {cfg.addr} dst-port {ports[i]} context {ctx_id}"490ntuple = ethtool_create(cfg, "-N", flow)491defer(ethtool, f"-N {cfg.ifname} delete {ntuple}")492493_ntuple_rule_check(cfg, ntuple, ctx_id)494495for i in range(ctx_cnt):496_send_traffic_check(cfg, ports[i], f"context {i}",497{ 'target': (2+i*2, 3+i*2),498'noise': (0, 1),499'empty': list(range(2, 2+i*2)) + list(range(4+i*2, 2+2*ctx_cnt)) })500501if requested_ctx_cnt != ctx_cnt:502raise KsftSkipEx(f"Tested only {ctx_cnt} contexts, wanted {requested_ctx_cnt}")503504505def test_rss_context4(cfg):506test_rss_context(cfg, 4)507508509def test_rss_context32(cfg):510test_rss_context(cfg, 32)511512513def test_rss_context4_create_with_cfg(cfg):514test_rss_context(cfg, 4, create_with_cfg=True)515516517def test_rss_context_queue_reconfigure(cfg):518test_rss_queue_reconfigure(cfg, main_ctx=False)519520521def test_rss_context_out_of_order(cfg, ctx_cnt=4):522"""523Test separating traffic into RSS contexts.524Contexts are removed in semi-random order, and steering re-tested525to make sure removal doesn't break steering to surviving contexts.526Test requires 3 contexts to work.527"""528529require_ntuple(cfg)530require_context_cnt(cfg, 4)531532# Try to allocate more queues when necessary533qcnt = len(_get_rx_cnts(cfg))534if qcnt < 2 + 2 * ctx_cnt:535try:536ksft_pr(f"Increasing queue count {qcnt} -> {2 + 2 * ctx_cnt}")537ethtool(f"-L {cfg.ifname} combined {2 + 2 * ctx_cnt}")538defer(ethtool, f"-L {cfg.ifname} combined {qcnt}")539except:540raise KsftSkipEx("Not enough queues for the test")541542ntuple = []543ctx = []544ports = []545546def remove_ctx(idx):547ntuple[idx].exec()548ntuple[idx] = None549ctx[idx].exec()550ctx[idx] = None551552def check_traffic():553for i in range(ctx_cnt):554if ctx[i]:555expected = {556'target': (2+i*2, 3+i*2),557'noise': (0, 1),558'empty': list(range(2, 2+i*2)) + list(range(4+i*2, 2+2*ctx_cnt))559}560else:561expected = {562'target': (0, 1),563'empty': range(2, 2+2*ctx_cnt)564}565566_send_traffic_check(cfg, ports[i], f"context {i}", expected)567568# Use queues 0 and 1 for normal traffic569ethtool(f"-X {cfg.ifname} equal 2")570defer(ethtool, f"-X {cfg.ifname} default")571572for i in range(ctx_cnt):573ctx_id = ethtool_create(cfg, "-X", f"context new start {2 + i * 2} equal 2")574ctx.append(defer(ethtool, f"-X {cfg.ifname} context {ctx_id} delete"))575576ports.append(rand_port())577flow = f"flow-type tcp{cfg.addr_ipver} dst-ip {cfg.addr} dst-port {ports[i]} context {ctx_id}"578ntuple_id = ethtool_create(cfg, "-N", flow)579ntuple.append(defer(ethtool, f"-N {cfg.ifname} delete {ntuple_id}"))580581check_traffic()582583# Remove middle context584remove_ctx(ctx_cnt // 2)585check_traffic()586587# Remove first context588remove_ctx(0)589check_traffic()590591# Remove last context592remove_ctx(-1)593check_traffic()594595596def test_rss_context_overlap(cfg, other_ctx=0):597"""598Test contexts overlapping with each other.599Use 4 queues for the main context, but only queues 2 and 3 for context 1.600"""601602require_ntuple(cfg)603if other_ctx:604require_context_cnt(cfg, 2)605606queue_cnt = len(_get_rx_cnts(cfg))607if queue_cnt < 4:608try:609ksft_pr(f"Increasing queue count {queue_cnt} -> 4")610ethtool(f"-L {cfg.ifname} combined 4")611defer(ethtool, f"-L {cfg.ifname} combined {queue_cnt}")612except:613raise KsftSkipEx("Not enough queues for the test")614615if other_ctx == 0:616ethtool(f"-X {cfg.ifname} equal 4")617defer(ethtool, f"-X {cfg.ifname} default")618else:619other_ctx = ethtool_create(cfg, "-X", "context new")620ethtool(f"-X {cfg.ifname} context {other_ctx} equal 4")621defer(ethtool, f"-X {cfg.ifname} context {other_ctx} delete")622623ctx_id = ethtool_create(cfg, "-X", "context new")624ethtool(f"-X {cfg.ifname} context {ctx_id} start 2 equal 2")625defer(ethtool, f"-X {cfg.ifname} context {ctx_id} delete")626627port = rand_port()628if other_ctx:629flow = f"flow-type tcp{cfg.addr_ipver} dst-ip {cfg.addr} dst-port {port} context {other_ctx}"630ntuple_id = ethtool_create(cfg, "-N", flow)631ntuple = defer(ethtool, f"-N {cfg.ifname} delete {ntuple_id}")632633# Test the main context634cnts = _get_rx_cnts(cfg)635GenerateTraffic(cfg, port=port).wait_pkts_and_stop(20000)636cnts = _get_rx_cnts(cfg, prev=cnts)637638ksft_ge(sum(cnts[ :4]), 20000, "traffic on main context: " + str(cnts))639ksft_ge(sum(cnts[ :2]), 7000, "traffic on main context (1/2): " + str(cnts))640ksft_ge(sum(cnts[2:4]), 7000, "traffic on main context (2/2): " + str(cnts))641if other_ctx == 0:642ksft_eq(sum(cnts[4: ]), 0, "traffic on other queues: " + str(cnts))643644# Now create a rule for context 1 and make sure traffic goes to a subset645if other_ctx:646ntuple.exec()647flow = f"flow-type tcp{cfg.addr_ipver} dst-ip {cfg.addr} dst-port {port} context {ctx_id}"648ntuple_id = ethtool_create(cfg, "-N", flow)649defer(ethtool, f"-N {cfg.ifname} delete {ntuple_id}")650651cnts = _get_rx_cnts(cfg)652GenerateTraffic(cfg, port=port).wait_pkts_and_stop(20000)653cnts = _get_rx_cnts(cfg, prev=cnts)654655directed = sum(cnts[2:4])656ksft_lt(sum(cnts[ :2]), directed / 2, "traffic on main context: " + str(cnts))657ksft_ge(directed, 20000, "traffic on extra context: " + str(cnts))658if other_ctx == 0:659ksft_eq(sum(cnts[4: ]), 0, "traffic on other queues: " + str(cnts))660661662def test_rss_context_overlap2(cfg):663test_rss_context_overlap(cfg, True)664665666def test_flow_add_context_missing(cfg):667"""668Test that we are not allowed to add a rule pointing to an RSS context669which was never created.670"""671672require_ntuple(cfg)673674# Find a context which doesn't exist675for ctx_id in range(1, 100):676try:677get_rss(cfg, context=ctx_id)678except CmdExitFailure:679break680681with ksft_raises(CmdExitFailure) as cm:682flow = f"flow-type tcp{cfg.addr_ipver} dst-ip {cfg.addr} dst-port 1234 context {ctx_id}"683ntuple_id = ethtool_create(cfg, "-N", flow)684ethtool(f"-N {cfg.ifname} delete {ntuple_id}")685if cm.exception:686ksft_in('Invalid argument', cm.exception.cmd.stderr)687688689def test_delete_rss_context_busy(cfg):690"""691Test that deletion returns -EBUSY when an rss context is being used692by an ntuple filter.693"""694695require_ntuple(cfg)696697# create additional rss context698ctx_id = ethtool_create(cfg, "-X", "context new")699ctx_deleter = defer(ethtool, f"-X {cfg.ifname} context {ctx_id} delete")700701# utilize context from ntuple filter702port = rand_port()703flow = f"flow-type tcp{cfg.addr_ipver} dst-ip {cfg.addr} dst-port {port} context {ctx_id}"704ntuple_id = ethtool_create(cfg, "-N", flow)705defer(ethtool, f"-N {cfg.ifname} delete {ntuple_id}")706707# attempt to delete in-use context708try:709ctx_deleter.exec_only()710ctx_deleter.cancel()711raise KsftFailEx(f"deleted context {ctx_id} used by rule {ntuple_id}")712except CmdExitFailure:713pass714715716def test_rss_ntuple_addition(cfg):717"""718Test that the queue offset (ring_cookie) of an ntuple rule is added719to the queue number read from the indirection table.720"""721722require_ntuple(cfg)723724queue_cnt = len(_get_rx_cnts(cfg))725if queue_cnt < 4:726try:727ksft_pr(f"Increasing queue count {queue_cnt} -> 4")728ethtool(f"-L {cfg.ifname} combined 4")729defer(ethtool, f"-L {cfg.ifname} combined {queue_cnt}")730except:731raise KsftSkipEx("Not enough queues for the test")732733# Use queue 0 for normal traffic734ethtool(f"-X {cfg.ifname} equal 1")735defer(ethtool, f"-X {cfg.ifname} default")736737# create additional rss context738ctx_id = ethtool_create(cfg, "-X", "context new equal 2")739defer(ethtool, f"-X {cfg.ifname} context {ctx_id} delete")740741# utilize context from ntuple filter742port = rand_port()743flow = f"flow-type tcp{cfg.addr_ipver} dst-ip {cfg.addr} dst-port {port} context {ctx_id} action 2"744try:745ntuple_id = ethtool_create(cfg, "-N", flow)746except CmdExitFailure:747raise KsftSkipEx("Ntuple filter with RSS and nonzero action not supported")748defer(ethtool, f"-N {cfg.ifname} delete {ntuple_id}")749750_send_traffic_check(cfg, port, f"context {ctx_id}", { 'target': (2, 3),751'empty' : (1,),752'noise' : (0,) })753754755def test_rss_default_context_rule(cfg):756"""757Allocate a port, direct this port to context 0, then create a new RSS758context and steer all TCP traffic to it (context 1). Verify that:759* Traffic to the specific port continues to use queues of the main760context (0/1).761* Traffic to any other TCP port is redirected to the new context762(queues 2/3).763"""764765require_ntuple(cfg)766767queue_cnt = len(_get_rx_cnts(cfg))768if queue_cnt < 4:769try:770ksft_pr(f"Increasing queue count {queue_cnt} -> 4")771ethtool(f"-L {cfg.ifname} combined 4")772defer(ethtool, f"-L {cfg.ifname} combined {queue_cnt}")773except Exception as exc:774raise KsftSkipEx("Not enough queues for the test") from exc775776# Use queues 0 and 1 for the main context777ethtool(f"-X {cfg.ifname} equal 2")778defer(ethtool, f"-X {cfg.ifname} default")779780# Create a new RSS context that uses queues 2 and 3781ctx_id = ethtool_create(cfg, "-X", "context new start 2 equal 2")782defer(ethtool, f"-X {cfg.ifname} context {ctx_id} delete")783784# Generic low-priority rule: redirect all TCP traffic to the new context.785# Give it an explicit higher location number (lower priority).786flow_generic = f"flow-type tcp{cfg.addr_ipver} dst-ip {cfg.addr} context {ctx_id} loc 1"787ethtool(f"-N {cfg.ifname} {flow_generic}")788defer(ethtool, f"-N {cfg.ifname} delete 1")789790# Specific high-priority rule for a random port that should stay on context 0.791# Assign loc 0 so it is evaluated before the generic rule.792port_main = rand_port()793flow_main = f"flow-type tcp{cfg.addr_ipver} dst-ip {cfg.addr} dst-port {port_main} context 0 loc 0"794ethtool(f"-N {cfg.ifname} {flow_main}")795defer(ethtool, f"-N {cfg.ifname} delete 0")796797_ntuple_rule_check(cfg, 1, ctx_id)798799# Verify that traffic matching the specific rule still goes to queues 0/1800_send_traffic_check(cfg, port_main, "context 0",801{ 'target': (0, 1),802'empty' : (2, 3) })803804# And that traffic for any other port is steered to the new context805port_other = rand_port()806_send_traffic_check(cfg, port_other, f"context {ctx_id}",807{ 'target': (2, 3),808'noise' : (0, 1) })809810811def main() -> None:812with NetDrvEpEnv(__file__, nsim_test=False) as cfg:813cfg.context_cnt = None814cfg.ethnl = EthtoolFamily()815cfg.netdevnl = NetdevFamily()816817ksft_run([test_rss_key_indir, test_rss_queue_reconfigure,818test_rss_resize, test_hitless_key_update,819test_rss_context, test_rss_context4, test_rss_context32,820test_rss_context_dump, test_rss_context_queue_reconfigure,821test_rss_context_overlap, test_rss_context_overlap2,822test_rss_context_out_of_order, test_rss_context4_create_with_cfg,823test_flow_add_context_missing,824test_delete_rss_context_busy, test_rss_ntuple_addition,825test_rss_default_context_rule],826args=(cfg, ))827ksft_exit()828829830if __name__ == "__main__":831main()832833834