mbedtls: export cmake files
[openwrt/staging/nbd.git] / package / network / config / netifd / files / usr / libexec / network / packet-steering.uc
1 #!/usr/bin/env ucode
2 'use strict';
3 import { glob, basename, dirname, readlink, readfile, realpath, writefile, error, open } from "fs";
4
5 let napi_weight = 1.0;
6 let cpu_thread_weight = 0.75;
7 let rx_weight = 0.75;
8 let eth_bias = 2.0;
9 let debug = 0, do_nothing = 0;
10 let disable;
11 let cpus;
12 let all_cpus;
13 let local_flows = 0;
14
15 while (length(ARGV) > 0) {
16 let arg = shift(ARGV);
17 switch (arg) {
18 case "-d":
19 debug++;
20 break;
21 case "-n":
22 do_nothing++;
23 break;
24 case '0':
25 disable = true;
26 break;
27 case '2':
28 all_cpus = true;
29 break;
30 case '-l':
31 local_flows = +shift(ARGV);
32 break;
33 }
34 }
35
36 function task_name(pid)
37 {
38 let stat = open(`/proc/${pid}/status`, "r");
39 if (!stat)
40 return;
41 let line = stat.read("line");
42 stat.close();
43 return trim(split(line, "\t", 2)[1]);
44 }
45
46 function set_task_cpu(pid, cpu) {
47 if (disable)
48 cpu = join(",", map(cpus, (cpu) => cpu.id));
49 let name = task_name(pid);
50 if (!name)
51 return;
52 if (debug || do_nothing)
53 warn(`taskset -p -c ${cpu} ${name}\n`);
54 if (!do_nothing)
55 system(`taskset -p -c ${cpu} ${pid}`);
56 }
57
58 function cpu_mask(cpu)
59 {
60 let mask;
61 if (cpu < 0)
62 mask = (1 << length(cpus)) - 1;
63 else
64 mask = (1 << int(cpu));
65 return sprintf("%x", mask);
66 }
67
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);
71 if (disable)
72 val = 0;
73 for (let queue in queues) {
74 if (debug || do_nothing)
75 warn(`echo ${val} > ${queue}\n`);
76 if (!do_nothing)
77 writefile(queue, `${val}`);
78 }
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`);
83 if (!do_nothing)
84 writefile(queue, `${local_flows}`);
85 }
86 }
87
88 function task_device_match(name, device)
89 {
90 let napi_match = match(name, /napi\/([^-+])-\d+/);
91 if (!napi_match)
92 napi_match = match(name, /mt76-tx (phy\d+)/);
93 if (napi_match &&
94 (index(device.phy, napi_match[1]) >= 0 ||
95 index(device.netdev, napi_match[1]) >= 0))
96 return true;
97
98 if (device.driver == "mtk_soc_eth" && match(name, /napi\/mtk_eth-/))
99 return true;
100
101 return false;
102 }
103
104 cpus = map(glob("/sys/bus/cpu/devices/*"), (path) => {
105 return {
106 id: int(match(path, /.*cpu(\d+)/)[1]),
107 core: int(trim(readfile(`${path}/topology/core_id`))),
108 load: 0.0,
109 };
110 });
111
112 cpus = slice(cpus, 0, 64);
113 if (length(cpus) < 2)
114 exit(0);
115
116 function cpu_add_weight(cpu_id, weight)
117 {
118 let cpu = cpus[cpu_id];
119 cpu.load += weight;
120 for (let sibling in cpus) {
121 if (sibling == cpu || sibling.core != cpu.core)
122 continue;
123 sibling.load += weight * cpu_thread_weight;
124 }
125 }
126
127 function get_next_cpu(weight, prev_cpu)
128 {
129 if (disable)
130 return 0;
131
132 let sort_cpus = sort(slice(cpus), (a, b) => a.load - b.load);
133 let idx = 0;
134
135 if (prev_cpu != null && sort_cpus[idx].id == prev_cpu)
136 idx++;
137
138 let cpu = sort_cpus[idx].id;
139 cpu_add_weight(cpu, weight);
140 return cpu;
141 }
142
143 let phys_devs = {};
144 let netdev_phys = {};
145 let netdevs = map(glob("/sys/class/net/*"), (dev) => basename(dev));
146
147 for (let dev in netdevs) {
148 let pdev_path = realpath(`/sys/class/net/${dev}/device`);
149 if (!pdev_path)
150 continue;
151
152 if (length(glob(`/sys/class/net/${dev}/lower_*`)) > 0)
153 continue;
154
155 let pdev = phys_devs[pdev_path];
156 if (!pdev) {
157 pdev = phys_devs[pdev_path] = {
158 path: pdev_path,
159 driver: basename(readlink(`${pdev_path}/driver`)),
160 netdev: [],
161 phy: [],
162 tasks: [],
163 };
164 }
165
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)
170 push(pdev.phy, phy);
171 }
172
173 push(pdev.netdev, dev);
174 netdev_phys[dev] = pdev;
175 }
176
177 for (let path in glob("/proc/*/exe")) {
178 readlink(path);
179 if (error() != "No such file or directory")
180 continue;
181
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))
187 continue;
188
189 push(dev.tasks, pid);
190 break;
191 }
192 }
193
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);
199 }
200
201 if (length(dev.netdev) > 0) {
202 let cpu;
203 if (all_cpus)
204 cpu = -1;
205 else
206 cpu = get_next_cpu(rx_weight, dev.napi_cpu);
207 dev.rx_cpu = cpu;
208 for (let netdev in dev.netdev)
209 set_netdev_cpu(netdev, cpu);
210 }
211 }
212
213 // Assign ethernet devices first
214 for (let devname in phys_devs) {
215 let dev = phys_devs[devname];
216 if (!length(dev.phy))
217 assign_dev_cpu(dev);
218 }
219
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)
224 continue;
225 cpu_add_weight(dev.napi_cpu, eth_bias);
226 }
227
228 // Assign WLAN devices
229 for (let devname in phys_devs) {
230 let dev = phys_devs[devname];
231 if (length(dev.phy) > 0)
232 assign_dev_cpu(dev);
233 }
234
235 if (debug > 1)
236 warn(sprintf("devices: %.J\ncpus: %.J\n", phys_devs, cpus));