1515#include <linux/log2.h>
1616#include <linux/jhash.h>
1717#include <linux/netlink.h>
18+ #include <linux/workqueue.h>
1819#include <linux/rhashtable.h>
1920#include <linux/netfilter.h>
2021#include <linux/netfilter/nf_tables.h>
2526
2627struct nft_hash {
2728 struct rhashtable ht ;
29+ struct delayed_work gc_work ;
2830};
2931
3032struct nft_hash_elem {
@@ -62,6 +64,8 @@ static inline int nft_hash_cmp(struct rhashtable_compare_arg *arg,
6264
6365 if (nft_data_cmp (nft_set_ext_key (& he -> ext ), x -> key , x -> set -> klen ))
6466 return 1 ;
67+ if (nft_set_elem_expired (& he -> ext ))
68+ return 1 ;
6569 if (!nft_set_elem_active (& he -> ext , x -> genmask ))
6670 return 1 ;
6771 return 0 ;
@@ -107,6 +111,7 @@ static void nft_hash_activate(const struct nft_set *set,
107111 struct nft_hash_elem * he = elem -> priv ;
108112
109113 nft_set_elem_change_active (set , & he -> ext );
114+ nft_set_elem_clear_busy (& he -> ext );
110115}
111116
112117static void * nft_hash_deactivate (const struct nft_set * set ,
@@ -120,9 +125,15 @@ static void *nft_hash_deactivate(const struct nft_set *set,
120125 .key = & elem -> key ,
121126 };
122127
128+ rcu_read_lock ();
123129 he = rhashtable_lookup_fast (& priv -> ht , & arg , nft_hash_params );
124- if (he != NULL )
125- nft_set_elem_change_active (set , & he -> ext );
130+ if (he != NULL ) {
131+ if (!nft_set_elem_mark_busy (& he -> ext ))
132+ nft_set_elem_change_active (set , & he -> ext );
133+ else
134+ he = NULL ;
135+ }
136+ rcu_read_unlock ();
126137
127138 return he ;
128139}
@@ -170,6 +181,8 @@ static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set,
170181
171182 if (iter -> count < iter -> skip )
172183 goto cont ;
184+ if (nft_set_elem_expired (& he -> ext ))
185+ goto cont ;
173186 if (!nft_set_elem_active (& he -> ext , genmask ))
174187 goto cont ;
175188
@@ -188,6 +201,54 @@ static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set,
188201 rhashtable_walk_exit (& hti );
189202}
190203
204+ static void nft_hash_gc (struct work_struct * work )
205+ {
206+ const struct nft_set * set ;
207+ struct nft_hash_elem * he ;
208+ struct nft_hash * priv ;
209+ struct nft_set_gc_batch * gcb = NULL ;
210+ struct rhashtable_iter hti ;
211+ int err ;
212+
213+ priv = container_of (work , struct nft_hash , gc_work .work );
214+ set = nft_set_container_of (priv );
215+
216+ err = rhashtable_walk_init (& priv -> ht , & hti );
217+ if (err )
218+ goto schedule ;
219+
220+ err = rhashtable_walk_start (& hti );
221+ if (err && err != - EAGAIN )
222+ goto out ;
223+
224+ while ((he = rhashtable_walk_next (& hti ))) {
225+ if (IS_ERR (he )) {
226+ if (PTR_ERR (he ) != - EAGAIN )
227+ goto out ;
228+ continue ;
229+ }
230+
231+ if (!nft_set_elem_expired (& he -> ext ))
232+ continue ;
233+ if (nft_set_elem_mark_busy (& he -> ext ))
234+ continue ;
235+
236+ gcb = nft_set_gc_batch_check (set , gcb , GFP_ATOMIC );
237+ if (gcb == NULL )
238+ goto out ;
239+ rhashtable_remove_fast (& priv -> ht , & he -> node , nft_hash_params );
240+ nft_set_gc_batch_add (gcb , he );
241+ }
242+ out :
243+ rhashtable_walk_stop (& hti );
244+ rhashtable_walk_exit (& hti );
245+
246+ nft_set_gc_batch_complete (gcb );
247+ schedule :
248+ queue_delayed_work (system_power_efficient_wq , & priv -> gc_work ,
249+ nft_set_gc_interval (set ));
250+ }
251+
191252static unsigned int nft_hash_privsize (const struct nlattr * const nla [])
192253{
193254 return sizeof (struct nft_hash );
@@ -207,11 +268,20 @@ static int nft_hash_init(const struct nft_set *set,
207268{
208269 struct nft_hash * priv = nft_set_priv (set );
209270 struct rhashtable_params params = nft_hash_params ;
271+ int err ;
210272
211273 params .nelem_hint = desc -> size ?: NFT_HASH_ELEMENT_HINT ;
212274 params .key_len = set -> klen ;
213275
214- return rhashtable_init (& priv -> ht , & params );
276+ err = rhashtable_init (& priv -> ht , & params );
277+ if (err < 0 )
278+ return err ;
279+
280+ INIT_DEFERRABLE_WORK (& priv -> gc_work , nft_hash_gc );
281+ if (set -> flags & NFT_SET_TIMEOUT )
282+ queue_delayed_work (system_power_efficient_wq , & priv -> gc_work ,
283+ nft_set_gc_interval (set ));
284+ return 0 ;
215285}
216286
217287static void nft_hash_elem_destroy (void * ptr , void * arg )
@@ -223,6 +293,7 @@ static void nft_hash_destroy(const struct nft_set *set)
223293{
224294 struct nft_hash * priv = nft_set_priv (set );
225295
296+ cancel_delayed_work_sync (& priv -> gc_work );
226297 rhashtable_free_and_destroy (& priv -> ht , nft_hash_elem_destroy ,
227298 (void * )set );
228299}
@@ -264,7 +335,7 @@ static struct nft_set_ops nft_hash_ops __read_mostly = {
264335 .remove = nft_hash_remove ,
265336 .lookup = nft_hash_lookup ,
266337 .walk = nft_hash_walk ,
267- .features = NFT_SET_MAP ,
338+ .features = NFT_SET_MAP | NFT_SET_TIMEOUT ,
268339 .owner = THIS_MODULE ,
269340};
270341
0 commit comments