#!/bin/sh
#
# Static IRQ affinity (no irqbalance).
#
# Photonicat2 ships without irqbalance so cores can stay in CPU idle longer and
# the daemon does not rebalance every 10s. Pin eth IRQs to separate little
# cores once at device add instead: fixed, cheap, and enough for dual-GMAC.
# Masks are hex CPU bitmasks (2=CPU1, 4=CPU2, 8=CPU3).
#
# hotplug raises a net "add" for lo, every eth*, br-lan, wlan*, wwan* and tun*,
# and for all of them again on a network restart; hotplug-call drains that queue
# one script at a time. Walking the board table on each event, waiting for an
# IRQ that is not registered until the interface is opened, held the queue for
# up to 24s a time. So: latch the run to once per boot, and wait detached.

[ "$ACTION" = add ] || exit

# mkdir is atomic and /var/lock is tmpfs, so the first event through wins and
# every later one costs a single failed syscall.
mkdir /var/lock/net-smp-affinity 2>/dev/null || exit

get_device_irq() {
	local device="$1"
	local line
	local seconds="0"

	# the IRQ is only requested when the interface is brought up, well after
	# its netdev appears. Nothing is queued behind us now, so poll in small
	# steps over a window wide enough to cover ifup.
	while [ "${seconds}" -le 30 ]; do
		line=$(grep -E -m 1 "${device}\$" /proc/interrupts) && break
		seconds="$(( seconds + 1 ))"
		sleep 1
	done
	echo ${line} | sed 's/:.*//'
}

set_interface_core() {
	local core_mask="$1"
	local interface="$2"
	local device="$3"

	[ -z "${device}" ] && device="$interface"

	local irq=$(get_device_irq "$device")
	[ -n "$irq" ] || return 0
	[ -w "/proc/irq/${irq}/smp_affinity" ] || return 0

	echo "${core_mask}" > /proc/irq/${irq}/smp_affinity
}

set_board_affinity() {
	case "$(board_name)" in
# Photonicat2 (RK3576): eth0=WAN, eth1=LAN on little cores; leave big cores free.
ariaboard,photonicat2)
	set_interface_core 2 "eth0"
	set_interface_core 4 "eth1"
	;;
# Photonicat v1 (RK3568): same dual-GMAC layout.
ariaboard,photonicat|\
ariaboard,rk3568-photonicat)
	set_interface_core 2 "eth0"
	set_interface_core 4 "eth1"
	;;
armsom,sige1|\
armsom,sige3|\
armsom,sige5|\
armsom,sige7|\
hinlink,opc-h28k|\
radxa,e20c|\
widora,mangopi-m28k|\
widora,mangopi-m28k-pro)
	set_interface_core 4 "eth0"
	;;
friendlyarm,nanopi-r3s|\
friendlyarm,nanopi-r5c|\
sinovoip,rk3568-bpi-r2pro)
	set_interface_core 2 "eth0"
	set_interface_core 4 "eth1"
	;;
friendlyarm,nanopi-r2c|\
friendlyarm,nanopi-r2c-plus|\
friendlyarm,nanopi-r2s|\
friendlyarm,nanopi-r2s-plus|\
xunlong,orangepi-r1-plus|\
xunlong,orangepi-r1-plus-lts)
	set_interface_core 2 "eth0"
	set_interface_core 4 "eth1" "xhci-hcd:usb[0-9]+"
	;;
friendlyarm,nanopi-r4s|\
friendlyarm,nanopi-r4se|\
friendlyarm,nanopi-r6c|\
friendlyarm,nanopc-t6|\
sharevdi,guangmiao-g4c|\
xunlong,orangepi-5-plus)
	set_interface_core 10 "eth0"
	set_interface_core 20 "eth1"
	;;
friendlyarm,nanopi-r5s)
	set_interface_core 2 "eth0"
	set_interface_core 4 "eth1"
	set_interface_core 8 "eth2"
	;;
friendlyarm,nanopi-r6s)
	set_interface_core 10 "eth0"
	set_interface_core 20 "eth1"
	set_interface_core 40 "eth2"
	;;
widora,mangopi-m28c)
	set_interface_core 4 "eth0"
	set_interface_core 8 "xhci-hcd:usb1"
	;;
	esac
}

# detached: hotplug-call must not wait out the polling above
set_board_affinity >/dev/null 2>&1 &

