Skip to content

Commit bbf7383

Browse files
w1ldptrdavem330
authored andcommitted
net: sched: traverse chains in block with tcf_get_next_chain()
All users of block->chain_list rely on rtnl lock and assume that no new chains are added when traversing the list. Use tcf_get_next_chain() to traverse chain list without relying on rtnl mutex. This function iterates over chains by taking reference to current iterator chain only and doesn't assume external synchronization of chain list. Don't take reference to all chains in block when flushing and use tcf_get_next_chain() to safely iterate over chain list instead. Remove tcf_block_put_all_chains() that is no longer used. Signed-off-by: Vlad Buslov <[email protected]> Acked-by: Jiri Pirko <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 165f013 commit bbf7383

File tree

3 files changed

+76
-26
lines changed

3 files changed

+76
-26
lines changed

include/net/pkt_cls.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ bool tcf_queue_work(struct rcu_work *rwork, work_func_t func);
4444
struct tcf_chain *tcf_chain_get_by_act(struct tcf_block *block,
4545
u32 chain_index);
4646
void tcf_chain_put_by_act(struct tcf_chain *chain);
47+
struct tcf_chain *tcf_get_next_chain(struct tcf_block *block,
48+
struct tcf_chain *chain);
4749
void tcf_block_netif_keep_dst(struct tcf_block *block);
4850
int tcf_block_get(struct tcf_block **p_block,
4951
struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q,

net/sched/cls_api.c

Lines changed: 71 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -883,28 +883,62 @@ static struct tcf_block *tcf_block_refcnt_get(struct net *net, u32 block_index)
883883
return block;
884884
}
885885

886-
static void tcf_block_flush_all_chains(struct tcf_block *block)
886+
static struct tcf_chain *
887+
__tcf_get_next_chain(struct tcf_block *block, struct tcf_chain *chain)
887888
{
888-
struct tcf_chain *chain;
889+
mutex_lock(&block->lock);
890+
if (chain)
891+
chain = list_is_last(&chain->list, &block->chain_list) ?
892+
NULL : list_next_entry(chain, list);
893+
else
894+
chain = list_first_entry_or_null(&block->chain_list,
895+
struct tcf_chain, list);
889896

890-
/* Hold a refcnt for all chains, so that they don't disappear
891-
* while we are iterating.
892-
*/
893-
list_for_each_entry(chain, &block->chain_list, list)
897+
/* skip all action-only chains */
898+
while (chain && tcf_chain_held_by_acts_only(chain))
899+
chain = list_is_last(&chain->list, &block->chain_list) ?
900+
NULL : list_next_entry(chain, list);
901+
902+
if (chain)
894903
tcf_chain_hold(chain);
904+
mutex_unlock(&block->lock);
895905

896-
list_for_each_entry(chain, &block->chain_list, list)
897-
tcf_chain_flush(chain);
906+
return chain;
898907
}
899908

