netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 1/1] net: sched: bump refcount for new action in ACT replace mode
@ 2021-03-29 22:53 Kumar Kartikeya Dwivedi
  2021-03-30 10:32 ` Vlad Buslov
                   ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2021-03-29 22:53 UTC (permalink / raw)
  To: vladbu
  Cc: davem, jhs, jiri, kuba, linux-kernel, memxor, netdev, toke,
	xiyou.wangcong

Currently, action creation using ACT API in replace mode is buggy.
When invoking for non-existent action index 42,

	tc action replace action bpf obj foo.o sec <xyz> index 42

kernel creates the action, fills up the netlink response, and then just
deletes the action after notifying userspace.

	tc action show action bpf

doesn't list the action.

This happens due to the following sequence when ovr = 1 (replace mode)
is enabled:

tcf_idr_check_alloc is used to atomically check and either obtain
reference for existing action at index, or reserve the index slot using
a dummy entry (ERR_PTR(-EBUSY)).

This is necessary as pointers to these actions will be held after
dropping the idrinfo lock, so bumping the reference count is necessary
as we need to insert the actions, and notify userspace by dumping their
attributes. Finally, we drop the reference we took using the
tcf_action_put_many call in tcf_action_add. However, for the case where
a new action is created due to free index, its refcount remains one.
This when paired with the put_many call leads to the kernel setting up
the action, notifying userspace of its creation, and then tearing it
down. For existing actions, the refcount is still held so they remain
unaffected.

Fortunately due to rtnl_lock serialization requirement, such an action
with refcount == 1 will not be concurrently deleted by anything else, at
best CLS API can move its refcount up and down by binding to it after it
has been published from tcf_idr_insert_many. Since refcount is atleast
one until put_many call, CLS API cannot delete it. Also __tcf_action_put
release path already ensures deterministic outcome (either new action
will be created or existing action will be reused in case CLS API tries
to bind to action concurrently) due to idr lock serialization.

We fix this by making refcount of newly created actions as 2 in ACT API
replace mode. A relaxed store will suffice as visibility is ensured only
after the tcf_idr_insert_many call.

Note that in case of creation or overwriting using CLS API only (i.e.
bind = 1), overwriting existing action object is not allowed, and any
such request is silently ignored (without error).

The refcount bump that occurs in tcf_idr_check_alloc call there for
existing action will pair with tcf_exts_destroy call made from the
owner module for the same action. In case of action creation, there
is no existing action, so no tcf_exts_destroy callback happens.

This means no code changes for CLS API.

Fixes: cae422f379f3 ("net: sched: use reference counting action init")
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
Changelog:

v1 -> v2
Remove erroneous tcf_action_put_many call in tcf_exts_validate (Vlad)
Isolate refcount bump to ACT API in replace mode
---
 net/sched/act_api.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index b919826939e0..43cceb924976 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -1042,6 +1042,9 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
 	if (err != ACT_P_CREATED)
 		module_put(a_o->owner);

+	if (!bind && ovr && err == ACT_P_CREATED)
+		refcount_set(&a->tcfa_refcnt, 2);
+
 	return a;

 err_out:
--
2.30.2


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 1/1] net: sched: bump refcount for new action in ACT replace mode
  2021-03-29 22:53 [PATCH v2 1/1] net: sched: bump refcount for new action in ACT replace mode Kumar Kartikeya Dwivedi
@ 2021-03-30 10:32 ` Vlad Buslov
  2021-03-30 10:41 ` [PATCH net-next] tc-testing: add simple action change test Vlad Buslov
  2021-03-31  4:40 ` [PATCH v2 1/1] net: sched: bump refcount for new action in ACT replace mode Cong Wang
  2 siblings, 0 replies; 9+ messages in thread
From: Vlad Buslov @ 2021-03-30 10:32 UTC (permalink / raw)
  To: Kumar Kartikeya Dwivedi
  Cc: davem, jhs, jiri, kuba, linux-kernel, netdev, toke, xiyou.wangcong


