15
15
#include <linux/log2.h>
16
16
#include <linux/jhash.h>
17
17
#include <linux/netlink.h>
18
+ #include <linux/workqueue.h>
18
19
#include <linux/rhashtable.h>
19
20
#include <linux/netfilter.h>
20
21
#include <linux/netfilter/nf_tables.h>
25
26
26
27
struct nft_hash {
27
28
struct rhashtable ht ;
29
+ struct delayed_work gc_work ;
28
30
};
29
31
30
32
struct nft_hash_elem {
@@ -62,6 +64,8 @@ static inline int nft_hash_cmp(struct rhashtable_compare_arg *arg,
62
64
63
65
if (nft_data_cmp (nft_set_ext_key (& he -> ext ), x -> key , x -> set -> klen ))
64
66
return 1 ;
67
+ if (nft_set_elem_expired (& he -> ext ))
68
+ return 1 ;
65
69
if (!nft_set_elem_active (& he -> ext , x -> genmask ))
66
70
return 1 ;
67
71
return 0 ;
@@ -107,6 +111,7 @@ static void nft_hash_activate(const struct nft_set *set,
107
111
struct nft_hash_elem * he = elem -> priv ;
108
112
109
113
nft_set_elem_change_active (set , & he -> ext );
114
+ nft_set_elem_clear_busy (& he -> ext );
110
115
}
111
116
112
117
static void * nft_hash_deactivate (const struct nft_set * set ,
@@ -120,9 +125,15 @@ static void *nft_hash_deactivate(const struct nft_set *set,
120
125
.key = & elem -> key ,
121
126
};
122
127
128
+ rcu_read_lock ();
123
129
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 ();
126
137
127
138
return he ;
128
139
}
@@ -170,6 +181,8 @@ static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set,
170
181
171
182
if (iter -> count < iter -> skip )
172
183
goto cont ;
184
+ if (nft_set_elem_expired (& he -> ext ))
185
+ goto cont ;
173
186
if (!nft_set_elem_active (& he -> ext , genmask ))
174
187
goto cont ;
175
188
@@ -188,6 +201,54 @@ static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set,
188
201
rhashtable_walk_exit (& hti );
189
202
}
190
203
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
+
191
252
static unsigned int nft_hash_privsize (const struct nlattr * const nla [])
192
253
{
193
254
return sizeof (struct nft_hash );
@@ -207,11 +268,20 @@ static int nft_hash_init(const struct nft_set *set,
207
268
{
208
269
struct nft_hash * priv = nft_set_priv (set );
209
270
struct rhashtable_params params = nft_hash_params ;
271
+ int err ;
210
272
211
273
params .nelem_hint = desc -> size ?: NFT_HASH_ELEMENT_HINT ;
212
274
params .key_len = set -> klen ;
213
275
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 ;
215
285
}
216
286
217
287
static void nft_hash_elem_destroy (void * ptr , void * arg )
@@ -223,6 +293,7 @@ static void nft_hash_destroy(const struct nft_set *set)
223
293
{
224
294
struct nft_hash * priv = nft_set_priv (set );
225
295
296
+ cancel_delayed_work_sync (& priv -> gc_work );
226
297
rhashtable_free_and_destroy (& priv -> ht , nft_hash_elem_destroy ,
227
298
(void * )set );
228
299
}
@@ -264,7 +335,7 @@ static struct nft_set_ops nft_hash_ops __read_mostly = {
264
335
.remove = nft_hash_remove ,
265
336
.lookup = nft_hash_lookup ,
266
337
.walk = nft_hash_walk ,
267
- .features = NFT_SET_MAP ,
338
+ .features = NFT_SET_MAP | NFT_SET_TIMEOUT ,
268
339
.owner = THIS_MODULE ,
269
340
};
270
341
0 commit comments