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;
15 while (length(ARGV) > 0) {
16 let arg = shift(ARGV);
31 local_flows = +shift(ARGV);
36 function task_name(pid)
38 let stat = open(`/proc/${pid}/status`, "r");
41 let line = stat.read("line");
43 return trim(split(line, "\t", 2)[1]);
46 function set_task_cpu(pid, cpu) {
48 cpu = join(",", map(cpus, (cpu) => cpu.id));
49 let name = task_name(pid);
52 if (debug || do_nothing)
53 warn(`taskset -p -c ${cpu} ${name}\n`);
55 system(`taskset -p -c ${cpu} ${pid}`);
58 function cpu_mask(cpu)
62 mask = (1 << length(cpus)) - 1;
64 mask = (1 << int(cpu));
65 return sprintf("%x", mask);
68 function set_netdev_cpu(dev, cpu) {
69 let queues = glob(`/sys/class/net/${dev}/queues/rx-*/rps_cpus`);
70 let val = cpu_mask(cpu);
73 for (let queue in queues) {
74 if (debug || do_nothing)
75 warn(`echo ${val} > ${queue}\n`);
77 writefile(queue, `${val}`);
79 queues = glob(`/sys/class/net/${dev}/queues/rx-*/rps_flow_cnt`);
80 for (let queue in queues) {
81 if (debug || do_nothing)
82 warn(`echo ${local_flows} > ${queue}\n`);
84 writefile(queue, `${local_flows}`);
88 function task_device_match(name, device)
90 let napi_match = match(name, /napi\/([^-+])-\d+/);
92 napi_match = match(name, /mt76-tx (phy\d+)/);
94 (index(device.phy, napi_match[1]) >= 0 ||
95 index(device.netdev, napi_match[1]) >= 0))
98 if (device.driver == "mtk_soc_eth" && match(name, /napi\/mtk_eth-/))
104 cpus = map(glob("/sys/bus/cpu/devices/*"), (path) => {
106 id: int(match(path, /.*cpu(\d+)/)[1]),
107 core: int(trim(readfile(`${path}/topology/core_id`))),
112 cpus = slice(cpus, 0, 64);
113 if (length(cpus) < 2)
116 function cpu_add_weight(cpu_id, weight)
118 let cpu = cpus[cpu_id];
120 for (let sibling in cpus) {
121 if (sibling == cpu || sibling.core != cpu.core)
123 sibling.load += weight * cpu_thread_weight;
127 function get_next_cpu(weight, prev_cpu)
132 let sort_cpus = sort(slice(cpus), (a, b) => a.load - b.load);
135 if (prev_cpu != null && sort_cpus[idx].id == prev_cpu)
138 let cpu = sort_cpus[idx].id;
139 cpu_add_weight(cpu, weight);
144 let netdev_phys = {};
145 let netdevs = map(glob("/sys/class/net/*"), (dev) => basename(dev));
147 for (let dev in netdevs) {
148 let pdev_path = realpath(`/sys/class/net/${dev}/device`);
152 if (length(glob(`/sys/class/net/${dev}/lower_*`)) > 0)
155 let pdev = phys_devs[pdev_path];
157 pdev = phys_devs[pdev_path] = {
159 driver: basename(readlink(`${pdev_path}/driver`)),
166 let phyidx = trim(readfile(`/sys/class/net/${dev}/phy80211/index`));
167 if (phyidx != null) {
168 let phy = `phy${phyidx}`;
169 if (index(pdev.phy, phy) < 0)
173 push(pdev.netdev, dev);
174 netdev_phys[dev] = pdev;
177 for (let path in glob("/proc/*/exe")) {
179 if (error() != "No such file or directory")
182 let pid = basename(dirname(path));
183 let name = task_name(pid);
184 for (let devname in phys_devs) {
185 let dev = phys_devs[devname];
186 if (!task_device_match(name, dev))
189 push(dev.tasks, pid);
194 function assign_dev_cpu(dev) {
195 if (length(dev.tasks) > 0) {
196 let cpu = dev.napi_cpu = get_next_cpu(napi_weight);
197 for (let task in dev.tasks)
198 set_task_cpu(task, cpu);
201 if (length(dev.netdev) > 0) {
206 cpu = get_next_cpu(rx_weight, dev.napi_cpu);
208 for (let netdev in dev.netdev)
209 set_netdev_cpu(netdev, cpu);
213 // Assign ethernet devices first
214 for (let devname in phys_devs) {
215 let dev = phys_devs[devname];
216 if (!length(dev.phy))
220 // Add bias to avoid assigning other tasks to CPUs with ethernet NAPI
221 for (let devname in phys_devs) {
222 let dev = phys_devs[devname];
223 if (!length(dev.tasks) || dev.napi_cpu == null)
225 cpu_add_weight(dev.napi_cpu, eth_bias);
228 // Assign WLAN devices
229 for (let devname in phys_devs) {
230 let dev = phys_devs[devname];
231 if (length(dev.phy) > 0)
236 warn(sprintf("devices: %.J\ncpus: %.J\n", phys_devs, cpus));