OSDN Git Service

mptcp: invoke MP_FAIL response when needed
[tomoyo/tomoyo-test1.git] / net / mptcp / protocol.c
index 17e1339..e6fcb61 100644 (file)
@@ -500,7 +500,7 @@ static void mptcp_set_timeout(struct sock *sk)
        __mptcp_set_timeout(sk, tout);
 }
 
-static bool tcp_can_send_ack(const struct sock *ssk)
+static inline bool tcp_can_send_ack(const struct sock *ssk)
 {
        return !((1 << inet_sk_state_load(ssk)) &
               (TCPF_SYN_SENT | TCPF_SYN_RECV | TCPF_TIME_WAIT | TCPF_CLOSE | TCPF_LISTEN));
@@ -2175,21 +2175,6 @@ static void mptcp_retransmit_timer(struct timer_list *t)
        sock_put(sk);
 }
 
-static struct mptcp_subflow_context *
-mp_fail_response_expect_subflow(struct mptcp_sock *msk)
-{
-       struct mptcp_subflow_context *subflow, *ret = NULL;
-
-       mptcp_for_each_subflow(msk, subflow) {
-               if (READ_ONCE(subflow->mp_fail_response_expect)) {
-                       ret = subflow;
-                       break;
-               }
-       }
-
-       return ret;
-}
-
 static void mptcp_timeout_timer(struct timer_list *t)
 {
        struct sock *sk = from_timer(sk, t, sk_timer);
@@ -2518,27 +2503,50 @@ reset_timer:
                mptcp_reset_timer(sk);
 }
 
+/* schedule the timeout timer for the relevant event: either close timeout
+ * or mp_fail timeout. The close timeout takes precedence on the mp_fail one
+ */
+void mptcp_reset_timeout(struct mptcp_sock *msk, unsigned long fail_tout)
+{
+       struct sock *sk = (struct sock *)msk;
+       unsigned long timeout, close_timeout;
+
+       if (!fail_tout && !sock_flag(sk, SOCK_DEAD))
+               return;
+
+       close_timeout = inet_csk(sk)->icsk_mtup.probe_timestamp - tcp_jiffies32 + jiffies + TCP_TIMEWAIT_LEN;
+
+       /* the close timeout takes precedence on the fail one, and here at least one of
+        * them is active
+        */
+       timeout = sock_flag(sk, SOCK_DEAD) ? close_timeout : fail_tout;
+
+       sk_reset_timer(sk, &sk->sk_timer, timeout);
+}
+
 static void mptcp_mp_fail_no_response(struct mptcp_sock *msk)
 {
-       struct mptcp_subflow_context *subflow;
-       struct sock *ssk;
+       struct sock *ssk = msk->first;
        bool slow;
 
-       subflow = mp_fail_response_expect_subflow(msk);
-       if (subflow) {
-               pr_debug("MP_FAIL doesn't respond, reset the subflow");
+       if (!ssk)
+               return;
 
-               ssk = mptcp_subflow_tcp_sock(subflow);
-               slow = lock_sock_fast(ssk);
-               mptcp_subflow_reset(ssk);
-               unlock_sock_fast(ssk, slow);
-       }
+       pr_debug("MP_FAIL doesn't respond, reset the subflow");
+
+       slow = lock_sock_fast(ssk);
+       mptcp_subflow_reset(ssk);
+       WRITE_ONCE(mptcp_subflow_ctx(ssk)->fail_tout, 0);
+       unlock_sock_fast(ssk, slow);
+
+       mptcp_reset_timeout(msk, 0);
 }
 
 static void mptcp_worker(struct work_struct *work)
 {
        struct mptcp_sock *msk = container_of(work, struct mptcp_sock, work);
        struct sock *sk = &msk->sk.icsk_inet.sk;
+       unsigned long fail_tout;
        int state;
 
        lock_sock(sk);
@@ -2575,7 +2583,9 @@ static void mptcp_worker(struct work_struct *work)
        if (test_and_clear_bit(MPTCP_WORK_RTX, &msk->flags))
                __mptcp_retrans(sk);
 
-       mptcp_mp_fail_no_response(msk);
+       fail_tout = msk->first ? READ_ONCE(mptcp_subflow_ctx(msk->first)->fail_tout) : 0;
+       if (fail_tout && time_after(jiffies, fail_tout))
+               mptcp_mp_fail_no_response(msk);
 
 unlock:
        release_sock(sk);
@@ -2822,6 +2832,7 @@ static void __mptcp_destroy_sock(struct sock *sk)
 static void mptcp_close(struct sock *sk, long timeout)
 {
        struct mptcp_subflow_context *subflow;
+       struct mptcp_sock *msk = mptcp_sk(sk);
        bool do_cancel_work = false;
 
        lock_sock(sk);
@@ -2840,10 +2851,16 @@ static void mptcp_close(struct sock *sk, long timeout)
 cleanup:
        /* orphan all the subflows */
        inet_csk(sk)->icsk_mtup.probe_timestamp = tcp_jiffies32;
-       mptcp_for_each_subflow(mptcp_sk(sk), subflow) {
+       mptcp_for_each_subflow(msk, subflow) {
                struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
                bool slow = lock_sock_fast_nested(ssk);
 
+               /* since the close timeout takes precedence on the fail one,
+                * cancel the latter
+                */
+               if (ssk == msk->first)
+                       subflow->fail_tout = 0;
+
                sock_orphan(ssk);
                unlock_sock_fast(ssk, slow);
        }
@@ -2852,13 +2869,13 @@ cleanup:
        sock_hold(sk);
        pr_debug("msk=%p state=%d", sk, sk->sk_state);
        if (mptcp_sk(sk)->token)
-               mptcp_event(MPTCP_EVENT_CLOSED, mptcp_sk(sk), NULL, GFP_KERNEL);
+               mptcp_event(MPTCP_EVENT_CLOSED, msk, NULL, GFP_KERNEL);
 
        if (sk->sk_state == TCP_CLOSE) {
                __mptcp_destroy_sock(sk);
                do_cancel_work = true;
        } else {
-               sk_reset_timer(sk, &sk->sk_timer, jiffies + TCP_TIMEWAIT_LEN);
+               mptcp_reset_timeout(msk, 0);
        }
        release_sock(sk);
        if (do_cancel_work)