@@ -163,6 +163,23 @@ static inline u32 tcf_auto_prio(struct tcf_proto *tp)
163
163
return TC_H_MAJ (first );
164
164
}
165
165
166
+ static bool tcf_proto_is_unlocked (const char * kind )
167
+ {
168
+ const struct tcf_proto_ops * ops ;
169
+ bool ret ;
170
+
171
+ ops = tcf_proto_lookup_ops (kind , false, NULL );
172
+ /* On error return false to take rtnl lock. Proto lookup/create
173
+ * functions will perform lookup again and properly handle errors.
174
+ */
175
+ if (IS_ERR (ops ))
176
+ return false;
177
+
178
+ ret = !!(ops -> flags & TCF_PROTO_OPS_DOIT_UNLOCKED );
179
+ module_put (ops -> owner );
180
+ return ret ;
181
+ }
182
+
166
183
static struct tcf_proto * tcf_proto_create (const char * kind , u32 protocol ,
167
184
u32 prio , struct tcf_chain * chain ,
168
185
bool rtnl_held ,
@@ -1312,8 +1329,12 @@ static void tcf_block_release(struct Qdisc *q, struct tcf_block *block,
1312
1329
if (!IS_ERR_OR_NULL (block ))
1313
1330
tcf_block_refcnt_put (block , rtnl_held );
1314
1331
1315
- if (q )
1316
- qdisc_put (q );
1332
+ if (q ) {
1333
+ if (rtnl_held )
1334
+ qdisc_put (q );
1335
+ else
1336
+ qdisc_put_unlocked (q );
1337
+ }
1317
1338
}
1318
1339
1319
1340
struct tcf_block_owner_item {
@@ -1966,7 +1987,7 @@ static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
1966
1987
void * fh ;
1967
1988
int err ;
1968
1989
int tp_created ;
1969
- bool rtnl_held = true ;
1990
+ bool rtnl_held = false ;
1970
1991
1971
1992
if (!netlink_ns_capable (skb , net -> user_ns , CAP_NET_ADMIN ))
1972
1993
return - EPERM ;
@@ -1985,6 +2006,7 @@ static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
1985
2006
parent = t -> tcm_parent ;
1986
2007
tp = NULL ;
1987
2008
cl = 0 ;
2009
+ block = NULL ;
1988
2010
1989
2011
if (prio == 0 ) {
1990
2012
/* If no priority is provided by the user,
@@ -2001,8 +2023,27 @@ static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
2001
2023
2002
2024
/* Find head of filter chain. */
2003
2025
2004
- block = tcf_block_find (net , & q , & parent , & cl ,
2005
- t -> tcm_ifindex , t -> tcm_block_index , extack );
2026
+ err = __tcf_qdisc_find (net , & q , & parent , t -> tcm_ifindex , false, extack );
2027
+ if (err )
2028
+ return err ;
2029
+
2030
+ /* Take rtnl mutex if rtnl_held was set to true on previous iteration,
2031
+ * block is shared (no qdisc found), qdisc is not unlocked, classifier
2032
+ * type is not specified, classifier is not unlocked.
2033
+ */
2034
+ if (rtnl_held ||
2035
+ (q && !(q -> ops -> cl_ops -> flags & QDISC_CLASS_OPS_DOIT_UNLOCKED )) ||
2036
+ !tca [TCA_KIND ] || !tcf_proto_is_unlocked (nla_data (tca [TCA_KIND ]))) {
2037
+ rtnl_held = true;
2038
+ rtnl_lock ();
2039
+ }
2040
+
2041
+ err = __tcf_qdisc_cl_find (q , parent , & cl , t -> tcm_ifindex , extack );
2042
+ if (err )
2043
+ goto errout ;
2044
+
2045
+ block = __tcf_block_find (net , q , cl , t -> tcm_ifindex , t -> tcm_block_index ,
2046
+ extack );
2006
2047
if (IS_ERR (block )) {
2007
2048
err = PTR_ERR (block );
2008
2049
goto errout ;
@@ -2123,9 +2164,18 @@ static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
2123
2164
tcf_chain_put (chain );
2124
2165
}
2125
2166
tcf_block_release (q , block , rtnl_held );
2126
- if (err == - EAGAIN )
2167
+
2168
+ if (rtnl_held )
2169
+ rtnl_unlock ();
2170
+
2171
+ if (err == - EAGAIN ) {
2172
+ /* Take rtnl lock in case EAGAIN is caused by concurrent flush
2173
+ * of target chain.
2174
+ */
2175
+ rtnl_held = true;
2127
2176
/* Replay the request. */
2128
2177
goto replay ;
2178
+ }
2129
2179
return err ;
2130
2180
2131
2181
errout_locked :
@@ -2146,12 +2196,12 @@ static int tc_del_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
2146
2196
struct Qdisc * q = NULL ;
2147
2197
struct tcf_chain_info chain_info ;
2148
2198
struct tcf_chain * chain = NULL ;
2149
- struct tcf_block * block ;
2199
+ struct tcf_block * block = NULL ;
2150
2200
struct tcf_proto * tp = NULL ;
2151
2201
unsigned long cl = 0 ;
2152
2202
void * fh = NULL ;
2153
2203
int err ;
2154
- bool rtnl_held = true ;
2204
+ bool rtnl_held = false ;
2155
2205
2156
2206
if (!netlink_ns_capable (skb , net -> user_ns , CAP_NET_ADMIN ))
2157
2207
return - EPERM ;
@@ -2172,8 +2222,27 @@ static int tc_del_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
2172
2222
2173
2223
/* Find head of filter chain. */
2174
2224
2175
- block = tcf_block_find (net , & q , & parent , & cl ,
2176
- t -> tcm_ifindex , t -> tcm_block_index , extack );
2225
+ err = __tcf_qdisc_find (net , & q , & parent , t -> tcm_ifindex , false, extack );
2226
+ if (err )
2227
+ return err ;
2228
+
2229
+ /* Take rtnl mutex if flushing whole chain, block is shared (no qdisc
2230
+ * found), qdisc is not unlocked, classifier type is not specified,
2231
+ * classifier is not unlocked.
2232
+ */
2233
+ if (!prio ||
2234
+ (q && !(q -> ops -> cl_ops -> flags & QDISC_CLASS_OPS_DOIT_UNLOCKED )) ||
2235
+ !tca [TCA_KIND ] || !tcf_proto_is_unlocked (nla_data (tca [TCA_KIND ]))) {
2236
+ rtnl_held = true;
2237
+ rtnl_lock ();
2238
+ }
2239
+
2240
+ err = __tcf_qdisc_cl_find (q , parent , & cl , t -> tcm_ifindex , extack );
2241
+ if (err )
2242
+ goto errout ;
2243
+
2244
+ block = __tcf_block_find (net , q , cl , t -> tcm_ifindex , t -> tcm_block_index ,
2245
+ extack );
2177
2246
if (IS_ERR (block )) {
2178
2247
err = PTR_ERR (block );
2179
2248
goto errout ;
@@ -2255,6 +2324,10 @@ static int tc_del_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
2255
2324
tcf_chain_put (chain );
2256
2325
}
2257
2326
tcf_block_release (q , block , rtnl_held );
2327
+
2328
+ if (rtnl_held )
2329
+ rtnl_unlock ();
2330
+
2258
2331
return err ;
2259
2332
2260
2333
errout_locked :
@@ -2275,12 +2348,12 @@ static int tc_get_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
2275
2348
struct Qdisc * q = NULL ;
2276
2349
struct tcf_chain_info chain_info ;
2277
2350
struct tcf_chain * chain = NULL ;
2278
- struct tcf_block * block ;
2351
+ struct tcf_block * block = NULL ;
2279
2352
struct tcf_proto * tp = NULL ;
2280
2353
unsigned long cl = 0 ;
2281
2354
void * fh = NULL ;
2282
2355
int err ;
2283
- bool rtnl_held = true ;
2356
+ bool rtnl_held = false ;
2284
2357
2285
2358
err = nlmsg_parse (n , sizeof (* t ), tca , TCA_MAX , rtm_tca_policy , extack );
2286
2359
if (err < 0 )
@@ -2298,8 +2371,26 @@ static int tc_get_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
2298
2371
2299
2372
/* Find head of filter chain. */
2300
2373
2301
- block = tcf_block_find (net , & q , & parent , & cl ,
2302
- t -> tcm_ifindex , t -> tcm_block_index , extack );
2374
+ err = __tcf_qdisc_find (net , & q , & parent , t -> tcm_ifindex , false, extack );
2375
+ if (err )
2376
+ return err ;
2377
+
2378
+ /* Take rtnl mutex if block is shared (no qdisc found), qdisc is not
2379
+ * unlocked, classifier type is not specified, classifier is not
2380
+ * unlocked.
2381
+ */
2382
+ if ((q && !(q -> ops -> cl_ops -> flags & QDISC_CLASS_OPS_DOIT_UNLOCKED )) ||
2383
+ !tca [TCA_KIND ] || !tcf_proto_is_unlocked (nla_data (tca [TCA_KIND ]))) {
2384
+ rtnl_held = true;
2385
+ rtnl_lock ();
2386
+ }
2387
+
2388
+ err = __tcf_qdisc_cl_find (q , parent , & cl , t -> tcm_ifindex , extack );
2389
+ if (err )
2390
+ goto errout ;
2391
+
2392
+ block = __tcf_block_find (net , q , cl , t -> tcm_ifindex , t -> tcm_block_index ,
2393
+ extack );
2303
2394
if (IS_ERR (block )) {
2304
2395
err = PTR_ERR (block );
2305
2396
goto errout ;
@@ -2352,6 +2443,10 @@ static int tc_get_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
2352
2443
tcf_chain_put (chain );
2353
2444
}
2354
2445
tcf_block_release (q , block , rtnl_held );
2446
+
2447
+ if (rtnl_held )
2448
+ rtnl_unlock ();
2449
+
2355
2450
return err ;
2356
2451
}
2357
2452
@@ -3214,10 +3309,12 @@ static int __init tc_filter_init(void)
3214
3309
if (err )
3215
3310
goto err_rhash_setup_block_ht ;
3216
3311
3217
- rtnl_register (PF_UNSPEC , RTM_NEWTFILTER , tc_new_tfilter , NULL , 0 );
3218
- rtnl_register (PF_UNSPEC , RTM_DELTFILTER , tc_del_tfilter , NULL , 0 );
3312
+ rtnl_register (PF_UNSPEC , RTM_NEWTFILTER , tc_new_tfilter , NULL ,
3313
+ RTNL_FLAG_DOIT_UNLOCKED );
3314
+ rtnl_register (PF_UNSPEC , RTM_DELTFILTER , tc_del_tfilter , NULL ,
3315
+ RTNL_FLAG_DOIT_UNLOCKED );
3219
3316
rtnl_register (PF_UNSPEC , RTM_GETTFILTER , tc_get_tfilter ,
3220
- tc_dump_tfilter , 0 );
3317
+ tc_dump_tfilter , RTNL_FLAG_DOIT_UNLOCKED );
3221
3318
rtnl_register (PF_UNSPEC , RTM_NEWCHAIN , tc_ctl_chain , NULL , 0 );
3222
3319
rtnl_register (PF_UNSPEC , RTM_DELCHAIN , tc_ctl_chain , NULL , 0 );
3223
3320
rtnl_register (PF_UNSPEC , RTM_GETCHAIN , tc_ctl_chain ,
0 commit comments