Skip to content

Commit 8837ba3

Browse files
committed
netfilter: nf_tables: split async and sync catchall in two functions
list_for_each_entry_safe() does not work for the async case which runs under RCU, therefore, split GC logic for catchall in two functions instead, one for each of the sync and async GC variants. The catchall sync GC variant never sees a _DEAD bit set on ever, thus, this handling is removed in such case, moreover, allocate GC sync batch via GFP_KERNEL. Fixes: 93995bf ("netfilter: nf_tables: remove catchall element in GC sync path") Reported-by: Florian Westphal <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 28628fa commit 8837ba3

File tree

1 file changed

+30
-25
lines changed

1 file changed

+30
-25
lines changed

net/netfilter/nf_tables_api.c

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9680,16 +9680,14 @@ void nft_trans_gc_queue_sync_done(struct nft_trans_gc *trans)
96809680
call_rcu(&trans->rcu, nft_trans_gc_trans_free);
96819681
}
96829682

9683-
static struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc,
9684-
unsigned int gc_seq,
9685-
bool sync)
9683+
struct nft_trans_gc *nft_trans_gc_catchall_async(struct nft_trans_gc *gc,
9684+
unsigned int gc_seq)
96869685
{
9687-
struct nft_set_elem_catchall *catchall, *next;
9686+
struct nft_set_elem_catchall *catchall;
96889687
const struct nft_set *set = gc->set;
9689-
struct nft_elem_priv *elem_priv;
96909688
struct nft_set_ext *ext;
96919689

9692-
list_for_each_entry_safe(catchall, next, &set->catchall_list, list) {
9690+
list_for_each_entry_rcu(catchall, &set->catchall_list, list) {
96939691
ext = nft_set_elem_ext(set, catchall->elem);
96949692

96959693
if (!nft_set_elem_expired(ext))
@@ -9699,35 +9697,42 @@ static struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc,
96999697

97009698
nft_set_elem_dead(ext);
97019699
dead_elem:
9702-
if (sync)
9703-
gc = nft_trans_gc_queue_sync(gc, GFP_ATOMIC);
9704-
else
9705-
gc = nft_trans_gc_queue_async(gc, gc_seq, GFP_ATOMIC);
9706-
9700+
gc = nft_trans_gc_queue_async(gc, gc_seq, GFP_ATOMIC);
97079701
if (!gc)
97089702
return NULL;
97099703

9710-
elem_priv = catchall->elem;
9711-
if (sync) {
9712-
nft_setelem_data_deactivate(gc->net, gc->set, elem_priv);
9713-
nft_setelem_catchall_destroy(catchall);
9714-
}
9715-
9716-
nft_trans_gc_elem_add(gc, elem_priv);
9704+
nft_trans_gc_elem_add(gc, catchall->elem);
97179705
}
97189706

97199707
return gc;
97209708
}
97219709

9722-
struct nft_trans_gc *nft_trans_gc_catchall_async(struct nft_trans_gc *gc,
9723-
unsigned int gc_seq)
9724-
{
9725-
return nft_trans_gc_catchall(gc, gc_seq, false);
9726-
}
9727-
97289710
struct nft_trans_gc *nft_trans_gc_catchall_sync(struct nft_trans_gc *gc)
97299711
{
9730-
return nft_trans_gc_catchall(gc, 0, true);
9712+
struct nft_set_elem_catchall *catchall, *next;
9713+
const struct nft_set *set = gc->set;
9714+
struct nft_elem_priv *elem_priv;
9715+
struct nft_set_ext *ext;
9716+
9717+
WARN_ON_ONCE(!lockdep_commit_lock_is_held(gc->net));
9718+
9719+
list_for_each_entry_safe(catchall, next, &set->catchall_list, list) {
9720+
ext = nft_set_elem_ext(set, catchall->elem);
9721+
9722+
if (!nft_set_elem_expired(ext))
9723+
continue;
9724+
9725+
gc = nft_trans_gc_queue_sync(gc, GFP_KERNEL);
9726+
if (!gc)
9727+
return NULL;
9728+
9729+
elem_priv = catchall->elem;
9730+
nft_setelem_data_deactivate(gc->net, gc->set, elem_priv);
9731+
nft_setelem_catchall_destroy(catchall);
9732+
nft_trans_gc_elem_add(gc, elem_priv);
9733+
}
9734+
9735+
return gc;
97319736
}
97329737

97339738
static void nf_tables_module_autoload_cleanup(struct net *net)

0 commit comments

Comments
 (0)