900-
static void tcf_block_put_all_chains(struct tcf_block *block)
909+
/* Function to be used by all clients that want to iterate over all chains on
910+
* block. It properly obtains block->lock and takes reference to chain before
911+
* returning it. Users of this function must be tolerant to concurrent chain
912+
* insertion/deletion or ensure that no concurrent chain modification is
913+
* possible. Note that all netlink dump callbacks cannot guarantee to provide
914+
* consistent dump because rtnl lock is released each time skb is filled with
915+
* data and sent to user-space.
916+
*/
917+
918+
struct tcf_chain *
919+
tcf_get_next_chain(struct tcf_block *block, struct tcf_chain *chain)
901920
{
902-
struct tcf_chain *chain, *tmp;
921+
struct tcf_chain *chain_next = __tcf_get_next_chain(block, chain);
903922

904-
/* At this point, all the chains should have refcnt >= 1. */
905-
list_for_each_entry_safe(chain, tmp, &block->chain_list, list) {
906-
tcf_chain_put_explicitly_created(chain);
923+
if (chain)
907924
tcf_chain_put(chain);
925+
926+
return chain_next;
927+
}
928+
EXPORT_SYMBOL(tcf_get_next_chain);
929+
930+
static void tcf_block_flush_all_chains(struct tcf_block *block)
931+
{
932+
struct tcf_chain *chain;
933+
934+
/* Last reference to block. At this point chains cannot be added or
935+
* removed concurrently.
936+
*/
937+
for (chain = tcf_get_next_chain(block, NULL);
938+
chain;
939+
chain = tcf_get_next_chain(block, chain)) {
940+
tcf_chain_put_explicitly_created(chain);
941+
tcf_chain_flush(chain);
908942
}
909943
}
910944

@@ -923,16 +957,14 @@ static void __tcf_block_put(struct tcf_block *block, struct Qdisc *q,
923957
mutex_unlock(&block->lock);
924958
if (tcf_block_shared(block))
925959
tcf_block_remove(block, block->net);
926-
if (!free_block)
927-
tcf_block_flush_all_chains(block);
928960

929961
if (q)
930962
tcf_block_offload_unbind(block, q, ei);
931963

932964
if (free_block)
933965
tcf_block_destroy(block);
934966
else
935-
tcf_block_put_all_chains(block);
967+
tcf_block_flush_all_chains(block);
936968
} else if (q) {
937969
tcf_block_offload_unbind(block, q, ei);
938970
}
@@ -1266,11 +1298,15 @@ tcf_block_playback_offloads(struct tcf_block *block, tc_setup_cb_t *cb,
12661298
void *cb_priv, bool add, bool offload_in_use,
12671299
struct netlink_ext_ack *extack)
12681300
{
1269-
struct tcf_chain *chain;
1301+
struct tcf_chain *chain, *chain_prev;
12701302
struct tcf_proto *tp;
12711303
int err;
12721304

1273-
list_for_each_entry(chain, &block->chain_list, list) {
1305+
for (chain = __tcf_get_next_chain(block, NULL);
1306+
chain;
1307+
chain_prev = chain,
1308+
chain = __tcf_get_next_chain(block, chain),
1309+
tcf_chain_put(chain_prev)) {
12741310
for (tp = rtnl_dereference(chain->filter_chain); tp;
12751311
tp = rtnl_dereference(tp->next)) {
12761312
if (tp->ops->reoffload) {
@@ -1289,6 +1325,7 @@ tcf_block_playback_offloads(struct tcf_block *block, tc_setup_cb_t *cb,
12891325
return 0;
12901326

12911327
err_playback_remove:
1328+
tcf_chain_put(chain);
12921329
tcf_block_playback_offloads(block, cb, cb_priv, false, offload_in_use,
12931330
extack);
12941331
return err;
@@ -2023,11 +2060,11 @@ static bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent,
20232060
/* called with RTNL */
20242061
static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
20252062
{
2063+
struct tcf_chain *chain, *chain_prev;
20262064
struct net *net = sock_net(skb->sk);
20272065
struct nlattr *tca[TCA_MAX + 1];
20282066
struct Qdisc *q = NULL;
20292067
struct tcf_block *block;
2030-
struct tcf_chain *chain;
20312068
struct tcmsg *tcm = nlmsg_data(cb->nlh);
20322069
long index_start;
20332070
long index;
@@ -2091,12 +2128,17 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
20912128
index_start = cb->args[0];
20922129
index = 0;
20932130

2094-
list_for_each_entry(chain, &block->chain_list, list) {
2131+
for (chain = __tcf_get_next_chain(block, NULL);
2132+
chain;
2133+
chain_prev = chain,
2134+
chain = __tcf_get_next_chain(block, chain),
2135+
tcf_chain_put(chain_prev)) {
20952136
if (tca[TCA_CHAIN] &&
20962137
nla_get_u32(tca[TCA_CHAIN]) != chain->index)
20972138
continue;
20982139
if (!tcf_chain_dump(chain, q, parent, skb, cb,
20992140
index_start, &index)) {
2141+
tcf_chain_put(chain);
21002142
err = -EMSGSIZE;
21012143
break;
21022144
}
@@ -2364,11 +2406,11 @@ static int tc_ctl_chain(struct sk_buff *skb, struct nlmsghdr *n,
23642406
/* called with RTNL */
23652407
static int tc_dump_chain(struct sk_buff *skb, struct netlink_callback *cb)
23662408
{
2409+
struct tcf_chain *chain, *chain_prev;
23672410
struct net *net = sock_net(skb->sk);
23682411
struct nlattr *tca[TCA_MAX + 1];
23692412
struct Qdisc *q = NULL;
23702413
struct tcf_block *block;
2371-
struct tcf_chain *chain;
23722414
struct tcmsg *tcm = nlmsg_data(cb->nlh);
23732415
long index_start;
23742416
long index;
@@ -2432,22 +2474,26 @@ static int tc_dump_chain(struct sk_buff *skb, struct netlink_callback *cb)
24322474
index_start = cb->args[0];
24332475
index = 0;
24342476

2435-
list_for_each_entry(chain, &block->chain_list, list) {
2477+
for (chain = __tcf_get_next_chain(block, NULL);
2478+
chain;
2479+
chain_prev = chain,
2480+
chain = __tcf_get_next_chain(block, chain),
2481+
tcf_chain_put(chain_prev)) {
24362482
if ((tca[TCA_CHAIN] &&
24372483
nla_get_u32(tca[TCA_CHAIN]) != chain->index))
24382484
continue;
24392485
if (index < index_start) {
24402486
index++;
24412487
continue;
24422488
}
2443-
if (tcf_chain_held_by_acts_only(chain))
2444-
continue;
24452489
err = tc_chain_fill_node(chain, net, skb, block,
24462490
NETLINK_CB(cb->skb).portid,
24472491
cb->nlh->nlmsg_seq, NLM_F_MULTI,
24482492
RTM_NEWCHAIN);
2449-
if (err <= 0)
2493+
if (err <= 0) {
2494+
tcf_chain_put(chain);
24502495
break;
2496+
}
24512497
index++;
24522498
}
24532499

net/sched/sch_api.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1909,7 +1909,9 @@ static void tc_bind_tclass(struct Qdisc *q, u32 portid, u32 clid,
19091909
block = cops->tcf_block(q, cl, NULL);
19101910
if (!block)
19111911
return;
1912-
list_for_each_entry(chain, &block->chain_list, list) {
1912+
for (chain = tcf_get_next_chain(block, NULL);
1913+
chain;
1914+
chain = tcf_get_next_chain(block, chain)) {
19131915
struct tcf_proto *tp;
19141916

19151917
for (tp = rtnl_dereference(chain->filter_chain);

0 commit comments

Comments
 (0)