On Tue 30 Mar 2021 at 01:53, Kumar Kartikeya Dwivedi <memxor@gmail.com> wrote:
> Currently, action creation using ACT API in replace mode is buggy.
> When invoking for non-existent action index 42,
>
> 	tc action replace action bpf obj foo.o sec <xyz> index 42
>
> kernel creates the action, fills up the netlink response, and then just
> deletes the action after notifying userspace.
>
> 	tc action show action bpf
>
> doesn't list the action.

Okay, I understand the issue now. I'll also add a tdc test for it.

>
> This happens due to the following sequence when ovr = 1 (replace mode)
> is enabled:
>
> tcf_idr_check_alloc is used to atomically check and either obtain
> reference for existing action at index, or reserve the index slot using
> a dummy entry (ERR_PTR(-EBUSY)).
>
> This is necessary as pointers to these actions will be held after
> dropping the idrinfo lock, so bumping the reference count is necessary
> as we need to insert the actions, and notify userspace by dumping their
> attributes. Finally, we drop the reference we took using the
> tcf_action_put_many call in tcf_action_add. However, for the case where
> a new action is created due to free index, its refcount remains one.
> This when paired with the put_many call leads to the kernel setting up
> the action, notifying userspace of its creation, and then tearing it
> down. For existing actions, the refcount is still held so they remain
> unaffected.
>
> Fortunately due to rtnl_lock serialization requirement, such an action
> with refcount == 1 will not be concurrently deleted by anything else, at
> best CLS API can move its refcount up and down by binding to it after it
> has been published from tcf_idr_insert_many. Since refcount is atleast
> one until put_many call, CLS API cannot delete it. Also __tcf_action_put
> release path already ensures deterministic outcome (either new action
> will be created or existing action will be reused in case CLS API tries
> to bind to action concurrently) due to idr lock serialization.
>
> We fix this by making refcount of newly created actions as 2 in ACT API
> replace mode. A relaxed store will suffice as visibility is ensured only
> after the tcf_idr_insert_many call.
>
> Note that in case of creation or overwriting using CLS API only (i.e.
> bind = 1), overwriting existing action object is not allowed, and any
> such request is silently ignored (without error).
>
> The refcount bump that occurs in tcf_idr_check_alloc call there for
> existing action will pair with tcf_exts_destroy call made from the
> owner module for the same action. In case of action creation, there
> is no existing action, so no tcf_exts_destroy callback happens.
>
> This means no code changes for CLS API.
>
> Fixes: cae422f379f3 ("net: sched: use reference counting action init")
> Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> ---
> Changelog:
>
> v1 -> v2
> Remove erroneous tcf_action_put_many call in tcf_exts_validate (Vlad)
> Isolate refcount bump to ACT API in replace mode
> ---
>  net/sched/act_api.c | 3 +++
>  1 file changed, 3 insertions(+)
>
> diff --git a/net/sched/act_api.c b/net/sched/act_api.c
> index b919826939e0..43cceb924976 100644
> --- a/net/sched/act_api.c
> +++ b/net/sched/act_api.c
> @@ -1042,6 +1042,9 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
>  	if (err != ACT_P_CREATED)
>  		module_put(a_o->owner);
>
> +	if (!bind && ovr && err == ACT_P_CREATED)
> +		refcount_set(&a->tcfa_refcnt, 2);
> +
>  	return a;
>
>  err_out:

Reviewed-and-tested-by: Vlad Buslov <vladbu@nvidia.com>


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH net-next] tc-testing: add simple action change test
  2021-03-29 22:53 [PATCH v2 1/1] net: sched: bump refcount for new action in ACT replace mode Kumar Kartikeya Dwivedi
  2021-03-30 10:32 ` Vlad Buslov
@ 2021-03-30 10:41 ` Vlad Buslov
  2021-03-31  0:20   ` patchwork-bot+netdevbpf
  2021-03-31  4:40 ` [PATCH v2 1/1] net: sched: bump refcount for new action in ACT replace mode Cong Wang
  2 siblings, 1 reply; 9+ messages in thread
From: Vlad Buslov @ 2021-03-30 10:41 UTC (permalink / raw)
  To: memxor
  Cc: xiyou.wangcong, davem, jhs, jiri, kuba, toke, linux-kernel,
	netdev, Vlad Buslov

