@@ -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
12911327err_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 */
20242061static 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 */
23652407static 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
0 commit comments