Skip to content

Commit 16af606

Browse files
w1ldptrdavem330
authored andcommitted
net: sched: implement reference counted action release
Implement helper delete function that uses new action ops 'delete', instead of destroying action directly. This is required so act API could delete actions by index, without holding any references to action that is being deleted. Implement function __tcf_action_put() that releases reference to action and frees it, if necessary. Refactor action deletion code to use new put function and not to rely on rtnl lock. Remove rtnl lock assertions that are no longer needed. Reviewed-by: Marcelo Ricardo Leitner <[email protected]> Signed-off-by: Vlad Buslov <[email protected]> Signed-off-by: Jiri Pirko <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent b409074 commit 16af606

File tree

2 files changed

+62
-23
lines changed

2 files changed

+62
-23
lines changed

net/sched/act_api.c

Lines changed: 62 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -90,21 +90,39 @@ static void free_tcf(struct tc_action *p)
9090
kfree(p);
9191
}
9292

93-
static void tcf_idr_remove(struct tcf_idrinfo *idrinfo, struct tc_action *p)
93+
static void tcf_action_cleanup(struct tc_action *p)
9494
{
95-
spin_lock(&idrinfo->lock);
96-
idr_remove(&idrinfo->action_idr, p->tcfa_index);
97-
spin_unlock(&idrinfo->lock);
95+
if (p->ops->cleanup)
96+
p->ops->cleanup(p);
97+
9898
gen_kill_estimator(&p->tcfa_rate_est);
9999
free_tcf(p);
100100
}
101101

102+
static int __tcf_action_put(struct tc_action *p, bool bind)
103+
{
104+
struct tcf_idrinfo *idrinfo = p->idrinfo;
105+
106+
if (refcount_dec_and_lock(&p->tcfa_refcnt, &idrinfo->lock)) {
107+
if (bind)
108+
atomic_dec(&p->tcfa_bindcnt);
109+
idr_remove(&idrinfo->action_idr, p->tcfa_index);
110+
spin_unlock(&idrinfo->lock);
111+
112+
tcf_action_cleanup(p);
113+
return 1;
114+
}
115+
116+
if (bind)
117+
atomic_dec(&p->tcfa_bindcnt);
118+
119+
return 0;
120+
}
121+
102122
int __tcf_idr_release(struct tc_action *p, bool bind, bool strict)
103123
{
104124
int ret = 0;
105125

106-
ASSERT_RTNL();
107-
108126
/* Release with strict==1 and bind==0 is only called through act API
109127
* interface (classifiers always bind). Only case when action with
110128
* positive reference count and zero bind count can exist is when it was
@@ -118,18 +136,11 @@ int __tcf_idr_release(struct tc_action *p, bool bind, bool strict)
118136
* are acceptable.
119137
*/
120138
if (p) {
121-
if (bind)
122-
atomic_dec(&p->tcfa_bindcnt);
123-
else if (strict && atomic_read(&p->tcfa_bindcnt) > 0)
139+
if (!bind && strict && atomic_read(&p->tcfa_bindcnt) > 0)
124140
return -EPERM;
125141

126-
if (atomic_read(&p->tcfa_bindcnt) <= 0 &&
127-
refcount_dec_and_test(&p->tcfa_refcnt)) {
128-
if (p->ops->cleanup)
129-
p->ops->cleanup(p);
130-
tcf_idr_remove(p->idrinfo, p);
142+
if (__tcf_action_put(p, bind))
131143
ret = ACT_P_DELETED;
132-
}
133144
}
134145

135146
return ret;
@@ -340,11 +351,7 @@ int tcf_idr_delete_index(struct tc_action_net *tn, u32 index)
340351
p->tcfa_index));
341352
spin_unlock(&idrinfo->lock);
342353

343-
if (p->ops->cleanup)
344-
p->ops->cleanup(p);
345-
346-
gen_kill_estimator(&p->tcfa_rate_est);
347-
free_tcf(p);
354+
tcf_action_cleanup(p);
348355
module_put(owner);
349356
return 0;
350357
}
@@ -615,6 +622,11 @@ int tcf_action_destroy(struct list_head *actions, int bind)
615622
return ret;
616623
}
617624

625+
static int tcf_action_put(struct tc_action *p)
626+
{
627+
return __tcf_action_put(p, false);
628+
}
629+
618630
int
619631
tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
620632
{
@@ -1092,6 +1104,35 @@ static int tca_action_flush(struct net *net, struct nlattr *nla,
10921104
return err;
10931105
}
10941106

1107+
static int tcf_action_delete(struct net *net, struct list_head *actions,
1108+
struct netlink_ext_ack *extack)
1109+
{
1110+
struct tc_action *a, *tmp;
1111+
u32 act_index;
1112+
int ret;
1113+
1114+
list_for_each_entry_safe(a, tmp, actions, list) {
1115+
const struct tc_action_ops *ops = a->ops;
1116+
1117+
/* Actions can be deleted concurrently so we must save their
1118+
* type and id to search again after reference is released.
1119+
*/
1120+
act_index = a->tcfa_index;
1121+
1122+
list_del(&a->list);
1123+
if (tcf_action_put(a)) {
1124+
/* last reference, action was deleted concurrently */
1125+
module_put(ops->owner);
1126+
} else {
1127+
/* now do the delete */
1128+
ret = ops->delete(net, act_index);
1129+
if (ret < 0)
1130+
return ret;
1131+
}
1132+
}
1133+
return 0;
1134+
}
1135+
10951136
static int
10961137
tcf_del_notify(struct net *net, struct nlmsghdr *n, struct list_head *actions,
10971138
u32 portid, size_t attr_size, struct netlink_ext_ack *extack)
@@ -1112,7 +1153,7 @@ tcf_del_notify(struct net *net, struct nlmsghdr *n, struct list_head *actions,
11121153
}
11131154

11141155
/* now do the delete */
1115-
ret = tcf_action_destroy(actions, 0);
1156+
ret = tcf_action_delete(net, actions, extack);
11161157
if (ret < 0) {
11171158
NL_SET_ERR_MSG(extack, "Failed to delete TC action");
11181159
kfree_skb(skb);
@@ -1164,7 +1205,6 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
11641205
if (event == RTM_GETACTION)
11651206
ret = tcf_get_notify(net, portid, n, &actions, event, extack);
11661207
else { /* delete */
1167-
cleanup_a(&actions, 1); /* lookup took reference */
11681208
ret = tcf_del_notify(net, n, &actions, portid, attr_size, extack);
11691209
if (ret)
11701210
goto err;

net/sched/cls_api.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1611,7 +1611,6 @@ void tcf_exts_destroy(struct tcf_exts *exts)
16111611
#ifdef CONFIG_NET_CLS_ACT
16121612
LIST_HEAD(actions);
16131613

1614-
ASSERT_RTNL();
16151614
tcf_exts_to_list(exts, &actions);
16161615
tcf_action_destroy(&actions, TCA_ACT_UNBIND);
16171616
kfree(exts->actions);

0 commit comments

Comments
 (0)