Use act_simple to verify that action created with 'tc actions change'
command exists after command returns. The goal is to verify internal action
API reference counting to ensure that the case when netlink message has
NLM_F_REPLACE flag set but action with specified index doesn't exist is
handled correctly.

Signed-off-by: Vlad Buslov <vladbu@nvidia.com>
---
 .../tc-testing/tc-tests/actions/simple.json   | 24 +++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/simple.json b/tools/testing/selftests/tc-testing/tc-tests/actions/simple.json
index 8e8c1ae12260..e15f708b0fa4 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/actions/simple.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/actions/simple.json
@@ -23,6 +23,30 @@
             "$TC actions flush action simple"
         ]
     },
+    {
+        "id": "4297",
+        "name": "Add simple action with change command",
+        "category": [
+            "actions",
+            "simple"
+        ],
+        "setup": [
+            [
+                "$TC actions flush action simple",
+                0,
+                1,
+                255
+            ]
+        ],
+        "cmdUnderTest": "$TC actions change action simple sdata \"Not changed\" index 60",
+        "expExitCode": "0",
+        "verifyCmd": "$TC actions list action simple",
+        "matchPattern": "action order [0-9]*: Simple <Not changed>.*index 60 ref",
+        "matchCount": "1",
+        "teardown": [
+            "$TC actions flush action simple"
+        ]
+    },
     {
         "id": "6d4c",
         "name": "Add simple action with duplicate index",
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH net-next] tc-testing: add simple action change test
  2021-03-30 10:41 ` [PATCH net-next] tc-testing: add simple action change test Vlad Buslov
@ 2021-03-31  0:20   ` patchwork-bot+netdevbpf
  0 siblings, 0 replies; 9+ messages in thread
From: patchwork-bot+netdevbpf @ 2021-03-31  0:20 UTC (permalink / raw)
  To: Vlad Buslov
  Cc: memxor, xiyou.wangcong, davem, jhs, jiri, kuba, toke,
	linux-kernel, netdev

Hello:

This patch was applied to netdev/net-next.git (refs/heads/master):

On Tue, 30 Mar 2021 13:41:10 +0300 you wrote:
> Use act_simple to verify that action created with 'tc actions change'
> command exists after command returns. The goal is to verify internal action
> API reference counting to ensure that the case when netlink message has
> NLM_F_REPLACE flag set but action with specified index doesn't exist is
> handled correctly.
> 
> Signed-off-by: Vlad Buslov <vladbu@nvidia.com>
> 
> [...]

Here is the summary with links:
  - [net-next] tc-testing: add simple action change test
    https://git.kernel.org/netdev/net-next/c/e48792a9ec78

You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 1/1] net: sched: bump refcount for new action in ACT replace mode
  2021-03-29 22:53 [PATCH v2 1/1] net: sched: bump refcount for new action in ACT replace mode Kumar Kartikeya Dwivedi
  2021-03-30 10:32 ` Vlad Buslov
  2021-03-30 10:41 ` [PATCH net-next] tc-testing: add simple action change test Vlad Buslov
@ 2021-03-31  4:40 ` Cong Wang
  2021-03-31  7:59   ` Vlad Buslov
                     ` (2 more replies)
  2 siblings, 3 replies; 9+ messages in thread
From: Cong Wang @ 2021-03-31  4:40 UTC (permalink / raw)
  To: Kumar Kartikeya Dwivedi
  Cc: Vlad Buslov, David Miller, Jamal Hadi Salim, Jiri Pirko,
	Jakub Kicinski, LKML, Linux Kernel Network Developers,
	Toke Høiland-Jørgensen

On Mon, Mar 29, 2021 at 3:55 PM Kumar Kartikeya Dwivedi
<memxor@gmail.com> wrote:
> diff --git a/net/sched/act_api.c b/net/sched/act_api.c
> index b919826939e0..43cceb924976 100644
> --- a/net/sched/act_api.c
> +++ b/net/sched/act_api.c
> @@ -1042,6 +1042,9 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
>         if (err != ACT_P_CREATED)
>                 module_put(a_o->owner);
>
> +       if (!bind && ovr && err == ACT_P_CREATED)
> +               refcount_set(&a->tcfa_refcnt, 2);
> +

Hmm, if we set the refcnt to 2 here, how could tcf_action_destroy()
destroy them when we rollback from a failure in the middle of the loop
in tcf_action_init()?

Thanks.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 1/1] net: sched: bump refcount for new action in ACT replace mode
  2021-03-31  4:40 ` [PATCH v2 1/1] net: sched: bump refcount for new action in ACT replace mode Cong Wang
