diff mbox series

[mptcp-next,2/3] mptcp: fix race in release_cb

Message ID 45298408838519faf5eb1ca8e39593c3a5d4bac9.1612869198.git.pabeni@redhat.com
State Accepted, archived
Commit f1c293d570e18e8ba0c7af97129158df05860731
Delegated to: Matthieu Baerts
Headers show
Series mptcp: improve mptcp release callback | expand

Commit Message

Paolo Abeni Feb. 9, 2021, 11:42 a.m. UTC
If we receive a MPTCP_PUSH_PENDING even from a subflow when
mptcp_release_cb() is serving the previous one, the latter
will be delayed up to the next release_sock(msk).

Address the issue implementing a test/serve loop for such
event.

Additionally rename the push helper to __mptcp_push_pending()
to be more consistent with the existing code.

Fixes: 6e628cd3a8f7 ("mptcp: use mptcp release_cb for delayed tasks")
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
 net/mptcp/protocol.c | 33 +++++++++++++++++++++------------
 1 file changed, 21 insertions(+), 12 deletions(-)

Comments

Matthieu Baerts Feb. 9, 2021, 2:22 p.m. UTC | #1
Hi Paolo,

Thank you for the patch!

On 09/02/2021 12:42, Paolo Abeni wrote:
> If we receive a MPTCP_PUSH_PENDING even from a subflow when
> mptcp_release_cb() is serving the previous one, the latter
> will be delayed up to the next release_sock(msk).
> 
> Address the issue implementing a test/serve loop for such
> event.
> 
> Additionally rename the push helper to __mptcp_push_pending()
> to be more consistent with the existing code.

(...)

