From 336e4d0171d83595c588fd9fdcd7886de2d30257 Mon Sep 17 00:00:00 2001 From: Prabhat Aravind Date: Tue, 10 Sep 2024 18:45:56 +0000 Subject: [PATCH 1/3] [image_config]: Enable Receive Packet Steering (RPS) * This patch enables Receive Packet Steering (RPS) which helps to distribute softirq processing for CPU bound packets to all available cores. This will help prevent kernel packet drops when there are bouts of intense CPU bound packets. Ref: https://lwn.net/Articles/362339/ https://docs.kernel.org/networking/scaling.html Signed-off-by: Prabhat Aravind --- .../build_templates/sonic_debian_extension.j2 | 7 +- files/image_config/rps/rps.py | 110 ++++++++++++++++++ files/image_config/udev/rules.d/99-rps.rules | 1 + 3 files changed, 117 insertions(+), 1 deletion(-) create mode 100755 files/image_config/rps/rps.py create mode 100644 files/image_config/udev/rules.d/99-rps.rules diff --git a/files/build_templates/sonic_debian_extension.j2 b/files/build_templates/sonic_debian_extension.j2 index da7cdd8a2744..4887210ed2ba 100644 --- a/files/build_templates/sonic_debian_extension.j2 +++ b/files/build_templates/sonic_debian_extension.j2 @@ -513,8 +513,10 @@ sudo cp $IMAGE_CONFIGS/resolv-config/update-containers $FILESYSTEM_ROOT/etc/reso sudo cp $IMAGE_CONFIGS/interfaces/init_interfaces $FILESYSTEM_ROOT/etc/network/interfaces sudo mkdir -p $FILESYSTEM_ROOT/etc/network/interfaces.d -# System'd network udev rules +# Systemd network udev rules sudo cp $IMAGE_CONFIGS/systemd/network/* $FILESYSTEM_ROOT_ETC/systemd/network/ +sudo mkdir -p $FILESYSTEM_ROOT_ETC/udev/rules.d +sudo cp $IMAGE_CONFIGS/udev/rules.d/* $FILESYSTEM_ROOT_ETC/udev/rules.d/ # copy core file uploader files sudo cp $IMAGE_CONFIGS/corefile_uploader/core_uploader.service $FILESYSTEM_ROOT_USR_LIB_SYSTEMD_SYSTEM @@ -723,6 +725,9 @@ sudo cp $IMAGE_CONFIGS/backend_acl/backend-acl.service $FILESYSTEM_ROOT_USR_LIB_ sudo cp $IMAGE_CONFIGS/backend_acl/backend_acl.py $FILESYSTEM_ROOT/usr/bin/backend_acl.py echo "backend-acl.service" | sudo tee -a $GENERATED_SERVICE_FILE +# Copy RPS script file +sudo cp $IMAGE_CONFIGS/rps/rps.py $FILESYSTEM_ROOT/usr/bin/rps.py + # Copy SNMP configuration files sudo cp $IMAGE_CONFIGS/snmp/snmp.yml $FILESYSTEM_ROOT/etc/sonic/ diff --git a/files/image_config/rps/rps.py b/files/image_config/rps/rps.py new file mode 100755 index 000000000000..c70bf6581730 --- /dev/null +++ b/files/image_config/rps/rps.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 +""" + Script to enable Receive Packet Steering (RPS). + This script reads the number of available CPUs in the system + and configures RPS on all front-panel ports using sysfs. +""" +import os +import sys +import syslog + + +def write_syslog(message, *args): + """ + Write a message to syslog. + + Args: + message (str): Message string to be logged + args: Optional args + + Returns: + None + """ + + if args: + message %= args + syslog.syslog(syslog.LOG_NOTICE, message) + + +def get_num_cpus(): + """ + Get number of CPUs in the system. + + Returns: + ncpus (int): Number of CPUs + """ + ncpus = 0 + cpu_count = os.cpu_count() + if cpu_count is not None: + ncpus = cpu_count + + return ncpus + + +def get_cpumask(ncpus): + """ + Get a hex cpumask string. + + Args: + ncpus (int): Number of CPUs + + Returns: + cpu_mask (str): CPU mask as hex string + """ + cpu_mask = '0' + if isinstance(ncpus, int) and ncpus >= 0: + cpu_mask = hex(pow(2, ncpus) - 1)[2:] + + return cpu_mask + + +def configure_rps(): + """ + Configure Receive Packet Steering (RPS) + + Returns: + rv (int): zero for success and non-zero otherwise + """ + rv = 0 + NET_DIR_PATH = "/sys/class/net" + + num_cpus = get_num_cpus() + if not num_cpus: + rv = -1 + return rv + + cpumask = get_cpumask(num_cpus) + if cpumask == '0': + # Nothing to do + return rv + + write_syslog("configure_rps: cpu mask {}".format(cpumask)) + + # Loop through Ethernet interfaces in /sys/class/net and set rps_cpus + for intf in os.listdir(NET_DIR_PATH): + if "Ethernet" in intf: + queues_path = os.path.join(NET_DIR_PATH, intf, "queues") + queues = os.listdir(queues_path) + num_rx_queues = len([q for q in queues if q.startswith("rx")]) + for q in range(num_rx_queues): + rps_cpus_path = os.path.join(queues_path, "rx-{}", "rps_cpus").format(q) + with open(rps_cpus_path, 'w') as file: + file.write(cpumask) + + return rv + + +def main(): + rv = -1 + try: + rv = configure_rps() + write_syslog("configure_rps {}".format("failed" if rv else "successful")) + except Exception as e: + write_syslog("configure_rps exception: {}".format(str(e))) + + sys.exit(rv) + + +if __name__ == "__main__": + main() + diff --git a/files/image_config/udev/rules.d/99-rps.rules b/files/image_config/udev/rules.d/99-rps.rules new file mode 100644 index 000000000000..3764e29af2a5 --- /dev/null +++ b/files/image_config/udev/rules.d/99-rps.rules @@ -0,0 +1 @@ +ACTION=="add", SUBSYSTEM=="net", KERNEL=="Ethernet*", RUN+="/usr/bin/rps.py" From a604c821b635126eb7303a66a151c0afe7a0fdb4 Mon Sep 17 00:00:00 2001 From: Prabhat Aravind Date: Sat, 2 Nov 2024 04:47:41 +0000 Subject: [PATCH 2/3] fix script logic Make sure the script is run only once per interface Signed-off-by: Prabhat Aravind --- files/image_config/rps/rps.py | 31 ++++++++++---------- files/image_config/udev/rules.d/99-rps.rules | 2 +- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/files/image_config/rps/rps.py b/files/image_config/rps/rps.py index c70bf6581730..df1dbf5a3ccd 100755 --- a/files/image_config/rps/rps.py +++ b/files/image_config/rps/rps.py @@ -7,7 +7,7 @@ import os import sys import syslog - +import argparse def write_syslog(message, *args): """ @@ -58,7 +58,7 @@ def get_cpumask(ncpus): return cpu_mask -def configure_rps(): +def configure_rps(intf): """ Configure Receive Packet Steering (RPS) @@ -78,18 +78,13 @@ def configure_rps(): # Nothing to do return rv - write_syslog("configure_rps: cpu mask {}".format(cpumask)) - - # Loop through Ethernet interfaces in /sys/class/net and set rps_cpus - for intf in os.listdir(NET_DIR_PATH): - if "Ethernet" in intf: - queues_path = os.path.join(NET_DIR_PATH, intf, "queues") - queues = os.listdir(queues_path) - num_rx_queues = len([q for q in queues if q.startswith("rx")]) - for q in range(num_rx_queues): - rps_cpus_path = os.path.join(queues_path, "rx-{}", "rps_cpus").format(q) - with open(rps_cpus_path, 'w') as file: - file.write(cpumask) + queues_path = os.path.join(NET_DIR_PATH, intf, "queues") + queues = os.listdir(queues_path) + num_rx_queues = len([q for q in queues if q.startswith("rx")]) + for q in range(num_rx_queues): + rps_cpus_path = os.path.join(queues_path, "rx-{}", "rps_cpus").format(q) + with open(rps_cpus_path, 'w') as file: + file.write(cpumask) return rv @@ -97,8 +92,12 @@ def configure_rps(): def main(): rv = -1 try: - rv = configure_rps() - write_syslog("configure_rps {}".format("failed" if rv else "successful")) + parser = argparse.ArgumentParser(description='Configure RPS.') + parser.add_argument('interface', type=str, help='Network interface') + args = parser.parse_args() + rv = configure_rps(args.interface) + write_syslog("configure_rps {} for interface {}".format + ("failed" if rv else "successful", args.interface)) except Exception as e: write_syslog("configure_rps exception: {}".format(str(e))) diff --git a/files/image_config/udev/rules.d/99-rps.rules b/files/image_config/udev/rules.d/99-rps.rules index 3764e29af2a5..2e98cfe7e07d 100644 --- a/files/image_config/udev/rules.d/99-rps.rules +++ b/files/image_config/udev/rules.d/99-rps.rules @@ -1 +1 @@ -ACTION=="add", SUBSYSTEM=="net", KERNEL=="Ethernet*", RUN+="/usr/bin/rps.py" +ACTION=="add", SUBSYSTEM=="net", KERNEL=="Ethernet*", RUN+="/usr/bin/rps.py %k" From 241e27c9f97840f94db5b723113eb34acd45c4e5 Mon Sep 17 00:00:00 2001 From: Prabhat Aravind Date: Wed, 6 Nov 2024 19:50:26 +0000 Subject: [PATCH 3/3] change file permissions for rps.py Signed-off-by: Prabhat Aravind --- files/build_templates/sonic_debian_extension.j2 | 1 + 1 file changed, 1 insertion(+) diff --git a/files/build_templates/sonic_debian_extension.j2 b/files/build_templates/sonic_debian_extension.j2 index 322277055e5b..01230bf9edda 100644 --- a/files/build_templates/sonic_debian_extension.j2 +++ b/files/build_templates/sonic_debian_extension.j2 @@ -727,6 +727,7 @@ echo "backend-acl.service" | sudo tee -a $GENERATED_SERVICE_FILE # Copy RPS script file sudo cp $IMAGE_CONFIGS/rps/rps.py $FILESYSTEM_ROOT/usr/bin/rps.py +sudo chmod 755 $FILESYSTEM_ROOT/usr/bin/rps.py # Copy SNMP configuration files sudo cp $IMAGE_CONFIGS/snmp/snmp.yml $FILESYSTEM_ROOT/etc/sonic/