@ 2021-03-31  7:59   ` Vlad Buslov
  2021-03-31  8:37   ` [PATCH net-next v3] " Kumar Kartikeya Dwivedi
  2021-03-31  8:38   ` [PATCH v2 1/1] " Kumar Kartikeya Dwivedi
  2 siblings, 0 replies; 9+ messages in thread
From: Vlad Buslov @ 2021-03-31  7:59 UTC (permalink / raw)
  To: Cong Wang
  Cc: Kumar Kartikeya Dwivedi, David Miller, Jamal Hadi Salim,
	Jiri Pirko, Jakub Kicinski, LKML,
	Linux Kernel Network Developers,
	Toke Høiland-Jørgensen


On Wed 31 Mar 2021 at 07:40, Cong Wang <xiyou.wangcong@gmail.com> wrote:
> On Mon, Mar 29, 2021 at 3:55 PM Kumar Kartikeya Dwivedi
> <memxor@gmail.com> wrote:
>> diff --git a/net/sched/act_api.c b/net/sched/act_api.c
>> index b919826939e0..43cceb924976 100644
>> --- a/net/sched/act_api.c
>> +++ b/net/sched/act_api.c
>> @@ -1042,6 +1042,9 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
>>         if (err != ACT_P_CREATED)
>>                 module_put(a_o->owner);
>>
>> +       if (!bind && ovr && err == ACT_P_CREATED)
>> +               refcount_set(&a->tcfa_refcnt, 2);
>> +
>
> Hmm, if we set the refcnt to 2 here, how could tcf_action_destroy()
> destroy them when we rollback from a failure in the middle of the loop
> in tcf_action_init()?
>
> Thanks.

Hmm, you might be right. Also, the error handling code in
tcf_action_init() looks incorrect:

err:
	tcf_action_destroy(actions, bind);
err_mod:
	for (i = 0; i < TCA_ACT_MAX_PRIO; i++) {
		if (ops[i])
			module_put(ops[i]->owner);
	}
	return err;

It looks like here the modules for all actions that successfully
completed their init has already been release by either
tcf_action_init_1() on action overwrite or by tcf_action_destroy() on
action create. I'll try to come up with tests that validate these corner
cases.

Regards,
Vlad

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH net-next v3] net: sched: bump refcount for new action in ACT replace mode
  2021-03-31  4:40 ` [PATCH v2 1/1] net: sched: bump refcount for new action in ACT replace mode Cong Wang
  2021-03-31  7:59   ` Vlad Buslov
@ 2021-03-31  8:37   ` Kumar Kartikeya Dwivedi
  2021-03-31 14:08     ` Vlad Buslov
  2021-03-31  8:38   ` [PATCH v2 1/1] " Kumar Kartikeya Dwivedi
  2 siblings, 1 reply; 9+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2021-03-31  8:37 UTC (permalink / raw)
  To: xiyou.wangcong
  Cc: davem, jhs, jiri, kuba, linux-kernel, memxor, netdev, toke, vladbu

Currently, action creation using ACT API in replace mode is buggy.  When
invoking for non-existent action index 42,

	tc action replace action bpf obj foo.o sec <xyz> index 42

kernel creates the action, fills up the netlink response, and then just
deletes the action while notifying userspace of success.

	tc action show action bpf

doesn't list the action.

This happens due to the following sequence when ovr = 1 (replace mode)
is enabled:

tcf_idr_check_alloc is used to atomically check and either obtain
reference for existing action at index, or reserve the index slot using
a dummy entry (ERR_PTR(-EBUSY)).

This is necessary as pointers to these actions will be held after
dropping the idrinfo lock, so bumping the reference count is necessary
as we need to insert the actions, and notify userspace by dumping their
attributes. Finally, we drop the reference we took using the
tcf_action_put_many call in tcf_action_add. However, for the case where
a new action is created due to free index, its refcount remains one.
This when paired with the put_many call leads to the kernel setting up
the action, notifying userspace of its creation, and then tearing it
down. For existing actions, the refcount is still held so they remain
unaffected.

