src/functions/functions.hostapd \
src/functions/functions.hotplug \
src/functions/functions.http \
+ src/functions/functions.interrupts \
src/functions/functions.ip \
src/functions/functions.iptables \
src/functions/functions.ip-tunnel \
src/functions/functions.settings \
src/functions/functions.stp \
src/functions/functions.sysctl \
+ src/functions/functions.system \
src/functions/functions.triggers \
src/functions/functions.usb \
src/functions/functions.util \
man/network-device.8 \
man/network-dhcp.8 \
man/network-dns-server.8 \
+ man/network-performance-tuning.8 \
man/network-port.8 \
man/network-port-batman-adv.8 \
man/network-port-batman-adv-port.8 \
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS/DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<refentry id="network-performance-tuning">
+ <refentryinfo>
+ <title>network-performance-tuning</title>
+ <productname>network</productname>
+
+ <authorgroup>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Michael</firstname>
+ <surname>Tremer</surname>
+ <email>michael.tremer@ipfire.org</email>
+ </author>
+ </authorgroup>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>network-performance-tuning</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>network-performance-tuning</refname>
+ <refpurpose>Network Configuration Control Program</refpurpose>
+ </refnamediv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ This page contains a summary of some performance tuning techniques
+ that this system is using.
+ </para>
+ </refsect1>
+
+ <refsect2>
+ <title>SMP Affinity</title>
+
+ <para>
+ This system is automatically using SMP affinity for every physical
+ network controller, if supported.
+ </para>
+
+ <para>
+ A processor core is assigned to handle all interrupts of a certain
+ network controller which will result in minimising cache misses,
+ reducing network latency and quite possibly increasing throughput.
+ </para>
+
+ <para>
+ The algorithm is trying to balance all network controllers across
+ all processors.
+ </para>
+
+ <para>
+ See /proc/interrups for the distribution of interrupts.
+ </para>
+ </refsect2>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <para>
+ <citerefentry>
+ <refentrytitle>network</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </citerefentry>
+ </para>
+ </refsect1>
+</refentry>
<refentrytitle>network-dns-server</refentrytitle>
<manvolnum>8</manvolnum>
</citerefentry>,
+ <citerefentry>
+ <refentrytitle>network-performance-tuning</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </citerefentry>,
<citerefentry>
<refentrytitle>network-port</refentrytitle>
<manvolnum>8</manvolnum>
log DEBUG "Setting up device '${device}'"
ip link set ${device} up
+
+ # Set SMP affinity
+ if interrupt_use_smp_affinity; then
+ device_auto_configure_smp_affinity "${port}"
+ fi
+
+ return ${EXIT_OK}
}
device_set_parent_up() {
print "${s}"
}
+
+device_auto_configure_smp_affinity() {
+ assert [ $# -eq 1 ]
+
+ local device=${1}
+
+ if lock_acquire "smp-affinity"; then
+ device_set_smp_affinity "${port}" auto
+
+ lock_release "smp-affinity"
+ fi
+}
+
+device_set_smp_affinity() {
+ assert [ $# -eq 2 ]
+
+ local device=${1}
+ local mode=${2}
+
+ # mode can be auto which will automatically try to find
+ # the least busy processor, or an integer for the desired
+ # processor that should handle this device
+
+ local num_processors=$(system_get_processors)
+
+ if [ "${mode}" = "auto" ]; then
+ local processor=$(interrupt_choose_least_busy_processor)
+ else
+ assert isinteger mode
+ local processor=${mode}
+
+ if [ ${processor} -gt ${num_processors} ]; then
+ log ERROR "Processor ${processor} does not exist"
+ return ${EXIT_ERROR}
+ fi
+ fi
+
+ local interrupts=$(interrupts_for_device ${device})
+ if ! isset interrupts; then
+ log DEBUG "${device} has no interrupts. Not changing SMP affinity"
+ return ${EXIT_OK}
+ fi
+
+ # Set SMP affinity
+ local interrupt
+ for interrupt in ${interrupts}; do
+ interrupt_set_smp_affinity ${interrupt} ${processor}
+ done
+
+ # Find all queues and assign them to the next processor
+ local queue
+ for queue in $(device_get_queues ${device}); do
+ case "${queue}" in
+ # Only handle receive queues
+ rx-*)
+ for interrupt in $(interrupts_for_device_queue ${device} ${queue}); do
+ interrupt_set_smp_affinity ${interrupt} ${processor}
+ done
+
+ device_queue_set_smp_affinity ${device} ${queue} ${processor}
+ ;;
+
+ # Ignore the rest
+ *)
+ continue
+ ;;
+ esac
+
+ # Get the next available processor if in auto mode
+ [ "${mode}" = "auto" ] && processor=$(system_get_next_processor ${processor})
+ done
+
+ return ${EXIT_OK}
+}
+
+device_get_queues() {
+ assert [ $# -eq 1 ]
+
+ local device=${1}
+
+ local queue
+ for queue in ${SYS_CLASS_NET}/${device}/queues/*; do
+ basename "${queue}"
+ done
+}
+
+device_queue_set_smp_affinity() {
+ assert [ $# -eq 3 ]
+
+ local device=${1}
+ local queue=${2}
+ local processor=${3}
+
+ local path="${SYS_CLASS_NET}/${device}/queues/${queue}/rps_cpus"
+ assert [ -w "${path}" ]
+
+ log DEBUG "Setting SMP affinity of ${device} (${queue}) to processor ${processor}"
+
+ __processor_id_to_bitmap ${processor} > ${path}
+}
--- /dev/null
+#!/bin/bash
+###############################################################################
+# #
+# IPFire.org - A linux based firewall #
+# Copyright (C) 2016 IPFire Network Development Team #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# 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, see <http://www.gnu.org/licenses/>. #
+# #
+###############################################################################
+
+interrupts_list() {
+ local interrupt
+ for interrupt in /proc/irq/*; do
+ [ -d "${interrupt}" ] || continue
+
+ basename "${interrupt}"
+ done
+
+ return ${EXIT_OK}
+}
+
+interrupt_use_smp_affinity() {
+ local processors=$(system_get_processors)
+
+ # There is no point in this feature when there is only one processor
+ [ ${processors} -eq 1 ] && return ${EXIT_FALSE}
+
+ return ${EXIT_TRUE}
+}
+
+__interrupts_for() {
+ local path=${1}
+
+ local f
+ for f in ${path}; do
+ [ -d "${f}" ] || continue
+
+ local interrupt=$(dirname ${f})
+ basename ${interrupt}
+ done
+}
+
+interrupts_for_device() {
+ assert [ $# -eq 1 ]
+
+ local device=${1}
+
+ __interrupts_for "/proc/irq/*/${device}"
+}
+
+interrupts_for_device_queue() {
+ assert [ $# -eq 2 ]
+
+ local device="${1}"
+ local queue="${2}"
+
+ __interrupts_for "/proc/irq/*/${device}-[rt]x${queue}"
+}
+
+interrupt_get_smp_affinity() {
+ assert [ $# -eq 1 ]
+
+ local interrupt=${1}
+
+ local path="/proc/irq/${interrupt}/smp_affinity"
+ assert [ -r "${path}" ]
+
+ # Convert bitmap to list of processors
+ __bitmap_to_processor_id $(<${path})
+}
+
+__bitmap_to_processor_id() {
+ local bitmap=${1}
+
+ # This function shifts the bit map to the right
+ # and if the least significant bit equals one
+ # the index of that bit is returned.
+
+ local id=0
+ while [ $(( 0x${bitmap} )) -gt 0 ]; do
+ if [ $(( 0x${bitmap} & 0x1 )) -eq 1 ]; then
+ print "${id}"
+ fi
+
+ bitmap=$(( 0x${bitmap} >> 1 ))
+ ((id++))
+ done
+}
+
+__processor_id_to_bitmap() {
+ hex $(( 1 << $@ ))
+}
+
+interrupt_set_smp_affinity() {
+ assert [ $# -eq 2 ]
+
+ local interrupt=${1}
+ local processor=${2}
+
+ # Processor ID must be greater or equal than zero
+ # and not larger than the highest processor index
+ local num_processors=$(system_get_processors)
+ if [ ${processor} -ge ${num_processors} ]; then
+ error "Invalid processor ID ${processor}"
+ return ${EXIT_ERROR}
+ fi
+
+ local path="/proc/irq/${interrupt}/smp_affinity"
+ assert [ -w "${path}" ]
+
+ log DEBUG "Setting SMP affinity for interrupt ${interrupt} to processor ${processor}"
+
+ # Write processor ID as hex value
+ __processor_id_to_bitmap ${processor} > ${path}
+}
+
+interrupt_choose_least_busy_processor() {
+ local processors=$(system_get_processors)
+ local -a interrupts
+
+ # Create an array with the number of interrupts
+ # already handled by each processor
+
+ local i
+ for i in $(range ${processors}); do
+ interrupts[${i}]=0
+ done
+
+ local processor interrupt
+ for interrupt in $(interrupts_list); do
+ for processor in $(interrupt_get_smp_affinity ${interrupt}); do
+ interrupts[${processor}]=$(( ${interrupts[${processor}]} + 1 ))
+ done
+ done
+
+ # Walk through that map and find the first processor with the
+ # smallest number of interrupts handled so far
+
+ local least_busy_index=0
+ for i in $(range ${processors}); do
+ if [ ${interrupts[${least_busy_index}]} -gt ${interrupts[${i}]} ]; then
+ least_busy_index=${i}
+ fi
+ done
+
+ print "${least_busy_index}"
+}
--- /dev/null
+#!/bin/bash
+###############################################################################
+# #
+# IPFire.org - A linux based firewall #
+# Copyright (C) 2016 IPFire Network Development Team #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# 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, see <http://www.gnu.org/licenses/>. #
+# #
+###############################################################################
+
+_getconf() {
+ getconf $@
+}
+
+# Returns the number of online processors
+system_get_processors() {
+ _getconf _NPROCESSORS_ONLN
+}
+
+system_get_next_processor() {
+ assert [ $# -eq 1 ]
+
+ local processor=${1}
+ local processors=$(system_get_processors)
+
+ # Pick the next one
+ print $(( (${processor} + 1) % ${processors} ))
+}
fi
}
+range() {
+ eval echo {0..$(( ${1} - 1 ))}
+}
+
+count() {
+ local i=0
+
+ while read; do
+ ((i++))
+ done
+
+ echo ${i}
+}
+
which() {
type -P $@
}