Skip to content

Commit 516cba9

Browse files
committed
Merge branch 'net-sched-cls_u32-use-proper-refcounts'
Pedro Tammela says: ==================== net/sched: cls_u32: use proper refcounts In u32 we are open coding refcounts of hashtables with integers which is far from ideal. Update those with proper refcount and add a couple of tests to tdc that exercise the refcounts explicitly. ==================== Link: https://lore.kernel.org/r/20231114141856.974326-1-pctammela@mojatatu.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2 parents 289354f + 54293e4 commit 516cba9

File tree

2 files changed

+75
-18
lines changed

2 files changed

+75
-18
lines changed

net/sched/cls_u32.c

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ struct tc_u_hnode {
7171
struct tc_u_hnode __rcu *next;
7272
u32 handle;
7373
u32 prio;
74-
int refcnt;
74+
refcount_t refcnt;
7575
unsigned int divisor;
7676
struct idr handle_idr;
7777
bool is_root;
@@ -86,7 +86,7 @@ struct tc_u_hnode {
8686
struct tc_u_common {
8787
struct tc_u_hnode __rcu *hlist;
8888
void *ptr;
89-
int refcnt;
89+
refcount_t refcnt;
9090
struct idr handle_idr;
9191
struct hlist_node hnode;
9292
long knodes;
@@ -359,7 +359,7 @@ static int u32_init(struct tcf_proto *tp)
359359
if (root_ht == NULL)
360360
return -ENOBUFS;
361361

362-
root_ht->refcnt++;
362+
refcount_set(&root_ht->refcnt, 1);
363363
root_ht->handle = tp_c ? gen_new_htid(tp_c, root_ht) : 0x80000000;
364364
root_ht->prio = tp->prio;
365365
root_ht->is_root = true;
@@ -371,18 +371,20 @@ static int u32_init(struct tcf_proto *tp)
371371
kfree(root_ht);
372372
return -ENOBUFS;
373373
}
374+
refcount_set(&tp_c->refcnt, 1);
374375
tp_c->ptr = key;
375376
INIT_HLIST_NODE(&tp_c->hnode);
376377
idr_init(&tp_c->handle_idr);
377378

378379
hlist_add_head(&tp_c->hnode, tc_u_hash(key));
380+
} else {
381+
refcount_inc(&tp_c->refcnt);
379382
}
380383

381-
tp_c->refcnt++;
382384
RCU_INIT_POINTER(root_ht->next, tp_c->hlist);
383385
rcu_assign_pointer(tp_c->hlist, root_ht);
384386

385-
root_ht->refcnt++;
387+
/* root_ht must be destroyed when tcf_proto is destroyed */
386388
rcu_assign_pointer(tp->root, root_ht);
387389
tp->data = tp_c;
388390
return 0;
@@ -393,7 +395,7 @@ static void __u32_destroy_key(struct tc_u_knode *n)
393395
struct tc_u_hnode *ht = rtnl_dereference(n->ht_down);
394396

395397
tcf_exts_destroy(&n->exts);
396-
if (ht && --ht->refcnt == 0)
398+
if (ht && refcount_dec_and_test(&ht->refcnt))
397399
kfree(ht);
398400
kfree(n);
399401
}
@@ -601,8 +603,6 @@ static int u32_destroy_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht,
601603
struct tc_u_hnode __rcu **hn;
602604
struct tc_u_hnode *phn;
603605

604-
WARN_ON(--ht->refcnt);
605-
606606
u32_clear_hnode(tp, ht, extack);
607607

608608
hn = &tp_c->hlist;
@@ -630,10 +630,10 @@ static void u32_destroy(struct tcf_proto *tp, bool rtnl_held,
630630

631631
WARN_ON(root_ht == NULL);
632632

633-
if (root_ht && --root_ht->refcnt == 1)
633+
if (root_ht && refcount_dec_and_test(&root_ht->refcnt))
634634
u32_destroy_hnode(tp, root_ht, extack);
635635

636-
if (--tp_c->refcnt == 0) {
636+
if (refcount_dec_and_test(&tp_c->refcnt)) {
637637
struct tc_u_hnode *ht;
638638

639639
hlist_del(&tp_c->hnode);
@@ -645,7 +645,7 @@ static void u32_destroy(struct tcf_proto *tp, bool rtnl_held,
645645
/* u32_destroy_key() will later free ht for us, if it's
646646
* still referenced by some knode
647647
*/
648-
if (--ht->refcnt == 0)
648+
if (refcount_dec_and_test(&ht->refcnt))
649649
kfree_rcu(ht, rcu);
650650
}
651651

@@ -674,15 +674,15 @@ static int u32_delete(struct tcf_proto *tp, void *arg, bool *last,
674674
return -EINVAL;
675675
}
676676

677-
if (ht->refcnt == 1) {
677+
if (refcount_dec_if_one(&ht->refcnt)) {
678678
u32_destroy_hnode(tp, ht, extack);
679679
} else {
680680
NL_SET_ERR_MSG_MOD(extack, "Can not delete in-use filter");
681681
return -EBUSY;
682682
}
683683

684684
out:
685-
*last = tp_c->refcnt == 1 && tp_c->knodes == 0;
685+
*last = refcount_read(&tp_c->refcnt) == 1 && tp_c->knodes == 0;
686686
return ret;
687687
}
688688

@@ -766,14 +766,14 @@ static int u32_set_parms(struct net *net, struct tcf_proto *tp,
766766
NL_SET_ERR_MSG_MOD(extack, "Not linking to root node");
767767
return -EINVAL;
768768
}
769-
ht_down->refcnt++;
769+
refcount_inc(&ht_down->refcnt);
770770
}
771771

772772
ht_old = rtnl_dereference(n->ht_down);
773773
rcu_assign_pointer(n->ht_down, ht_down);
774774

775775
if (ht_old)
776-
ht_old->refcnt--;
776+
refcount_dec(&ht_old->refcnt);
777777
}
778778

779779
if (ifindex >= 0)
@@ -852,7 +852,7 @@ static struct tc_u_knode *u32_init_knode(struct net *net, struct tcf_proto *tp,
852852

853853
/* bump reference count as long as we hold pointer to structure */
854854
if (ht)
855-
ht->refcnt++;
855+
refcount_inc(&ht->refcnt);
856856

857857
return new;
858858
}
@@ -932,7 +932,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
932932

933933
ht_old = rtnl_dereference(n->ht_down);
934934
if (ht_old)
935-
ht_old->refcnt++;
935+
refcount_inc(&ht_old->refcnt);
936936
}
937937
__u32_destroy_key(new);
938938
return err;
@@ -980,7 +980,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
980980
return err;
981981
}
982982
}
983-
ht->refcnt = 1;
983+
refcount_set(&ht->refcnt, 1);
984984
ht->divisor = divisor;
985985
ht->handle = handle;
986986
ht->prio = tp->prio;

