Skip to content

Commit 470502d

Browse files
w1ldptrdavem330
authored andcommitted
net: sched: unlock rules update API
Register netlink protocol handlers for message types RTM_NEWTFILTER, RTM_DELTFILTER, RTM_GETTFILTER as unlocked. Set rtnl_held variable that tracks rtnl mutex state to be false by default. Introduce tcf_proto_is_unlocked() helper that is used to check tcf_proto_ops->flag to determine if ops can be called without taking rtnl lock. Manually lookup Qdisc, class and block in rule update handlers. Verify that both Qdisc ops and proto ops are unlocked before using any of their callbacks, and obtain rtnl lock otherwise. Signed-off-by: Vlad Buslov <vladbu@mellanox.com> Acked-by: Jiri Pirko <jiri@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 18d3eef commit 470502d

File tree

1 file changed

+114
-17
lines changed

1 file changed

+114
-17
lines changed

net/sched/cls_api.c

Lines changed: 114 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,23 @@ static inline u32 tcf_auto_prio(struct tcf_proto *tp)
163163
return TC_H_MAJ(first);
164164
}
165165

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+
166183
static struct tcf_proto *tcf_proto_create(const char *kind, u32 protocol,
167184
u32 prio, struct tcf_chain *chain,
168185
bool rtnl_held,
@@ -1312,8 +1329,12 @@ static void tcf_block_release(struct Qdisc *q, struct tcf_block *block,
13121329
if (!IS_ERR_OR_NULL(block))
13131330
tcf_block_refcnt_put(block, rtnl_held);
13141331

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+
}
13171338
}
13181339

13191340
struct tcf_block_owner_item {
@@ -1966,7 +1987,7 @@ static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
19661987
void *fh;
19671988
int err;
19681989
int tp_created;
1969-
bool rtnl_held = true;
1990+
bool rtnl_held = false;
19701991

19711992
if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
19721993
return -EPERM;
@@ -1985,6 +2006,7 @@ static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
19852006
parent = t->tcm_parent;
19862007
tp = NULL;
19872008
cl = 0;
2009+
block = NULL;
19882010

19892011
if (prio == 0) {
19902012
/* If no priority is provided by the user,
@@ -2001,8 +2023,27 @@ static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
20012023

20022024
/* Find head of filter chain. */
20032025

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);
20062047
if (IS_ERR(block)) {
20072048
err = PTR_ERR(block);
20082049
goto errout;
@@ -2123,9 +2164,18 @@ static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
21232164
tcf_chain_put(chain);
21242165
}
21252166
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;
21272176
/* Replay the request. */
21282177
goto replay;
2178+
}
21292179
return err;
21302180

21312181
errout_locked:
@@ -2146,12 +2196,12 @@ static int tc_del_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
21462196
struct Qdisc *q = NULL;
21472197
struct tcf_chain_info chain_info;
21482198
struct tcf_chain *chain = NULL;
2149-
struct tcf_block *block;
2199+
struct tcf_block *block = NULL;
21502200
struct tcf_proto *tp = NULL;
21512201
unsigned long cl = 0;
21522202
void *fh = NULL;
21532203
int err;
2154-
bool rtnl_held = true;
2204+
bool rtnl_held = false;
21552205

21562206
if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
21572207
return -EPERM;
@@ -2172,8 +2222,27 @@ static int tc_del_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
21722222

21732223
/* Find head of filter chain. */
21742224

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);
21772246
if (IS_ERR(block)) {
21782247
err = PTR_ERR(block);
21792248
goto errout;
@@ -2255,6 +2324,10 @@ static int tc_del_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
22552324
tcf_chain_put(chain);
22562325
}
22572326
tcf_block_release(q, block, rtnl_held);
2327+
2328+
if (rtnl_held)
2329+
rtnl_unlock();
2330+
22582331
return err;
22592332

22602333
errout_locked:
@@ -2275,12 +2348,12 @@ static int tc_get_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
22752348
struct Qdisc *q = NULL;
22762349
struct tcf_chain_info chain_info;
22772350
struct tcf_chain *chain = NULL;
2278-
struct tcf_block *block;
2351+
struct tcf_block *block = NULL;
22792352
struct tcf_proto *tp = NULL;
22802353
unsigned long cl = 0;
22812354
void *fh = NULL;
22822355
int err;
2283-
bool rtnl_held = true;
2356+
bool rtnl_held = false;
22842357

22852358
err = nlmsg_parse(n, sizeof(*t), tca, TCA_MAX, rtm_tca_policy, extack);
22862359
if (err < 0)
@@ -2298,8 +2371,26 @@ static int tc_get_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
22982371

22992372
/* Find head of filter chain. */
23002373

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);
23032394
if (IS_ERR(block)) {
23042395
err = PTR_ERR(block);
23052396
goto errout;
@@ -2352,6 +2443,10 @@ static int tc_get_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
23522443
tcf_chain_put(chain);
23532444
}
23542445
tcf_block_release(q, block, rtnl_held);
2446+
2447+
if (rtnl_held)
2448+
rtnl_unlock();
2449+
23552450
return err;
23562451
}
23572452

@@ -3214,10 +3309,12 @@ static int __init tc_filter_init(void)
32143309
if (err)
32153310
goto err_rhash_setup_block_ht;
32163311

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);
32193316
rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_get_tfilter,
3220-
tc_dump_tfilter, 0);
3317+
tc_dump_tfilter, RTNL_FLAG_DOIT_UNLOCKED);
32213318
rtnl_register(PF_UNSPEC, RTM_NEWCHAIN, tc_ctl_chain, NULL, 0);
32223319
rtnl_register(PF_UNSPEC, RTM_DELCHAIN, tc_ctl_chain, NULL, 0);
32233320
rtnl_register(PF_UNSPEC, RTM_GETCHAIN, tc_ctl_chain,

0 commit comments

Comments
 (0)