diff --git a/scripts/tempesta.sh b/scripts/tempesta.sh index c2770abd39..eeffcf962f 100755 --- a/scripts/tempesta.sh +++ b/scripts/tempesta.sh @@ -43,7 +43,11 @@ tdb_mod=tempesta_db tfw_mod=tempesta_fw declare -r LONG_OPTS="help,load,unload,start,stop,restart,reload" -declare devs=$(ip addr show up | awk '/^[0-9]+/ { sub(/:/, "", $2); print $2}') +# Exclude loopback interface since it needn't any tuning here: it hasn't RSS +# while RPS just add unnecessary overhead for it (traffic redistribution, IPIs +# introduction etc.). +declare devs=$(ip addr show up | grep -P '^[0-9]+' | grep -Pv '\bLOOPBACK\b' \ + | awk '{ sub(/:/, "", $2); print $2}') usage() { @@ -204,6 +208,8 @@ stop() echo "...unload Tempesta modules" unload_modules + tfw_irqbalance_revert + echo "done" } diff --git a/scripts/tfw_lib.sh b/scripts/tfw_lib.sh index e85b5313b8..9da003f363 100644 --- a/scripts/tfw_lib.sh +++ b/scripts/tfw_lib.sh @@ -27,36 +27,100 @@ declare -r TFW_NAME=`basename $0` # program name (comm name in ps) declare -r TFW_NETDEV_PATH="/sys/class/net/" declare -r TFW_SCRIPTS="$TFW_ROOT/scripts" declare -r CPUS_N=$(grep -c processor /proc/cpuinfo) +declare -r IRQB_CONF_PATH="/etc/default/irqbalance" +declare -r SYSD_IRQB_PATH="/lib/systemd/system/irqbalance.service" +declare -r BAN_CONF_VAR="TFW_BAN_IRQS" +declare -a IRQS_GLOB_LIST calc() { echo "$1" | bc -iq | tail -1 } +# Assigned IRQs can be reassigned by irqbalance. To avoid this, assigned +# vectors should be added to irqbalance config with '--banirq' option. +irqbalance_ban_irqs() +{ + args_str="" + sysd_conf_var="ExecStart" + + echo "...ban IRQs for irqbalance..." + for irq in ${IRQS_GLOB_LIST[@]}; do + args_str=$args_str"--banirq=$irq " + done + + conf_var_str="$BAN_CONF_VAR=$args_str" + perl -i.orig -ple ' + if (/^'"$BAN_CONF_VAR"'=.*$/) { + $_ = "'"$conf_var_str"'"; + $found = 1; + } + END { + unless ($found) { + open(CONFIG, ">>", $ARGV); + select(CONFIG); + print("\n# Do not edit this variable. It is" + . " automatically generated by start\n#" + . " script of Tempesta FW and is intended" + . " to avoid reassigning of specified" + . " IRQs."); + printf("%s\n", "'"$conf_var_str"'"); + select(STDOUT); + } + } + ' $IRQB_CONF_PATH + + perl -i.orig -ple ' + if (/^('"$sysd_conf_var"'=.*)$/) { + unless (/.*\$'"$BAN_CONF_VAR"'\b.*/) { + $_ = $1 . " \$'"$BAN_CONF_VAR"'"; + } + } + ' $SYSD_IRQB_PATH + + systemctl daemon-reload >/dev/null + systemctl restart irqbalance.service >/dev/null +} + distribute_queues() { dev=$1 - RXQ_MAX=$(ethtool -l $dev 2>/dev/null \ - | grep -m 1 RX | sed -e 's/RX\:\s*//') - - echo "...distribute $dev queues" - - if [ -n "$RXQ_MAX" -a ${RXQ_MAX:-0} -gt 0 ]; then - echo "...set rx channels to $RXQ_MAX, please wait..." - # Set maximum number of available channels for better - # packets hashing. - ethtool -L $dev rx $RXQ_MAX >/dev/null 2>&1 - # Wait for the interface reconfiguration. - opstate="$TFW_NETDEV_PATH/$dev/operstate" - while [ "$(cat $opstate)" = "down" ]; do - sleep 1 - done - else - echo "...0 channels for $dev - skip" + RXQ_MAX=$2 + + echo "...set rx channels to $RXQ_MAX, please wait..." + # Set maximum number of available channels for better + # packets hashing. + res=$(ethtool -L $dev rx $RXQ_MAX 2>&1) + if [ $? -ne 0 -a -z "$(echo $res | grep -P '^rx unmodified, ignoring')" ] + then + printf "Error: cannot set new queues count for %s:\n %s\n" \ + $dev "$res" return fi + # Wait for the interface reconfiguration. + opstate="$TFW_NETDEV_PATH/$dev/operstate" + while [ "$(cat $opstate)" = "down" ]; do + sleep 1 + done + + # Interrupts may not have interface-like description in + # '/proc/interrupts' - so, to find the vectors we also need + # to check the MSI directory for device. + dev_irqs_path="/sys/class/net/$dev/device/msi_irqs" irqs=($(grep $dev /proc/interrupts | sed -e 's/\s*\|:.*//g')) + if [ -z "$irqs" -a -d $dev_irqs_path ]; then + irqs=($(ls $dev_irqs_path)) + fi + + if [ -z "$irqs" ]; then + echo "Error: cannot find interrupts for $dev" + return + fi + + # Skip the first IRQ since this is general async interrupt + # for device (not assigned to any of the queues). + irqs=(${irqs[@]:1}) irq0=${irqs[0]} for i in ${irqs[@]}; do # Wrap around CPU mask if number of queues is @@ -74,11 +138,12 @@ distribute_queues() } ' > /proc/irq/$i/smp_affinity done + + IRQS_GLOB_LIST+=(${irqs[@]}) } -# Enable RPS for specified, or all by default, networking interfaces. -# This is required for loopback interface for proper local delivery, -# but physical interfaces can have RSS. +# Enable RSS for networking interfaces. Enable RPS for those devices which +# doesn't have enough hardware queues. tfw_set_net_queues() { devs=$1 @@ -86,21 +151,39 @@ tfw_set_net_queues() cpu_mask=$(perl -le 'printf("%x", (1 << '$CPUS_N') - 1)') for dev in $devs; do - queues=$(ls -d /sys/class/net/$dev/queues/rx-* | wc -l) - if [ $queues -le $min_queues ]; then - echo "...enable RPS on $dev" - for rx in $TFW_NETDEV_PATH/$dev/queues/rx-*; do - echo $cpu_mask > $rx/rps_cpus - done - else - + queues=$(ethtool -l $dev 2>/dev/null \ + | grep -m 1 RX | sed -e 's/RX\:\s*//') + if [ -n "$queues" -a ${queues:-0} -gt $min_queues ]; then # Switch off RPS for multi-queued interfaces. for rx in $TFW_NETDEV_PATH/$dev/queues/rx-*; do echo 0 > $rx/rps_cpus done - distribute_queues $dev + echo "...distribute $dev queues" + distribute_queues $dev $queues + else + echo "...enable RPS on $dev" + for rx in $TFW_NETDEV_PATH/$dev/queues/rx-*; do + echo $cpu_mask > $rx/rps_cpus + done fi done + + if [ ${#IRQS_GLOB_LIST[@]} -ne 0 -a -f $SYSD_IRQB_PATH \ + -a -f $IRQB_CONF_PATH ]; then + systemctl status irqbalance.service >/dev/null + [ $? -ne 0 ] || irqbalance_ban_irqs + fi } +tfw_irqbalance_revert() +{ + systemctl status irqbalance.service >/dev/null + if [ $? -eq 0 -a -f $IRQB_CONF_PATH ]; then + echo "...revert irqbalance config" + perl -i.orig -ple ' + s/^('"$BAN_CONF_VAR"'=).*$/$1/; + ' $IRQB_CONF_PATH + systemctl restart irqbalance.service >/dev/null + fi +}