tools/testing/selftests/tc-testing/tc-tests/filters/u32.json

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,5 +272,62 @@
272272
"teardown": [
273273
"$TC qdisc del dev $DEV1 parent root drr"
274274
]
275+
},
276+
{
277+
"id": "bd32",
278+
"name": "Try to delete hashtable referenced by another u32 filter",
279+
"category": [
280+
"filter",
281+
"u32"
282+
],
283+
"plugins": {
284+
"requires": "nsPlugin"
285+
},
286+
"setup": [
287+
"$TC qdisc add dev $DEV1 parent root handle 10: drr",
288+
"$TC filter add dev $DEV1 parent 10:0 protocol ip prio 2 handle 1: u32 divisor 1",
289+
"$TC filter add dev $DEV1 parent 10:0 protocol ip prio 2 u32 ht 800: match ip src any link 1:"
290+
],
291+
"cmdUnderTest": "$TC filter delete dev $DEV1 parent 10: prio 2 handle 1: u32",
292+
"expExitCode": "2",
293+
"verifyCmd": "$TC filter show dev $DEV1",
294+
"matchPattern": "protocol ip pref 2 u32 chain 0 fh 1:",
295+
"matchCount": "1",
296+
"teardown": [
297+
"$TC qdisc del dev $DEV1 parent root drr"
298+
]
299+
},
300+
{
301+
"id": "4585",
302+
"name": "Delete small tree of u32 hashtables and filters",
303+
"category": [
304+
"filter",
305+
"u32"
306+
],
307+
"plugins": {
308+
"requires": "nsPlugin"
309+
},
310+
"setup": [
311+
"$TC qdisc add dev $DEV1 parent root handle 10: drr",
312+
"$TC filter add dev $DEV1 parent 10:0 protocol ip prio 2 handle 1: u32 divisor 1",
313+
"$TC filter add dev $DEV1 parent 10:0 protocol ip prio 2 handle 2: u32 divisor 1",
314+
"$TC filter add dev $DEV1 parent 10:0 protocol ip prio 2 handle 3: u32 divisor 2",
315+
"$TC filter add dev $DEV1 parent 10:0 protocol ip prio 2 handle 4: u32 divisor 1",
316+
"$TC filter add dev $DEV1 parent 10:0 protocol ip prio 2 u32 ht 1: match ip src any action drop",
317+
"$TC filter add dev $DEV1 parent 10:0 protocol ip prio 2 u32 ht 2: match ip src any action drop",
318+
"$TC filter add dev $DEV1 parent 10:0 protocol ip prio 2 u32 ht 3: match ip src any link 2:",
319+
"$TC filter add dev $DEV1 parent 10:0 protocol ip prio 2 u32 ht 3: match ip src any link 1:",
320+
"$TC filter add dev $DEV1 parent 10:0 protocol ip prio 2 u32 ht 4: match ip src any action drop",
321+
"$TC filter add dev $DEV1 parent 10:0 protocol ip prio 2 u32 ht 800: match ip src any link 3:",
322+
"$TC filter add dev $DEV1 parent 10:0 protocol ip prio 2 u32 ht 800: match ip src any link 4:"
323+
],
324+
"cmdUnderTest": "$TC filter delete dev $DEV1 parent 10:",
325+
"expExitCode": "0",
326+
"verifyCmd": "$TC filter show dev $DEV1",
327+
"matchPattern": "protocol ip pref 2 u32",
328+
"matchCount": "0",
329+
"teardown": [
330+
"$TC qdisc del dev $DEV1 parent root drr"
331+
]
275332
}
276333
]

0 commit comments

Comments
 (0)