Fortunately due to rtnl_lock serialization requirement, such an action
with refcount == 1 will not be concurrently deleted by anything else, at
best CLS API can move its refcount up and down by binding to it after it
has been published from tcf_idr_insert_many. Since refcount is atleast
one until put_many call, CLS API cannot delete it. Also __tcf_action_put
release path already ensures deterministic outcome (either new action
will be created or existing action will be reused in case CLS API tries
to bind to action concurrently) due to idr lock serialization.

We fix this by making refcount of newly created actions as 2 in ACT API
replace mode. A relaxed store will suffice as visibility is ensured only
after the tcf_idr_insert_many call.

We also remember the new actions that we took an additional reference on,
and relinquish the temporal reference during rollback on failure.

Note that in case of creation or overwriting using CLS API only (i.e.
bind = 1), overwriting existing action object is not allowed, and any
such request is silently ignored (without error).

The refcount bump that occurs in tcf_idr_check_alloc call there for
existing action will pair with tcf_exts_destroy call made from the owner
module for the same action. In case of action creation, there is no
existing action, so no tcf_exts_destroy callback happens.

This means no code changes for CLS API.

Fixes: cae422f379f3 ("net: sched: use reference counting action init")
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
Changelog:

v2 -> v3
Cleanup new action on rollback after raising refcount (Cong)

v1 -> v2
Remove erroneous tcf_action_put_many call in tcf_exts_validate (Vlad)
Isolate refcount bump to ACT API in replace mode
---
 include/net/act_api.h |  2 +-
 net/sched/act_api.c   | 24 ++++++++++++++++++++++--
 net/sched/cls_api.c   |  2 +-
 3 files changed, 24 insertions(+), 4 deletions(-)

diff --git a/include/net/act_api.h b/include/net/act_api.h
index 2bf3092ae7ec..a01ff5fa641e 100644
--- a/include/net/act_api.h
+++ b/include/net/act_api.h
@@ -194,7 +194,7 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
 				    struct nlattr *nla, struct nlattr *est,
 				    char *name, int ovr, int bind,
 				    struct tc_action_ops *ops, bool rtnl_held,
-				    struct netlink_ext_ack *extack);
+				    struct netlink_ext_ack *extack, bool *ref);
 int tcf_action_dump(struct sk_buff *skb, struct tc_action *actions[], int bind,
 		    int ref, bool terse);
 int tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int, int);
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index b919826939e0..718bc197b9a7 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -993,7 +993,7 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
 				    struct nlattr *nla, struct nlattr *est,
 				    char *name, int ovr, int bind,
 				    struct tc_action_ops *a_o, bool rtnl_held,
