diff --git a/src/libbpftune.c b/src/libbpftune.c index 73b48b0..4a801de 100644 --- a/src/libbpftune.c +++ b/src/libbpftune.c @@ -900,6 +900,8 @@ int bpftune_sysctl_read(int netns_fd, const char *name, long *values) out: bpftune_netns_set(orig_netns_fd, NULL, true); out_unset: + if (orig_netns_fd) + close(orig_netns_fd); bpftune_cap_drop(); return err ? err : num_values; } @@ -957,6 +959,8 @@ int bpftune_sysctl_write(int netns_fd, const char *name, __u8 num_values, long * out: bpftune_netns_set(orig_netns_fd, NULL, true); out_unset: + if (orig_netns_fd) + close(orig_netns_fd); bpftune_cap_drop(); return err; } @@ -1311,10 +1315,12 @@ int bpftune_netns_info(int pid, int *fd, unsigned long *cookie) } else { bpftune_log(LOG_DEBUG, "setns failed for for fd %d\n", netns_fd); - ret = -errno; + ret = err; + } + if (fdnew) { + if (ret || !fd) + close(netns_fd); } - if (fdnew && !fd) - close(netns_fd); if (orig_netns_fd > 0) close(orig_netns_fd); return ret; @@ -1382,7 +1388,7 @@ static int bpftune_netns_find(unsigned long cookie) } while ((dirent = readdir(dir)) != NULL) { char *endptr; - int netns_fd; + int netns_fd = 0; long pid; pid = strtol(dirent->d_name, &endptr, 10); diff --git a/test/Makefile b/test/Makefile index 94fc1e1..f7cf6ab 100644 --- a/test/Makefile +++ b/test/Makefile @@ -24,6 +24,7 @@ TUNER_TESTS = support_test log_test service_test inotify_test cap_test \ sample_test sample_legacy_test \ strategy_test strategy_legacy_test \ rollback_test rollback_legacy_test \ + many_netns_test many_netns_legacy_test \ sysctl_test sysctl_legacy_test sysctl_netns_test \ netns_test netns_legacy_test \ backlog_test backlog_legacy_test \ diff --git a/test/many_netns_legacy_test.sh b/test/many_netns_legacy_test.sh new file mode 100644 index 0000000..a6503b8 --- /dev/null +++ b/test/many_netns_legacy_test.sh @@ -0,0 +1,159 @@ +#!/usr/bin/bash +# +# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# +# Copyright (c) 2023, Oracle and/or its affiliates. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public +# License v2 as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this program; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 021110-1307, USA. +# + +# run iperf3 test with low wmem max, ensure tuner increases it while +# there are many netns set up; ensure no fd leaks + +PORT=5201 + +. ./test_lib.sh + +SLEEPTIME=1 +TIMEOUT=30 + +LSOF=$(which lsof 2>/dev/null) +if [[ -z "$LSOF" ]]; then + echo "lsof not available, skipping..." + exit 0 +fi + +for i in $(seq 1 1000); do + ip netns add ${NETNS_PREFIX}-extra${i} +done + + +for FAMILY in ipv4 ipv6 ; do + + for CLIENT_OPTS in "" "-R" ; do + case $FAMILY in + ipv4) + ADDR=$VETH1_IPV4 + ;; + ipv6) + ADDR=$VETH1_IPV6 + ;; + esac + + test_start "$0|wmem test to $ADDR:$PORT $FAMILY opts $CLIENT_OPTS $LATENCY" + + wmem_orig=($(sysctl -n net.ipv4.tcp_wmem)) + + test_setup true + + wmem_orig_netns=($(ip netns exec $NETNS sysctl -n net.ipv4.tcp_wmem)) + + sysctl -w net.ipv4.tcp_wmem="${wmem_orig[0]} ${wmem_orig[1]} ${wmem_orig[1]}" + ip netns exec $NETNS sysctl -w net.ipv4.tcp_wmem="${wmem_orig_netns[0]} ${wmem_orig_netns[1]} ${wmem_orig_netns[1]}" + + declare -A results + for MODE in baseline test ; do + + echo "Running ${MODE}..." + test_run_cmd_local "ip netns exec $NETNS $IPERF3 -s -p $PORT -1 &" + if [[ $MODE != "baseline" ]]; then + test_run_cmd_local "$BPFTUNE -L&" + sleep $SETUPTIME + fds_orig=$($LSOF -p $(pgrep bpftune) 2>/dev/null|wc -l) + else + LOGSZ=$(wc -l $LOGFILE | awk '{print $1}') + LOGSZ=$(expr $LOGSZ + 1) + fi + test_run_cmd_local "$IPERF3 -fm $CLIENT_OPTS -p $PORT -c $ADDR" true + + sleep $SLEEPTIME + + sresults=$(grep -E "sender" ${CMDLOG} | awk '{print $7}') + rresults=$(grep -E "receiver" ${CMDLOG} | awk '{print $7}') + units=$(grep -E "sender|receiver" ${CMDLOG} | awk '{print $8}' |head -1) + + if [[ $MODE == "baseline" ]]; then + read -r -a sbaseline_results <<< $sresults + read -r -a rbaseline_results <<< $rresults + echo "" > ${CMDLOG} + else + read -r -a stest_results <<< $sresults + read -r -a rtest_results <<< $rresults + + fi + sleep $SLEEPTIME + done + + wmem_post=($(sysctl -n net.ipv4.tcp_wmem)) + wmem_post_netns=($(ip netns exec $NETNS sysctl -n net.ipv4.tcp_wmem)) + sysctl -w net.ipv4.tcp_wmem="${wmem_orig[0]} ${wmem_orig[1]} ${wmem_orig[2]}" + if [[ $MODE == "test" ]]; then + if [[ "${wmem_post[2]}" -gt ${wmem_orig[1]} ]]; then + echo "wmem before ${wmem_orig[1]} ; after ${wmem_post[2]}" + + if [[ "${wmem_post_netns[2]}" -gt ${wmem_orig_netns[1]} ]]; then + echo "netns wmem before ${wmem_orig_netns[1]} ; after ${wmem_post_netns[2]}" + else + echo "netns wmem before ${wmem_orig_netns[1]} ; after ${wmem_post_netns[2]}" + if [[ ${BPFTUNE_NETNS} -eq 0 ]]; then + echo "bpftune does not support per-netns policy, skipping..." + else + test_cleanup + fi + fi + else + test_cleanup + fi + fi + printf "Results sender (${units}): " + for (( i=0; i < ${#sbaseline_results[@]}; i++ )) + do + sbase=$(roundup ${sbaseline_results[$i]}) + stest=$(roundup ${stest_results[$i]}) + if [[ ${sbase} -gt ${stest} ]]; then + bold "Warning: baseline (${sbase}) > test (${stest})" + else + echo "baseline (${sbase}) < test (${stest})" + fi + done + printf "Results receiver (${units}): " + for (( i=0; i < ${#rbaseline_results[@]}; i++ )) + do + rbase=$(roundup ${rbaseline_results[$i]}) + rtest=$(roundup ${rtest_results[$i]}) + if [[ ${rbase} -gt ${rtest} ]]; then + bold "Warning: baseline (${rbase}) > test (${rtest})" + else + echo "baseline (${rbase}) < test (${rtest})" + fi + done + + fds=$($LSOF -p $(pgrep bpftune) 2>/dev/null|wc -l) + # if we have 20 more than the original number of fds open, likely a leak + fdsX=${fds_orig}+20 + if [[ "$fds" -gt $fdsX ]]; then + echo "bpftune has $fds open versus original $fds_orig; fd leak? files:" + $LSOF -p $(pgrep bpftune) + test_cleanup + fi + echo "found $fds fds open for bpftune" + + test_pass + + test_cleanup + done +done + +test_exit diff --git a/test/many_netns_test.sh b/test/many_netns_test.sh new file mode 100644 index 0000000..22860d1 --- /dev/null +++ b/test/many_netns_test.sh @@ -0,0 +1,159 @@ +#!/usr/bin/bash +# +# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# +# Copyright (c) 2023, Oracle and/or its affiliates. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public +# License v2 as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this program; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 021110-1307, USA. +# + +# run iperf3 test with low wmem max, ensure tuner increases it while +# there are many netns set up; ensure no fd leaks + +PORT=5201 + +. ./test_lib.sh + +SLEEPTIME=1 +TIMEOUT=30 + +LSOF=$(which lsof 2>/dev/null) +if [[ -z "$LSOF" ]]; then + echo "lsof not available, skipping..." + exit 0 +fi + +for i in $(seq 1 1000); do + ip netns add ${NETNS_PREFIX}-extra${i} +done + + +for FAMILY in ipv4 ipv6 ; do + + for CLIENT_OPTS in "" "-R" ; do + case $FAMILY in + ipv4) + ADDR=$VETH1_IPV4 + ;; + ipv6) + ADDR=$VETH1_IPV6 + ;; + esac + + test_start "$0|wmem test to $ADDR:$PORT $FAMILY opts $CLIENT_OPTS $LATENCY" + + wmem_orig=($(sysctl -n net.ipv4.tcp_wmem)) + + test_setup true + + wmem_orig_netns=($(ip netns exec $NETNS sysctl -n net.ipv4.tcp_wmem)) + + sysctl -w net.ipv4.tcp_wmem="${wmem_orig[0]} ${wmem_orig[1]} ${wmem_orig[1]}" + ip netns exec $NETNS sysctl -w net.ipv4.tcp_wmem="${wmem_orig_netns[0]} ${wmem_orig_netns[1]} ${wmem_orig_netns[1]}" + + declare -A results + for MODE in baseline test ; do + + echo "Running ${MODE}..." + test_run_cmd_local "ip netns exec $NETNS $IPERF3 -s -p $PORT -1 &" + if [[ $MODE != "baseline" ]]; then + test_run_cmd_local "$BPFTUNE &" + sleep $SETUPTIME + fds_orig=$($LSOF -p $(pgrep bpftune) 2>/dev/null|wc -l) + else + LOGSZ=$(wc -l $LOGFILE | awk '{print $1}') + LOGSZ=$(expr $LOGSZ + 1) + fi + test_run_cmd_local "$IPERF3 -fm $CLIENT_OPTS -p $PORT -c $ADDR" true + + sleep $SLEEPTIME + + sresults=$(grep -E "sender" ${CMDLOG} | awk '{print $7}') + rresults=$(grep -E "receiver" ${CMDLOG} | awk '{print $7}') + units=$(grep -E "sender|receiver" ${CMDLOG} | awk '{print $8}' |head -1) + + if [[ $MODE == "baseline" ]]; then + read -r -a sbaseline_results <<< $sresults + read -r -a rbaseline_results <<< $rresults + echo "" > ${CMDLOG} + else + read -r -a stest_results <<< $sresults + read -r -a rtest_results <<< $rresults + + fi + sleep $SLEEPTIME + done + + wmem_post=($(sysctl -n net.ipv4.tcp_wmem)) + wmem_post_netns=($(ip netns exec $NETNS sysctl -n net.ipv4.tcp_wmem)) + sysctl -w net.ipv4.tcp_wmem="${wmem_orig[0]} ${wmem_orig[1]} ${wmem_orig[2]}" + if [[ $MODE == "test" ]]; then + if [[ "${wmem_post[2]}" -gt ${wmem_orig[1]} ]]; then + echo "wmem before ${wmem_orig[1]} ; after ${wmem_post[2]}" + + if [[ "${wmem_post_netns[2]}" -gt ${wmem_orig_netns[1]} ]]; then + echo "netns wmem before ${wmem_orig_netns[1]} ; after ${wmem_post_netns[2]}" + else + echo "netns wmem before ${wmem_orig_netns[1]} ; after ${wmem_post_netns[2]}" + if [[ ${BPFTUNE_NETNS} -eq 0 ]]; then + echo "bpftune does not support per-netns policy, skipping..." + else + test_cleanup + fi + fi + else + test_cleanup + fi + fi + printf "Results sender (${units}): " + for (( i=0; i < ${#sbaseline_results[@]}; i++ )) + do + sbase=$(roundup ${sbaseline_results[$i]}) + stest=$(roundup ${stest_results[$i]}) + if [[ ${sbase} -gt ${stest} ]]; then + bold "Warning: baseline (${sbase}) > test (${stest})" + else + echo "baseline (${sbase}) < test (${stest})" + fi + done + printf "Results receiver (${units}): " + for (( i=0; i < ${#rbaseline_results[@]}; i++ )) + do + rbase=$(roundup ${rbaseline_results[$i]}) + rtest=$(roundup ${rtest_results[$i]}) + if [[ ${rbase} -gt ${rtest} ]]; then + bold "Warning: baseline (${rbase}) > test (${rtest})" + else + echo "baseline (${rbase}) < test (${rtest})" + fi + done + + fds=$($LSOF -p $(pgrep bpftune) 2>/dev/null|wc -l) + # if we have 20 more than the original number of fds open, likely a leak + fdsX=${fds_orig}+20 + if [[ "$fds" -gt $fdsX ]]; then + echo "bpftune has $fds open versus original $fds_orig; fd leak? files:" + $LSOF -p $(pgrep bpftune) + test_cleanup + fi + echo "found $fds fds open for bpftune" + + test_pass + + test_cleanup + done +done + +test_exit diff --git a/test/test_lib.sh b/test/test_lib.sh index 4b270f8..bfeb5c2 100644 --- a/test/test_lib.sh +++ b/test/test_lib.sh @@ -215,7 +215,7 @@ test_setup_local() sysctl -qw net.ipv6.conf.all.disable_ipv6=0 if [[ $FOUND -ne 0 ]]; then ip netns pids $NETNS 2>/dev/null| xargs -r kill - ip netns del $NETNS 2>/dev/null|true + ip --all netns del ${NETNS_PREFIX}\* 2>/dev/null|true sleep 0.2 ip netns add $NETNS ip link add dev $VETH1 mtu $MTU netns $NETNS type veth \