|
|
@@ -0,0 +1,200 @@
|
|
|
+#!/usr/bin/env ucode
|
|
|
+'use strict';
|
|
|
+import { glob, basename, dirname, readlink, readfile, realpath, writefile, error, open } from "fs";
|
|
|
+
|
|
|
+let napi_weight = 1.0;
|
|
|
+let cpu_thread_weight = 0.75;
|
|
|
+let rx_weight = 0.75;
|
|
|
+let eth_bias = 2.0;
|
|
|
+let debug = 0, do_nothing = 0;
|
|
|
+let disable;
|
|
|
+let cpus;
|
|
|
+
|
|
|
+for (let arg in ARGV) {
|
|
|
+ switch (arg) {
|
|
|
+ case "-d":
|
|
|
+ debug++;
|
|
|
+ break;
|
|
|
+ case "-n":
|
|
|
+ do_nothing++;
|
|
|
+ break;
|
|
|
+ case '0':
|
|
|
+ disable = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function task_name(pid)
|
|
|
+{
|
|
|
+ let stat = open(`/proc/${pid}/status`, "r");
|
|
|
+ let line = stat.read("line");
|
|
|
+ stat.close();
|
|
|
+ return trim(split(line, "\t", 2)[1]);
|
|
|
+}
|
|
|
+
|
|
|
+function set_task_cpu(pid, cpu) {
|
|
|
+ if (disable)
|
|
|
+ cpu = join(",", map(cpus, (cpu) => cpu.id));
|
|
|
+ if (debug || do_nothing)
|
|
|
+ warn(`taskset -p -c ${cpu} ${task_name(pid)}\n`);
|
|
|
+ if (!do_nothing)
|
|
|
+ system(`taskset -p -c ${cpu} ${pid}`);
|
|
|
+}
|
|
|
+
|
|
|
+function set_netdev_cpu(dev, cpu) {
|
|
|
+ let queues = glob(`/sys/class/net/${dev}/queues/rx-*/rps_cpus`);
|
|
|
+ let val = sprintf("%x", (1 << int(cpu)));
|
|
|
+ if (disable)
|
|
|
+ val = 0;
|
|
|
+ for (let queue in queues) {
|
|
|
+ if (debug || do_nothing)
|
|
|
+ warn(`echo ${val} > ${queue}\n`);
|
|
|
+ if (!do_nothing)
|
|
|
+ writefile(queue, `${val}`);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function task_device_match(name, device)
|
|
|
+{
|
|
|
+ let napi_match = match(name, /napi\/([^-+])-\d+/);
|
|
|
+ if (!napi_match)
|
|
|
+ napi_match = match(name, /mt76-tx (phy\d+)/);
|
|
|
+ if (napi_match &&
|
|
|
+ (index(device.phy, napi_match[1]) >= 0 ||
|
|
|
+ index(device.netdev, napi_match[1]) >= 0))
|
|
|
+ return true;
|
|
|
+
|
|
|
+ if (device.driver == "mtk_soc_eth" && match(name, /napi\/mtk_eth-/))
|
|
|
+ return true;
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+cpus = map(glob("/sys/bus/cpu/devices/*"), (path) => {
|
|
|
+ return {
|
|
|
+ id: int(match(path, /.*cpu(\d+)/)[1]),
|
|
|
+ core: int(trim(readfile(`${path}/topology/core_id`))),
|
|
|
+ load: 0.0,
|
|
|
+ };
|
|
|
+});
|
|
|
+
|
|
|
+cpus = slice(cpus, 0, 64);
|
|
|
+if (length(cpus) < 2)
|
|
|
+ exit(0);
|
|
|
+
|
|
|
+function cpu_add_weight(cpu_id, weight)
|
|
|
+{
|
|
|
+ let cpu = cpus[cpu_id];
|
|
|
+ cpu.load += weight;
|
|
|
+ for (let sibling in cpus) {
|
|
|
+ if (sibling == cpu || sibling.core != cpu.core)
|
|
|
+ continue;
|
|
|
+ sibling.load += weight * cpu_thread_weight;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function get_next_cpu(weight, prev_cpu)
|
|
|
+{
|
|
|
+ if (disable)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ let sort_cpus = sort(slice(cpus), (a, b) => a.load - b.load);
|
|
|
+ let idx = 0;
|
|
|
+
|
|
|
+ if (prev_cpu != null && sort_cpus[idx].id == prev_cpu)
|
|
|
+ idx++;
|
|
|
+
|
|
|
+ let cpu = sort_cpus[idx].id;
|
|
|
+ cpu_add_weight(cpu, weight);
|
|
|
+ return cpu;
|
|
|
+}
|
|
|
+
|
|
|
+let phys_devs = {};
|
|
|
+let netdev_phys = {};
|
|
|
+let netdevs = map(glob("/sys/class/net/*"), (dev) => basename(dev));
|
|
|
+
|
|
|
+for (let dev in netdevs) {
|
|
|
+ let pdev_path = realpath(`/sys/class/net/${dev}/device`);
|
|
|
+ if (!pdev_path)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (length(glob(`/sys/class/net/${dev}/lower_*`)) > 0)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ let pdev = phys_devs[pdev_path];
|
|
|
+ if (!pdev) {
|
|
|
+ pdev = phys_devs[pdev_path] = {
|
|
|
+ path: pdev_path,
|
|
|
+ driver: basename(readlink(`${pdev_path}/driver`)),
|
|
|
+ netdev: [],
|
|
|
+ phy: [],
|
|
|
+ tasks: [],
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ let phyidx = trim(readfile(`/sys/class/net/${dev}/phy80211/index`));
|
|
|
+ if (phyidx != null) {
|
|
|
+ let phy = `phy${phyidx}`;
|
|
|
+ if (index(pdev.phy, phy) < 0)
|
|
|
+ push(pdev.phy, phy);
|
|
|
+ }
|
|
|
+
|
|
|
+ push(pdev.netdev, dev);
|
|
|
+ netdev_phys[dev] = pdev;
|
|
|
+}
|
|
|
+
|
|
|
+for (let path in glob("/proc/*/exe")) {
|
|
|
+ readlink(path);
|
|
|
+ if (error() != "No such file or directory")
|
|
|
+ continue;
|
|
|
+
|
|
|
+ let pid = basename(dirname(path));
|
|
|
+ let name = task_name(pid);
|
|
|
+ for (let devname in phys_devs) {
|
|
|
+ let dev = phys_devs[devname];
|
|
|
+ if (!task_device_match(name, dev))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ push(dev.tasks, pid);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function assign_dev_cpu(dev) {
|
|
|
+ if (length(dev.tasks) > 0) {
|
|
|
+ let cpu = dev.napi_cpu = get_next_cpu(napi_weight);
|
|
|
+ for (let task in dev.tasks)
|
|
|
+ set_task_cpu(task, cpu);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (length(dev.netdev) > 0) {
|
|
|
+ let cpu = dev.rx_cpu = get_next_cpu(rx_weight, dev.napi_cpu);
|
|
|
+ for (let netdev in dev.netdev)
|
|
|
+ set_netdev_cpu(netdev, cpu);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Assign ethernet devices first
|
|
|
+for (let devname in phys_devs) {
|
|
|
+ let dev = phys_devs[devname];
|
|
|
+ if (!length(dev.phy))
|
|
|
+ assign_dev_cpu(dev);
|
|
|
+}
|
|
|
+
|
|
|
+// Add bias to avoid assigning other tasks to CPUs with ethernet NAPI
|
|
|
+for (let devname in phys_devs) {
|
|
|
+ let dev = phys_devs[devname];
|
|
|
+ if (!length(dev.tasks) || dev.napi_cpu == null)
|
|
|
+ continue;
|
|
|
+ cpu_add_weight(dev.napi_cpu, eth_bias);
|
|
|
+}
|
|
|
+
|
|
|
+// Assign WLAN devices
|
|
|
+for (let devname in phys_devs) {
|
|
|
+ let dev = phys_devs[devname];
|
|
|
+ if (length(dev.phy) > 0)
|
|
|
+ assign_dev_cpu(dev);
|
|
|
+}
|
|
|
+
|
|
|
+if (debug > 1)
|
|
|
+ warn(sprintf("devices: %.J\ncpus: %.J\n", phys_devs, cpus));
|