From 1847195cf02a2baa478a664a671adc95c704a810 Mon Sep 17 00:00:00 2001 From: vincentpcng <129542523+vincentpcng@users.noreply.github.com> Date: Fri, 22 Nov 2024 10:01:49 -0800 Subject: [PATCH] Add port FEC BER feature swss, HLD#1829 (#3363) What I did This is to add the swss changes for the feature port FEC BER. It modify the port_rates.lua script to compute the BER and stored them in the DB . Two additonal PR(s) will address the cli ( sonic-utilities) and the sonic-mgmt changes Why I did it The HLD for this feature is HLD#1829 How I verified it We verify the counters internally (1) verify when link has correctable FEC errors, the BER were calcuated accordingly (2) verify 400G with 4x100 and the BER , four serdes with 100G rate (3) run a redis-script to polling the DB counters and verify the calculation (4) stop polling manually modify the uncorrectable counter and verify the cli display and calculation --- orchagent/port_rates.lua | 146 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 142 insertions(+), 4 deletions(-) diff --git a/orchagent/port_rates.lua b/orchagent/port_rates.lua index c29977d153..d7b8b8e4b1 100644 --- a/orchagent/port_rates.lua +++ b/orchagent/port_rates.lua @@ -13,6 +13,9 @@ end local counters_db = ARGV[1] local counters_table_name = ARGV[2] local rates_table_name = "RATES" +local appl_db_port = "PORT_TABLE" +-- refer back to common/schema.h +local appl_db = "0" -- Get configuration redis.call('SELECT', counters_db) @@ -29,11 +32,117 @@ logit(alpha) logit(one_minus_alpha) logit(delta) +local port_interface_oid_map = redis.call('HGETALL', "COUNTERS_PORT_NAME_MAP") +local port_interface_oid_key_count = redis.call('HLEN', "COUNTERS_PORT_NAME_MAP") + +-- lookup interface name from port oid + +local function find_interface_name_from_oid(port) + + for i = 1, port_interface_oid_key_count do + local index = i * 2 - 1 + if port_interface_oid_map[index + 1] == port then + return port_interface_oid_map[index] + end + end + + return 0 +end + +-- calculate lanes and serdes speed from interface lane count & speed +-- return lane speed and serdes speed + +local function calculate_lane_and_serdes_speed(count, speed) + + local serdes = 0 + local lane_speed = 0 + + if count == 0 or speed == 0 then + logit("Invalid number of lanes or speed") + return 0, 0 + end + + -- check serdes_cnt if it is a multiple of speed + local serdes_cnt = math.fmod(speed, count) + + if serdes_cnt ~= 0 then + logit("Invalid speed and number of lanes combination") + return 0, 0 + end + + lane_speed = math.floor(speed / count) + + -- return value in bits + if lane_speed == 1000 then + serdes = 1.25e+9 + elseif lane_speed == 10000 then + serdes = 10.3125e+9 + elseif lane_speed == 25000 then + serdes = 25.78125e+9 + elseif lane_speed == 50000 then + serdes = 53.125e+9 + elseif lane_speed == 100000 then + serdes = 106.25e+9 + else + logit("Invalid serdes speed") + end + + return lane_speed, serdes +end + +-- look up interface lanes count, lanes speed & serdes speed +-- return lane count, lane speed, serdes speed + +local function find_lanes_and_serdes(interface_name) + -- get the port config from config db + local _ + local serdes, lane_speed, count = 0, 0, 0 + + -- Get the port configure + redis.call('SELECT', appl_db) + local lanes = redis.call('HGET', appl_db_port ..':'..interface_name, 'lanes') + + if lanes then + local speed = redis.call('HGET', appl_db_port ..':'..interface_name, 'speed') + + -- we were spliting it on ',' + _, count = string.gsub(lanes, ",", ",") + count = count + 1 + + lane_speed, serdes = calculate_lane_and_serdes_speed(count, speed) + + end + -- switch back to counter db + redis.call('SELECT', counters_db) + + return count, lane_speed, serdes +end + local function compute_rate(port) + local state_table = rates_table_name .. ':' .. port .. ':' .. 'PORT' local initialized = redis.call('HGET', state_table, 'INIT_DONE') logit(initialized) + -- FEC BER + local fec_corr_bits, fec_uncorr_frames + local fec_corr_bits_ber_new, fec_uncorr_bits_ber_new = -1, -1 + -- HLD review suggest to use the statistical average when calculate the post fec ber + local rs_average_frame_ber = 1e-8 + local lanes_speed, serdes_speed, lanes_count = 0, 0, 0 + + -- lookup interface name from oid + local interface_name = find_interface_name_from_oid(port) + if interface_name then + lanes_count, lanes_speed, serdes_speed = find_lanes_and_serdes(interface_name) + + if lanes_count and serdes_speed then + fec_corr_bits = redis.call('HGET', counters_table_name .. ':' .. port, 'SAI_PORT_STAT_IF_IN_FEC_CORRECTED_BITS') + fec_uncorr_frames = redis.call('HGET', counters_table_name .. ':' .. port, 'SAI_PORT_STAT_IF_IN_FEC_NOT_CORRECTABLE_FRAMES') + end + end + + -- Get new COUNTERS values local in_ucast_pkts = redis.call('HGET', counters_table_name .. ':' .. port, 'SAI_PORT_STAT_IF_IN_UCAST_PKTS') local in_non_ucast_pkts = redis.call('HGET', counters_table_name .. ':' .. port, 'SAI_PORT_STAT_IF_IN_NON_UCAST_PKTS') @@ -58,10 +167,11 @@ local function compute_rate(port) local out_octets_last = redis.call('HGET', rates_table_name .. ':' .. port, 'SAI_PORT_STAT_IF_OUT_OCTETS_last') -- Calculate new rates values - local rx_bps_new = (in_octets - in_octets_last) / delta * 1000 - local tx_bps_new = (out_octets - out_octets_last) / delta * 1000 - local rx_pps_new = ((in_ucast_pkts + in_non_ucast_pkts) - (in_ucast_pkts_last + in_non_ucast_pkts_last)) / delta * 1000 - local tx_pps_new = ((out_ucast_pkts + out_non_ucast_pkts) - (out_ucast_pkts_last + out_non_ucast_pkts_last)) / delta * 1000 + local scale_factor = 1000 / delta + local rx_bps_new = (in_octets - in_octets_last) * scale_factor + local tx_bps_new = (out_octets - out_octets_last) * scale_factor + local rx_pps_new = ((in_ucast_pkts + in_non_ucast_pkts) - (in_ucast_pkts_last + in_non_ucast_pkts_last)) * scale_factor + local tx_pps_new = ((out_ucast_pkts + out_non_ucast_pkts) - (out_ucast_pkts_last + out_non_ucast_pkts_last)) * scale_factor if initialized == "DONE" then -- Get old rates values @@ -83,6 +193,21 @@ local function compute_rate(port) redis.call('HSET', rates_table_name .. ':' .. port, 'TX_PPS', tx_pps_new) redis.call('HSET', state_table, 'INIT_DONE', 'DONE') end + + -- only do the calculation when all info present + + if fec_corr_bits and fec_uncorr_frames and lanes_count and serdes_speed then + local fec_corr_bits_last = redis.call('HGET', rates_table_name .. ':' .. port, 'SAI_PORT_STAT_IF_FEC_CORRECTED_BITS_last') + local fec_uncorr_frames_last = redis.call('HGET', rates_table_name .. ':' .. port, 'SAI_PORT_STAT_IF_FEC_NOT_CORRECTABLE_FARMES_last') + + local serdes_rate_total = lanes_count * serdes_speed * delta / 1000 + + fec_corr_bits_ber_new = (fec_corr_bits - fec_corr_bits_last) / serdes_rate_total + fec_uncorr_bits_ber_new = (fec_uncorr_frames - fec_uncorr_frames_last) * rs_average_frame_ber / serdes_rate_total + else + logit("FEC counters or lane info not found on " .. port) + end + else redis.call('HSET', state_table, 'INIT_DONE', 'COUNTERS_LAST') end @@ -94,6 +219,19 @@ local function compute_rate(port) redis.call('HSET', rates_table_name .. ':' .. port, 'SAI_PORT_STAT_IF_OUT_NON_UCAST_PKTS_last', out_non_ucast_pkts) redis.call('HSET', rates_table_name .. ':' .. port, 'SAI_PORT_STAT_IF_IN_OCTETS_last', in_octets) redis.call('HSET', rates_table_name .. ':' .. port, 'SAI_PORT_STAT_IF_OUT_OCTETS_last', out_octets) + + -- do not update FEC related stat if we dont have it + + if not fec_corr_bits or not fec_uncorr_frames or not fec_corr_bits_ber_new or + not fec_uncorr_bits_ber_new then + logit("FEC counters not found on " .. port) + return + end + -- Set BER values + redis.call('HSET', rates_table_name .. ':' .. port, 'SAI_PORT_STAT_IF_FEC_CORRECTED_BITS_last', fec_corr_bits) + redis.call('HSET', rates_table_name .. ':' .. port, 'SAI_PORT_STAT_IF_FEC_NOT_CORRECTABLE_FARMES_last', fec_uncorr_frames) + redis.call('HSET', rates_table_name .. ':' .. port, 'FEC_PRE_BER', fec_corr_bits_ber_new) + redis.call('HSET', rates_table_name .. ':' .. port, 'FEC_POST_BER', fec_uncorr_bits_ber_new) end local n = table.getn(KEYS)