|
2 | 2 | # SPDX-License-Identifier: GPL-2.0 |
3 | 3 |
|
4 | 4 | import errno |
| 5 | +import subprocess |
| 6 | +import time |
5 | 7 | from lib.py import ksft_run, ksft_exit, ksft_pr |
6 | | -from lib.py import ksft_ge, ksft_eq, ksft_in, ksft_true, ksft_raises, KsftSkipEx, KsftXfailEx |
| 8 | +from lib.py import ksft_ge, ksft_eq, ksft_is, ksft_in, ksft_lt, ksft_true, ksft_raises |
| 9 | +from lib.py import KsftSkipEx, KsftXfailEx |
7 | 10 | from lib.py import ksft_disruptive |
8 | 11 | from lib.py import EthtoolFamily, NetdevFamily, RtnlFamily, NlError |
9 | 12 | from lib.py import NetDrvEnv |
10 | | -from lib.py import ip, defer |
| 13 | +from lib.py import cmd, ip, defer |
11 | 14 |
|
12 | 15 | ethnl = EthtoolFamily() |
13 | 16 | netfam = NetdevFamily() |
@@ -174,10 +177,95 @@ def check_down(cfg) -> None: |
174 | 177 | netfam.qstats_get({"ifindex": cfg.ifindex, "scope": "queue"}, dump=True) |
175 | 178 |
|
176 | 179 |
|
| 180 | +def __run_inf_loop(body): |
| 181 | + body = body.strip() |
| 182 | + if body[-1] != ';': |
| 183 | + body += ';' |
| 184 | + |
| 185 | + return subprocess.Popen(f"while true; do {body} done", shell=True, |
| 186 | + stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| 187 | + |
| 188 | + |
| 189 | +def __stats_increase_sanely(old, new) -> None: |
| 190 | + for k in old.keys(): |
| 191 | + ksft_ge(new[k], old[k]) |
| 192 | + ksft_lt(new[k] - old[k], 1 << 31, comment="likely wrapping error") |
| 193 | + |
| 194 | + |
| 195 | +def procfs_hammer(cfg) -> None: |
| 196 | + """ |
| 197 | + Reading stats via procfs only holds the RCU lock, which is not an exclusive |
| 198 | + lock, make sure drivers can handle parallel reads of stats. |
| 199 | + """ |
| 200 | + one = __run_inf_loop("cat /proc/net/dev") |
| 201 | + defer(one.kill) |
| 202 | + two = __run_inf_loop("cat /proc/net/dev") |
| 203 | + defer(two.kill) |
| 204 | + |
| 205 | + time.sleep(1) |
| 206 | + # Make sure the processes are running |
| 207 | + ksft_is(one.poll(), None) |
| 208 | + ksft_is(two.poll(), None) |
| 209 | + |
| 210 | + rtstat1 = rtnl.getlink({"ifi-index": cfg.ifindex})['stats64'] |
| 211 | + time.sleep(2) |
| 212 | + rtstat2 = rtnl.getlink({"ifi-index": cfg.ifindex})['stats64'] |
| 213 | + __stats_increase_sanely(rtstat1, rtstat2) |
| 214 | + # defers will kill the loops |
| 215 | + |
| 216 | + |
| 217 | +@ksft_disruptive |
| 218 | +def procfs_downup_hammer(cfg) -> None: |
| 219 | + """ |
| 220 | + Reading stats via procfs only holds the RCU lock, drivers often try |
| 221 | + to sleep when reading the stats, or don't protect against races. |
| 222 | + """ |
| 223 | + # Max out the queues, we'll flip between max and 1 |
| 224 | + channels = ethnl.channels_get({'header': {'dev-index': cfg.ifindex}}) |
| 225 | + if channels['combined-count'] == 0: |
| 226 | + rx_type = 'rx' |
| 227 | + else: |
| 228 | + rx_type = 'combined' |
| 229 | + cur_queue_cnt = channels[f'{rx_type}-count'] |
| 230 | + max_queue_cnt = channels[f'{rx_type}-max'] |
| 231 | + |
| 232 | + cmd(f"ethtool -L {cfg.ifname} {rx_type} {max_queue_cnt}") |
| 233 | + defer(cmd, f"ethtool -L {cfg.ifname} {rx_type} {cur_queue_cnt}") |
| 234 | + |
| 235 | + # Real test stats |
| 236 | + stats = __run_inf_loop("cat /proc/net/dev") |
| 237 | + defer(stats.kill) |
| 238 | + |
| 239 | + ipset = f"ip link set dev {cfg.ifname}" |
| 240 | + defer(ip, f"link set dev {cfg.ifname} up") |
| 241 | + # The "echo -n 1" lets us count iterations below |
| 242 | + updown = f"{ipset} down; sleep 0.05; {ipset} up; sleep 0.05; " + \ |
| 243 | + f"ethtool -L {cfg.ifname} {rx_type} 1; " + \ |
| 244 | + f"ethtool -L {cfg.ifname} {rx_type} {max_queue_cnt}; " + \ |
| 245 | + "echo -n 1" |
| 246 | + updown = __run_inf_loop(updown) |
| 247 | + kill_updown = defer(updown.kill) |
| 248 | + |
| 249 | + time.sleep(1) |
| 250 | + # Make sure the processes are running |
| 251 | + ksft_is(stats.poll(), None) |
| 252 | + ksft_is(updown.poll(), None) |
| 253 | + |
| 254 | + rtstat1 = rtnl.getlink({"ifi-index": cfg.ifindex})['stats64'] |
| 255 | + # We're looking for crashes, give it extra time |
| 256 | + time.sleep(9) |
| 257 | + rtstat2 = rtnl.getlink({"ifi-index": cfg.ifindex})['stats64'] |
| 258 | + __stats_increase_sanely(rtstat1, rtstat2) |
| 259 | + |
| 260 | + kill_updown.exec() |
| 261 | + stdout, _ = updown.communicate(timeout=5) |
| 262 | + ksft_pr("completed up/down cycles:", len(stdout.decode('utf-8'))) |
| 263 | + |
| 264 | + |
177 | 265 | def main() -> None: |
178 | 266 | with NetDrvEnv(__file__, queue_count=100) as cfg: |
179 | 267 | ksft_run([check_pause, check_fec, pkt_byte_sum, qstat_by_ifindex, |
180 | | - check_down], |
| 268 | + check_down, procfs_hammer, procfs_downup_hammer], |
181 | 269 | args=(cfg, )) |
182 | 270 | ksft_exit() |
183 | 271 |
|
|
0 commit comments