> @@ -2942,13 +2942,14 @@ static void mptcp_release_cb(struct sock *sk)
>   {
>   	unsigned long flags, nflags;
>   
> -	/* push_pending may touch wmem_reserved, do it before the later
> -	 * cleanup
> -	 */
> -	if (test_and_clear_bit(MPTCP_CLEAN_UNA, &mptcp_sk(sk)->flags))
> -		__mptcp_clean_una(sk);
> -	if (test_and_clear_bit(MPTCP_PUSH_PENDING, &mptcp_sk(sk)->flags)) {
> -		/* mptcp_push_pending() acquires the subflow socket lock
> +	for (;;) {
> +		flags = 0;
> +		if (test_and_clear_bit(MPTCP_PUSH_PENDING, &mptcp_sk(sk)->flags))
> +			flags |= MPTCP_PUSH_PENDING;
> +		if (!flags)
> +			break;

This looks strange: "flags" is reset to 0 then set if the previous 
condition is valid and we check what was done at the previous 
instruction. Maybe clearer with "else" instead of "if (!flags)"?

> +
> +		/* the following actions acquire the subflow socket lock
>   		 *
>   		 * 1) can't be invoked in atomic scope
>   		 * 2) must avoid ABBA deadlock with msk socket spinlock: the RX
> @@ -2957,13 +2958,21 @@ static void mptcp_release_cb(struct sock *sk)
>   		 */
>   
>   		spin_unlock_bh(&sk->sk_lock.slock);
> -		mptcp_push_pending(sk, 0);
> +		if (flags & MPTCP_PUSH_PENDING)
> +			__mptcp_push_pending(sk, 0);
> +
> +		cond_resched();
>   		spin_lock_bh(&sk->sk_lock.slock);
>   	}
> +
> +	if (test_and_clear_bit(MPTCP_CLEAN_UNA, &mptcp_sk(sk)->flags))
> +		__mptcp_clean_una(sk);
>   	if (test_and_clear_bit(MPTCP_ERROR_REPORT, &mptcp_sk(sk)->flags))
>   		__mptcp_error_report(sk);
>   
> -	/* clear any wmem reservation and errors */
> +	/* push_pending may touch wmem_reserved, ensure we do the cleanup
> +	 * later

But we still need to update "rmem", right? :)
(oh yes but even before, we were only talking about "wmem", fine!)

> +	 */
>   	__mptcp_update_wmem(sk);
>   	__mptcp_update_rmem(sk);
>   
> 

Cheers,
Matt
Matthieu Baerts Feb. 9, 2021, 2:26 p.m. UTC | #2
On 09/02/2021 15:22, Matthieu Baerts wrote:
> Hi Paolo,
> 
> Thank you for the patch!
> 
> On 09/02/2021 12:42, Paolo Abeni wrote:
>> If we receive a MPTCP_PUSH_PENDING even from a subflow when
>> mptcp_release_cb() is serving the previous one, the latter
>> will be delayed up to the next release_sock(msk).
>>
>> Address the issue implementing a test/serve loop for such
>> event.
>>
>> Additionally rename the push helper to __mptcp_push_pending()
>> to be more consistent with the existing code.
> 
> (...)
> 
>> @@ -2942,13 +2942,14 @@ static void mptcp_release_cb(struct sock *sk)
>>   {
>>       unsigned long flags, nflags;
>> -    /* push_pending may touch wmem_reserved, do it before the later
>> -     * cleanup
>> -     */
>> -    if (test_and_clear_bit(MPTCP_CLEAN_UNA, &mptcp_sk(sk)->flags))
>> -        __mptcp_clean_una(sk);
>> -    if (test_and_clear_bit(MPTCP_PUSH_PENDING, &mptcp_sk(sk)->flags)) {
>> -        /* mptcp_push_pending() acquires the subflow socket lock
>> +    for (;;) {
>> +        flags = 0;
>> +        if (test_and_clear_bit(MPTCP_PUSH_PENDING, 
>> &mptcp_sk(sk)->flags))
>> +            flags |= MPTCP_PUSH_PENDING;
>> +        if (!flags)
>> +            break;
> 
> This looks strange: "flags" is reset to 0 then set if the previous 
> condition is valid and we check what was done at the previous 
> instruction. Maybe clearer with "else" instead of "if (!flags)"?

My bad, I understand why after having looked at the next patch! I hit 
the send button too fast :)

Cheers,
Matt
diff mbox series

Patch

diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index 7e5016903e11c..0393cef5a08d8 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -1442,7 +1442,7 @@  static void mptcp_push_release(struct sock *sk, struct sock *ssk,
 	release_sock(ssk);
 }
 
-static void mptcp_push_pending(struct sock *sk, unsigned int flags)
+static void __mptcp_push_pending(struct sock *sk, unsigned int flags)
 {
 	struct sock *prev_ssk = NULL, *ssk = NULL;
 	struct mptcp_sock *msk = mptcp_sk(sk);
@@ -1694,14 +1694,14 @@  static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
 
 wait_for_memory:
 		mptcp_set_nospace(sk);
-		mptcp_push_pending(sk, msg->msg_flags);
+		__mptcp_push_pending(sk, msg->msg_flags);
 		ret = sk_stream_wait_memory(sk, &timeo);
 		if (ret)
 			goto out;
 	}
 
 	if (copied)
-		mptcp_push_pending(sk, msg->msg_flags);
+		__mptcp_push_pending(sk, msg->msg_flags);
 
 out:
 	release_sock(sk);
@@ -2942,13 +2942,14 @@  static void mptcp_release_cb(struct sock *sk)
 {
 	unsigned long flags, nflags;
 
-	/* push_pending may touch wmem_reserved, do it before the later
-	 * cleanup
-	 */
-	if (test_and_clear_bit(MPTCP_CLEAN_UNA, &mptcp_sk(sk)->flags))
-		__mptcp_clean_una(sk);
-	if (test_and_clear_bit(MPTCP_PUSH_PENDING, &mptcp_sk(sk)->flags)) {
-		/* mptcp_push_pending() acquires the subflow socket lock
+	for (;;) {
+		flags = 0;
+		if (test_and_clear_bit(MPTCP_PUSH_PENDING, &mptcp_sk(sk)->flags))
+			flags |= MPTCP_PUSH_PENDING;
+		if (!flags)
+			break;
+
+		/* the following actions acquire the subflow socket lock
 		 *
 		 * 1) can't be invoked in atomic scope
 		 * 2) must avoid ABBA deadlock with msk socket spinlock: the RX
@@ -2957,13 +2958,21 @@  static void mptcp_release_cb(struct sock *sk)
 		 */
 
 		spin_unlock_bh(&sk->sk_lock.slock);
-		mptcp_push_pending(sk, 0);
+		if (flags & MPTCP_PUSH_PENDING)
+			__mptcp_push_pending(sk, 0);
+
+		cond_resched();
 		spin_lock_bh(&sk->sk_lock.slock);
 	}
+
+	if (test_and_clear_bit(MPTCP_CLEAN_UNA, &mptcp_sk(sk)->flags))
+		__mptcp_clean_una(sk);
 	if (test_and_clear_bit(MPTCP_ERROR_REPORT, &mptcp_sk(sk)->flags))
 		__mptcp_error_report(sk);
 
-	/* clear any wmem reservation and errors */
+	/* push_pending may touch wmem_reserved, ensure we do the cleanup
+	 * later
+	 */
 	__mptcp_update_wmem(sk);
 	__mptcp_update_rmem(sk);