Skip to content

Commit 6b78deb

Browse files
tammelakuba-moo
authored andcommitted
net/sched: cls_u32: replace int refcounts with proper refcounts
Proper refcounts will always warn splat when something goes wrong, be it underflow, saturation or object resurrection. As these are always a source of bugs, use it in cls_u32 as a safeguard to prevent/catch issues. Another benefit is that the refcount API self documents the code, making clear when transitions to dead are expected. For such an update we had to make minor adaptations on u32 to fit the refcount API. First we set explicitly to '1' when objects are created, then the objects are alive until a 1 -> 0 happens, which is then released appropriately. The above made clear some redundant operations in the u32 code around the root_ht handling that were removed. The root_ht is created with a refcnt set to 1. Then when it's associated with tcf_proto it increments the refcnt to 2. Throughout the entire code the root_ht is an exceptional case and can never be referenced, therefore the refcnt never incremented/decremented. Its lifetime is always bound to tcf_proto, meaning if you delete tcf_proto the root_ht is deleted as well. The code made up for the fact that root_ht refcnt is 2 and did a double decrement to free it, which is not a fit for the refcount API. Even though refcount_t is implemented using atomics, we should observe a negligible control plane impact. Signed-off-by: Pedro Tammela <pctammela@mojatatu.com> Acked-by: Jamal Hadi Salim <jhs@mojatatu.com> Link: https://lore.kernel.org/r/20231114141856.974326-2-pctammela@mojatatu.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
1 parent 289354f commit 6b78deb

File tree

1 file changed

+18
-18
lines changed

1 file changed

+18
-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;

0 commit comments

Comments
 (0)