-				    struct netlink_ext_ack *extack)
+				    struct netlink_ext_ack *extack, bool *ref)
 {
 	struct nla_bitfield32 flags = { 0, 0 };
 	u8 hw_stats = TCA_ACT_HW_STATS_ANY;
@@ -1042,6 +1042,13 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
 	if (err != ACT_P_CREATED)
 		module_put(a_o->owner);

+	if (!bind && ovr && err == ACT_P_CREATED) {
+		if (ref)
+			*ref = true;
+
+		refcount_set(&a->tcfa_refcnt, 2);
+	}
+
 	return a;

 err_out:
@@ -1062,10 +1069,13 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
 	struct tc_action_ops *ops[TCA_ACT_MAX_PRIO] = {};
 	struct nlattr *tb[TCA_ACT_MAX_PRIO + 1];
 	struct tc_action *act;
+	u32 new_actions = 0;
 	size_t sz = 0;
 	int err;
 	int i;

+	BUILD_BUG_ON(TCA_ACT_MAX_PRIO > sizeof(new_actions) * 8);
+
 	err = nla_parse_nested_deprecated(tb, TCA_ACT_MAX_PRIO, nla, NULL,
 					  extack);
 	if (err < 0)
@@ -1083,8 +1093,9 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
 	}

 	for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) {
+		bool ovr_new = false;
 		act = tcf_action_init_1(net, tp, tb[i], est, name, ovr, bind,
-					ops[i - 1], rtnl_held, extack);
+					ops[i - 1], rtnl_held, extack, &ovr_new);
 		if (IS_ERR(act)) {
 			err = PTR_ERR(act);
 			goto err;
@@ -1092,6 +1103,10 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
 		sz += tcf_action_fill_size(act);
 		/* Start from index 0 */
 		actions[i - 1] = act;
+
+		/* remember new actions that we take a reference on */
+		if (ovr_new)
+			new_actions |= 1UL << (i - 1);
 	}

 	/* We have to commit them all together, because if any error happened in
@@ -1103,6 +1118,11 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
 	return i - 1;

 err:
+	/* reset the reference back to 1 in case of error */
+	for (i = 0; i < TCA_ACT_MAX_PRIO && actions[i]; i++) {
+		if (new_actions & (1UL << i))
+			refcount_set(&actions[i]->tcfa_refcnt, 1);
+	}
 	tcf_action_destroy(actions, bind);
 err_mod:
 	for (i = 0; i < TCA_ACT_MAX_PRIO; i++) {
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index d3db70865d66..4f4a7355b1e1 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -3052,7 +3052,7 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
 			act = tcf_action_init_1(net, tp, tb[exts->police],
 						rate_tlv, "police", ovr,
 						TCA_ACT_BIND, a_o, rtnl_held,
-						extack);
+						extack, NULL);
 			if (IS_ERR(act)) {
 				module_put(a_o->owner);
 				return PTR_ERR(act);
--
2.30.2


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 1/1] net: sched: bump refcount for new action in ACT replace mode
  2021-03-31  4:40 ` [PATCH v2 1/1] net: sched: bump refcount for new action in ACT replace mode Cong Wang
  2021-03-31  7:59   ` Vlad Buslov
  2021-03-31  8:37   ` [PATCH net-next v3] " Kumar Kartikeya Dwivedi
@ 2021-03-31  8:38   ` Kumar Kartikeya Dwivedi
  2 siblings, 0 replies; 9+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2021-03-31  8:38 UTC (permalink / raw)
  To: Cong Wang
  Cc: Vlad Buslov, David Miller, Jamal Hadi Salim, Jiri Pirko,
	Jakub Kicinski, LKML, Linux Kernel Network Developers,
	Toke Høiland-Jørgensen

On Wed, Mar 31, 2021 at 10:10:45AM IST, Cong Wang wrote:
> On Mon, Mar 29, 2021 at 3:55 PM Kumar Kartikeya Dwivedi
> <memxor@gmail.com> wrote:
> > diff --git a/net/sched/act_api.c b/net/sched/act_api.c
> > index b919826939e0..43cceb924976 100644
> > --- a/net/sched/act_api.c
> > +++ b/net/sched/act_api.c
> > @@ -1042,6 +1042,9 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
> >         if (err != ACT_P_CREATED)
> >                 module_put(a_o->owner);
> >
> > +       if (!bind && ovr && err == ACT_P_CREATED)
> > +               refcount_set(&a->tcfa_refcnt, 2);
> > +
>
> Hmm, if we set the refcnt to 2 here, how could tcf_action_destroy()
> destroy them when we rollback from a failure in the middle of the loop
> in tcf_action_init()?
>

You are right, it wouldn't. I sent a new version with a fix. PTAL.

> Thanks.

--
Kartikeya

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH net-next v3] net: sched: bump refcount for new action in ACT replace mode
  2021-03-31  8:37   ` [PATCH net-next v3] " Kumar Kartikeya Dwivedi
@ 2021-03-31 14:08     ` Vlad Buslov
  0 siblings, 0 replies; 9+ messages in thread
From: Vlad Buslov @ 2021-03-31 14:08 UTC (permalink / raw)
  To: Kumar Kartikeya Dwivedi
  Cc: xiyou.wangcong, davem, jhs, jiri, kuba, linux-kernel, netdev, toke


