3 import { glob, basename, dirname, readlink, readfile, realpath, writefile, error, open } from "fs";
6 let cpu_thread_weight = 0.75;
9 let debug = 0, do_nothing = 0;
14 for (let arg in ARGV) {
31 function task_name(pid)
33 let stat = open(`/proc/${pid}/status`, "r");
36 let line = stat.read("line");
38 return trim(split(line, "\t", 2)[1]);
41 function set_task_cpu(pid, cpu) {
43 cpu = join(",", map(cpus, (cpu) => cpu.id));
44 let name = task_name(pid);
47 if (debug || do_nothing)
48 warn(`taskset -p -c ${cpu} ${name}\n`);
50 system(`taskset -p -c ${cpu} ${pid}`);
53 function cpu_mask(cpu)
57 mask = (1 << length(cpus)) - 1;
59 mask = (1 << int(cpu));
60 return sprintf("%x", mask);
63 function set_netdev_cpu(dev, cpu) {
64 let queues = glob(`/sys/class/net/${dev}/queues/rx-*/rps_cpus`);
65 let val = cpu_mask(cpu);
68 for (let queue in queues) {
69 if (debug || do_nothing)
70 warn(`echo ${val} > ${queue}\n`);
72 writefile(queue, `${val}`);
76 function task_device_match(name, device)
78 let napi_match = match(name, /napi\/([^-+])-\d+/);
80 napi_match = match(name, /mt76-tx (phy\d+)/);
82 (index(device.phy, napi_match[1]) >= 0 ||
83 index(device.netdev, napi_match[1]) >= 0))
86 if (device.driver == "mtk_soc_eth" && match(name, /napi\/mtk_eth-/))
92 cpus = map(glob("/sys/bus/cpu/devices/*"), (path) => {
94 id: int(match(path, /.*cpu(\d+)/)[1]),
95 core: int(trim(readfile(`${path}/topology/core_id`))),
100 cpus = slice(cpus, 0, 64);
101 if (length(cpus) < 2)
104 function cpu_add_weight(cpu_id, weight)
106 let cpu = cpus[cpu_id];
108 for (let sibling in cpus) {
109 if (sibling == cpu || sibling.core != cpu.core)
111 sibling.load += weight * cpu_thread_weight;
115 function get_next_cpu(weight, prev_cpu)
120 let sort_cpus = sort(slice(cpus), (a, b) => a.load - b.load);
123 if (prev_cpu != null && sort_cpus[idx].id == prev_cpu)
126 let cpu = sort_cpus[idx].id;
127 cpu_add_weight(cpu, weight);
132 let netdev_phys = {};
133 let netdevs = map(glob("/sys/class/net/*"), (dev) => basename(dev));
135 for (let dev in netdevs) {
136 let pdev_path = realpath(`/sys/class/net/${dev}/device`);
140 if (length(glob(`/sys/class/net/${dev}/lower_*`)) > 0)
143 let pdev = phys_devs[pdev_path];
145 pdev = phys_devs[pdev_path] = {
147 driver: basename(readlink(`${pdev_path}/driver`)),
154 let phyidx = trim(readfile(`/sys/class/net/${dev}/phy80211/index`));
155 if (phyidx != null) {
156 let phy = `phy${phyidx}`;
157 if (index(pdev.phy, phy) < 0)
161 push(pdev.netdev, dev);
162 netdev_phys[dev] = pdev;
165 for (let path in glob("/proc/*/exe")) {
167 if (error() != "No such file or directory")
170 let pid = basename(dirname(path));
171 let name = task_name(pid);
172 for (let devname in phys_devs) {
173 let dev = phys_devs[devname];
174 if (!task_device_match(name, dev))
177 push(dev.tasks, pid);
182 function assign_dev_cpu(dev) {
183 if (length(dev.tasks) > 0) {
184 let cpu = dev.napi_cpu = get_next_cpu(napi_weight);
185 for (let task in dev.tasks)
186 set_task_cpu(task, cpu);
189 if (length(dev.netdev) > 0) {
194 cpu = get_next_cpu(rx_weight, dev.napi_cpu);
196 for (let netdev in dev.netdev)
197 set_netdev_cpu(netdev, cpu);
201 // Assign ethernet devices first
202 for (let devname in phys_devs) {
203 let dev = phys_devs[devname];
204 if (!length(dev.phy))
208 // Add bias to avoid assigning other tasks to CPUs with ethernet NAPI
209 for (let devname in phys_devs) {
210 let dev = phys_devs[devname];
211 if (!length(dev.tasks) || dev.napi_cpu == null)
213 cpu_add_weight(dev.napi_cpu, eth_bias);
216 // Assign WLAN devices
217 for (let devname in phys_devs) {
218 let dev = phys_devs[devname];
219 if (length(dev.phy) > 0)
224 warn(sprintf("devices: %.J\ncpus: %.J\n", phys_devs, cpus));