aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2017-10-29 09:49:32 -0400
committerDavid S. Miller <davem@davemloft.net>2017-10-29 09:49:32 -0400
commit6c325f4eca9ee9eb32cf58768e6e4ebcabaa8d6e (patch)
tree9977e690a21f354b5ed0535f8ddad4ec176f6dac /tools
parent8c83c88584abb3a04a4026be91060bc309f4c034 (diff)
parent31c2611b66e01378b54f7ef641cb0d23fcd8502f (diff)
Merge branch 'net_sched-fix-races-with-RCU-callbacks'
Cong Wang says: ==================== net_sched: fix races with RCU callbacks Recently, the RCU callbacks used in TC filters and TC actions keep drawing my attention, they introduce at least 4 race condition bugs: 1. A simple one fixed by Daniel: commit c78e1746d3ad7d548bdf3fe491898cc453911a49 Author: Daniel Borkmann <daniel@iogearbox.net> Date: Wed May 20 17:13:33 2015 +0200 net: sched: fix call_rcu() race on classifier module unloads 2. A very nasty one fixed by me: commit 1697c4bb5245649a23f06a144cc38c06715e1b65 Author: Cong Wang <xiyou.wangcong@gmail.com> Date: Mon Sep 11 16:33:32 2017 -0700 net_sched: carefully handle tcf_block_put() 3. Two more bugs found by Chris: https://patchwork.ozlabs.org/patch/826696/ https://patchwork.ozlabs.org/patch/826695/ Usually RCU callbacks are simple, however for TC filters and actions, they are complex because at least TC actions could be destroyed together with the TC filter in one callback. And RCU callbacks are invoked in BH context, without locking they are parallel too. All of these contribute to the cause of these nasty bugs. Alternatively, we could also: a) Introduce a spinlock to serialize these RCU callbacks. But as I said in commit 1697c4bb5245 ("net_sched: carefully handle tcf_block_put()"), it is very hard to do because of tcf_chain_dump(). Potentially we need to do a lot of work to make it possible (if not impossible). b) Just get rid of these RCU callbacks, because they are not necessary at all, callers of these call_rcu() are all on slow paths and holding RTNL lock, so blocking is allowed in their contexts. However, David and Eric dislike adding synchronize_rcu() here. As suggested by Paul, we could defer the work to a workqueue and gain the permission of holding RTNL again without any performance impact, however, in tcf_block_put() we could have a deadlock when flushing workqueue while hodling RTNL lock, the trick here is to defer the work itself in workqueue and make it queued after all other works so that we keep the same ordering to avoid any use-after-free. Please see the first patch for details. Patch 1 introduces the infrastructure, patch 2~12 move each tc filter to the new tc filter workqueue, patch 13 adds an assertion to catch potential bugs like this, patch 14 closes another rcu callback race, patch 15 and patch 16 add new test cases. ==================== Reported-by: Chris Mi <chrism@mellanox.com> Cc: Daniel Borkmann <daniel@iogearbox.net> Cc: Jiri Pirko <jiri@resnulli.us> Cc: John Fastabend <john.fastabend@gmail.com> Cc: Jamal Hadi Salim <jhs@mojatatu.com> Cc: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com> Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'tools')
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/filters/tests.json23
-rwxr-xr-xtools/testing/selftests/tc-testing/tdc.py20
-rwxr-xr-xtools/testing/selftests/tc-testing/tdc_batch.py62
-rw-r--r--tools/testing/selftests/tc-testing/tdc_config.py2
4 files changed, 102 insertions, 5 deletions
diff --git a/tools/testing/selftests/tc-testing/tc-tests/filters/tests.json b/tools/testing/selftests/tc-testing/tc-tests/filters/tests.json
index c727b96a59b0..5fa02d86b35f 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/filters/tests.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/filters/tests.json
@@ -17,5 +17,26 @@
17 "teardown": [ 17 "teardown": [
18 "$TC qdisc del dev $DEV1 ingress" 18 "$TC qdisc del dev $DEV1 ingress"
19 ] 19 ]
20 },
21 {
22 "id": "d052",
23 "name": "Add 1M filters with the same action",
24 "category": [
25 "filter",
26 "flower"
27 ],
28 "setup": [
29 "$TC qdisc add dev $DEV2 ingress",
30 "./tdc_batch.py $DEV2 $BATCH_FILE --share_action -n 1000000"
31 ],
32 "cmdUnderTest": "$TC -b $BATCH_FILE",
33 "expExitCode": "0",
34 "verifyCmd": "$TC actions list action gact",
35 "matchPattern": "action order 0: gact action drop.*index 1 ref 1000000 bind 1000000",
36 "matchCount": "1",
37 "teardown": [
38 "$TC qdisc del dev $DEV2 ingress",
39 "/bin/rm $BATCH_FILE"
40 ]
20 } 41 }
21] \ No newline at end of file 42]
diff --git a/tools/testing/selftests/tc-testing/tdc.py b/tools/testing/selftests/tc-testing/tdc.py
index cd61b7844c0d..5f11f5d7456e 100755
--- a/tools/testing/selftests/tc-testing/tdc.py
+++ b/tools/testing/selftests/tc-testing/tdc.py
@@ -88,7 +88,7 @@ def prepare_env(cmdlist):
88 exit(1) 88 exit(1)
89 89
90 90
91def test_runner(filtered_tests): 91def test_runner(filtered_tests, args):
92 """ 92 """
93 Driver function for the unit tests. 93 Driver function for the unit tests.
94 94
@@ -105,6 +105,8 @@ def test_runner(filtered_tests):
105 for tidx in testlist: 105 for tidx in testlist:
106 result = True 106 result = True
107 tresult = "" 107 tresult = ""
108 if "flower" in tidx["category"] and args.device == None:
109 continue
108 print("Test " + tidx["id"] + ": " + tidx["name"]) 110 print("Test " + tidx["id"] + ": " + tidx["name"])
109 prepare_env(tidx["setup"]) 111 prepare_env(tidx["setup"])
110 (p, procout) = exec_cmd(tidx["cmdUnderTest"]) 112 (p, procout) = exec_cmd(tidx["cmdUnderTest"])
@@ -152,6 +154,10 @@ def ns_create():
152 exec_cmd(cmd, False) 154 exec_cmd(cmd, False)
153 cmd = 'ip -s $NS link set $DEV1 up' 155 cmd = 'ip -s $NS link set $DEV1 up'
154 exec_cmd(cmd, False) 156 exec_cmd(cmd, False)
157 cmd = 'ip link set $DEV2 netns $NS'
158 exec_cmd(cmd, False)
159 cmd = 'ip -s $NS link set $DEV2 up'
160 exec_cmd(cmd, False)
155 161
156 162
157def ns_destroy(): 163def ns_destroy():
@@ -211,7 +217,8 @@ def set_args(parser):
211 help='Execute the single test case with specified ID') 217 help='Execute the single test case with specified ID')
212 parser.add_argument('-i', '--id', action='store_true', dest='gen_id', 218 parser.add_argument('-i', '--id', action='store_true', dest='gen_id',
213 help='Generate ID numbers for new test cases') 219 help='Generate ID numbers for new test cases')
214 return parser 220 parser.add_argument('-d', '--device',
221 help='Execute the test case in flower category')
215 return parser 222 return parser
216 223
217 224
@@ -225,6 +232,8 @@ def check_default_settings(args):
225 232
226 if args.path != None: 233 if args.path != None:
227 NAMES['TC'] = args.path 234 NAMES['TC'] = args.path
235 if args.device != None:
236 NAMES['DEV2'] = args.device
228 if not os.path.isfile(NAMES['TC']): 237 if not os.path.isfile(NAMES['TC']):
229 print("The specified tc path " + NAMES['TC'] + " does not exist.") 238 print("The specified tc path " + NAMES['TC'] + " does not exist.")
230 exit(1) 239 exit(1)
@@ -381,14 +390,17 @@ def set_operation_mode(args):
381 if (len(alltests) == 0): 390 if (len(alltests) == 0):
382 print("Cannot find a test case with ID matching " + target_id) 391 print("Cannot find a test case with ID matching " + target_id)
383 exit(1) 392 exit(1)
384 catresults = test_runner(alltests) 393 catresults = test_runner(alltests, args)
385 print("All test results: " + "\n\n" + catresults) 394 print("All test results: " + "\n\n" + catresults)
386 elif (len(target_category) > 0): 395 elif (len(target_category) > 0):
396 if (target_category == "flower") and args.device == None:
397 print("Please specify a NIC device (-d) to run category flower")
398 exit(1)
387 if (target_category not in ucat): 399 if (target_category not in ucat):
388 print("Specified category is not present in this file.") 400 print("Specified category is not present in this file.")
389 exit(1) 401 exit(1)
390 else: 402 else:
391 catresults = test_runner(testcases[target_category]) 403 catresults = test_runner(testcases[target_category], args)
392 print("Category " + target_category + "\n\n" + catresults) 404 print("Category " + target_category + "\n\n" + catresults)
393 405
394 ns_destroy() 406 ns_destroy()
diff --git a/tools/testing/selftests/tc-testing/tdc_batch.py b/tools/testing/selftests/tc-testing/tdc_batch.py
new file mode 100755
index 000000000000..707c6bfef689
--- /dev/null
+++ b/tools/testing/selftests/tc-testing/tdc_batch.py
@@ -0,0 +1,62 @@
1#!/usr/bin/python3
2
3"""
4tdc_batch.py - a script to generate TC batch file
5
6Copyright (C) 2017 Chris Mi <chrism@mellanox.com>
7"""
8
9import argparse
10
11parser = argparse.ArgumentParser(description='TC batch file generator')
12parser.add_argument("device", help="device name")
13parser.add_argument("file", help="batch file name")
14parser.add_argument("-n", "--number", type=int,
15 help="how many lines in batch file")
16parser.add_argument("-o", "--skip_sw",
17 help="skip_sw (offload), by default skip_hw",
18 action="store_true")
19parser.add_argument("-s", "--share_action",
20 help="all filters share the same action",
21 action="store_true")
22parser.add_argument("-p", "--prio",
23 help="all filters have different prio",
24 action="store_true")
25args = parser.parse_args()
26
27device = args.device
28file = open(args.file, 'w')
29
30number = 1
31if args.number:
32 number = args.number
33
34skip = "skip_hw"
35if args.skip_sw:
36 skip = "skip_sw"
37
38share_action = ""
39if args.share_action:
40 share_action = "index 1"
41
42prio = "prio 1"
43if args.prio:
44 prio = ""
45 if number > 0x4000:
46 number = 0x4000
47
48index = 0
49for i in range(0x100):
50 for j in range(0x100):
51 for k in range(0x100):
52 mac = ("%02x:%02x:%02x" % (i, j, k))
53 src_mac = "e4:11:00:" + mac
54 dst_mac = "e4:12:00:" + mac
55 cmd = ("filter add dev %s %s protocol ip parent ffff: flower %s "
56 "src_mac %s dst_mac %s action drop %s" %
57 (device, prio, skip, src_mac, dst_mac, share_action))
58 file.write("%s\n" % cmd)
59 index += 1
60 if index >= number:
61 file.close()
62 exit(0)
diff --git a/tools/testing/selftests/tc-testing/tdc_config.py b/tools/testing/selftests/tc-testing/tdc_config.py
index 01087375a7c3..b6352515c1b5 100644
--- a/tools/testing/selftests/tc-testing/tdc_config.py
+++ b/tools/testing/selftests/tc-testing/tdc_config.py
@@ -12,6 +12,8 @@ NAMES = {
12 # Name of veth devices to be created for the namespace 12 # Name of veth devices to be created for the namespace
13 'DEV0': 'v0p0', 13 'DEV0': 'v0p0',
14 'DEV1': 'v0p1', 14 'DEV1': 'v0p1',
15 'DEV2': '',
16 'BATCH_FILE': './batch.txt',
15 # Name of the namespace to use 17 # Name of the namespace to use
16 'NS': 'tcut' 18 'NS': 'tcut'
17 } 19 }