On Wed 31 Mar 2021 at 11:37, Kumar Kartikeya Dwivedi <memxor@gmail.com> wrote:
> Currently, action creation using ACT API in replace mode is buggy.  When
> invoking for non-existent action index 42,
>
> 	tc action replace action bpf obj foo.o sec <xyz> index 42
>
> kernel creates the action, fills up the netlink response, and then just
> deletes the action while notifying userspace of success.
>
> 	tc action show action bpf
>
> doesn't list the action.
>
> This happens due to the following sequence when ovr = 1 (replace mode)
> is enabled:
>
> tcf_idr_check_alloc is used to atomically check and either obtain
> reference for existing action at index, or reserve the index slot using
> a dummy entry (ERR_PTR(-EBUSY)).
>
> This is necessary as pointers to these actions will be held after
> dropping the idrinfo lock, so bumping the reference count is necessary
> as we need to insert the actions, and notify userspace by dumping their
> attributes. Finally, we drop the reference we took using the
> tcf_action_put_many call in tcf_action_add. However, for the case where
> a new action is created due to free index, its refcount remains one.
> This when paired with the put_many call leads to the kernel setting up
> the action, notifying userspace of its creation, and then tearing it
> down. For existing actions, the refcount is still held so they remain
> unaffected.
>
> Fortunately due to rtnl_lock serialization requirement, such an action
> with refcount == 1 will not be concurrently deleted by anything else, at
> best CLS API can move its refcount up and down by binding to it after it
> has been published from tcf_idr_insert_many. Since refcount is atleast
> one until put_many call, CLS API cannot delete it. Also __tcf_action_put
> release path already ensures deterministic outcome (either new action
> will be created or existing action will be reused in case CLS API tries
> to bind to action concurrently) due to idr lock serialization.
>
> We fix this by making refcount of newly created actions as 2 in ACT API
> replace mode. A relaxed store will suffice as visibility is ensured only
> after the tcf_idr_insert_many call.
>
> We also remember the new actions that we took an additional reference on,
> and relinquish the temporal reference during rollback on failure.
>
> Note that in case of creation or overwriting using CLS API only (i.e.
> bind = 1), overwriting existing action object is not allowed, and any
> such request is silently ignored (without error).
>
> The refcount bump that occurs in tcf_idr_check_alloc call there for
> existing action will pair with tcf_exts_destroy call made from the owner
> module for the same action. In case of action creation, there is no
> existing action, so no tcf_exts_destroy callback happens.
>
> This means no code changes for CLS API.
>
> Fixes: cae422f379f3 ("net: sched: use reference counting action init")
> Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> ---
> Changelog:
>
> v2 -> v3
> Cleanup new action on rollback after raising refcount (Cong)
>
> v1 -> v2
> Remove erroneous tcf_action_put_many call in tcf_exts_validate (Vlad)
> Isolate refcount bump to ACT API in replace mode
> ---
>  include/net/act_api.h |  2 +-
>  net/sched/act_api.c   | 24 ++++++++++++++++++++++--
>  net/sched/cls_api.c   |  2 +-
>  3 files changed, 24 insertions(+), 4 deletions(-)
>
> diff --git a/include/net/act_api.h b/include/net/act_api.h
> index 2bf3092ae7ec..a01ff5fa641e 100644
> --- a/include/net/act_api.h
> +++ b/include/net/act_api.h
> @@ -194,7 +194,7 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
>  				    struct nlattr *nla, struct nlattr *est,
>  				    char *name, int ovr, int bind,
>  				    struct tc_action_ops *ops, bool rtnl_held,
> -				    struct netlink_ext_ack *extack);
> +				    struct netlink_ext_ack *extack, bool *ref);
>  int tcf_action_dump(struct sk_buff *skb, struct tc_action *actions[], int bind,
>  		    int ref, bool terse);
>  int tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int, int);
> diff --git a/net/sched/act_api.c b/net/sched/act_api.c
> index b919826939e0..718bc197b9a7 100644
> --- a/net/sched/act_api.c
> +++ b/net/sched/act_api.c
> @@ -993,7 +993,7 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
>  				    struct nlattr *nla, struct nlattr *est,
>  				    char *name, int ovr, int bind,
>  				    struct tc_action_ops *a_o, bool rtnl_held,
> -				    struct netlink_ext_ack *extack)
> +				    struct netlink_ext_ack *extack, bool *ref)
>  {
>  	struct nla_bitfield32 flags = { 0, 0 };
>  	u8 hw_stats = TCA_ACT_HW_STATS_ANY;
> @@ -1042,6 +1042,13 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
>  	if (err != ACT_P_CREATED)
>  		module_put(a_o->owner);
>
> +	if (!bind && ovr && err == ACT_P_CREATED) {
> +		if (ref)
> +			*ref = true;
> +
> +		refcount_set(&a->tcfa_refcnt, 2);
> +	}
> +

I wonder if we are being too clever here with all the refcnt
manipulations. Maybe it is better to just expose to the caller whether
existing action has been overwritten or a new one has been created and
let the user decide if they want to keep the reference? I'll send a RFC
to gather feedback from maintainers.

>  	return a;
>
>  err_out:
> @@ -1062,10 +1069,13 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
>  	struct tc_action_ops *ops[TCA_ACT_MAX_PRIO] = {};
>  	struct nlattr *tb[TCA_ACT_MAX_PRIO + 1];
>  	struct tc_action *act;
> +	u32 new_actions = 0;
>  	size_t sz = 0;
>  	int err;
>  	int i;
>
> +	BUILD_BUG_ON(TCA_ACT_MAX_PRIO > sizeof(new_actions) * 8);
> +
>  	err = nla_parse_nested_deprecated(tb, TCA_ACT_MAX_PRIO, nla, NULL,
>  					  extack);
>  	if (err < 0)
> @@ -1083,8 +1093,9 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
>  	}
>
>  	for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) {
> +		bool ovr_new = false;
>  		act = tcf_action_init_1(net, tp, tb[i], est, name, ovr, bind,
> -					ops[i - 1], rtnl_held, extack);
> +					ops[i - 1], rtnl_held, extack, &ovr_new);
>  		if (IS_ERR(act)) {
>  			err = PTR_ERR(act);
>  			goto err;
> @@ -1092,6 +1103,10 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
>  		sz += tcf_action_fill_size(act);
>  		/* Start from index 0 */
>  		actions[i - 1] = act;
> +
> +		/* remember new actions that we take a reference on */
> +		if (ovr_new)
> +			new_actions |= 1UL << (i - 1);
>  	}
>
>  	/* We have to commit them all together, because if any error happened in
> @@ -1103,6 +1118,11 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
>  	return i - 1;
>
>  err:
> +	/* reset the reference back to 1 in case of error */
> +	for (i = 0; i < TCA_ACT_MAX_PRIO && actions[i]; i++) {
> +		if (new_actions & (1UL << i))
> +			refcount_set(&actions[i]->tcfa_refcnt, 1);
> +	}
>  	tcf_action_destroy(actions, bind);
>  err_mod:
>  	for (i = 0; i < TCA_ACT_MAX_PRIO; i++) {
> diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
> index d3db70865d66..4f4a7355b1e1 100644
> --- a/net/sched/cls_api.c
> +++ b/net/sched/cls_api.c
> @@ -3052,7 +3052,7 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
>  			act = tcf_action_init_1(net, tp, tb[exts->police],
>  						rate_tlv, "police", ovr,
>  						TCA_ACT_BIND, a_o, rtnl_held,
> -						extack);
> +						extack, NULL);
>  			if (IS_ERR(act)) {
>  				module_put(a_o->owner);
>  				return PTR_ERR(act);


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2021-03-31 14:09 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-29 22:53 [PATCH v2 1/1] net: sched: bump refcount for new action in ACT replace mode Kumar Kartikeya Dwivedi
2021-03-30 10:32 ` Vlad Buslov
2021-03-30 10:41 ` [PATCH net-next] tc-testing: add simple action change test Vlad Buslov
2021-03-31  0:20   ` patchwork-bot+netdevbpf
2021-03-31  4:40 ` [PATCH v2 1/1] net: sched: bump refcount for new action in ACT replace mode Cong Wang
2021-03-31  7:59   ` Vlad Buslov
2021-03-31  8:37   ` [PATCH net-next v3] " Kumar Kartikeya Dwivedi
2021-03-31 14:08     ` Vlad Buslov
2021-03-31  8:38   ` [PATCH v2 1/1] " Kumar Kartikeya Dwivedi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).