mptcp: subflow: check parent mptcp socket on subflow state change
[linux-2.6-microblaze.git] / net / mptcp / protocol.c
index 4cf88e3..8cc9dd2 100644 (file)
@@ -327,6 +327,15 @@ void mptcp_data_acked(struct sock *sk)
                sock_hold(sk);
 }
 
+void mptcp_subflow_eof(struct sock *sk)
+{
+       struct mptcp_sock *msk = mptcp_sk(sk);
+
+       if (!test_and_set_bit(MPTCP_WORK_EOF, &msk->flags) &&
+           schedule_work(&msk->work))
+               sock_hold(sk);
+}
+
 static void mptcp_stop_timer(struct sock *sk)
 {
        struct inet_connection_sock *icsk = inet_csk(sk);
@@ -1031,6 +1040,27 @@ static unsigned int mptcp_sync_mss(struct sock *sk, u32 pmtu)
        return 0;
 }
 
+static void mptcp_check_for_eof(struct mptcp_sock *msk)
+{
+       struct mptcp_subflow_context *subflow;
+       struct sock *sk = (struct sock *)msk;
+       int receivers = 0;
+
+       mptcp_for_each_subflow(msk, subflow)
+               receivers += !subflow->rx_eof;
+
+       if (!receivers && !(sk->sk_shutdown & RCV_SHUTDOWN)) {
+               /* hopefully temporary hack: propagate shutdown status
+                * to msk, when all subflows agree on it
+                */
+               sk->sk_shutdown |= RCV_SHUTDOWN;
+
+               smp_mb__before_atomic(); /* SHUTDOWN must be visible first */
+               set_bit(MPTCP_DATA_READY, &msk->flags);
+               sk->sk_data_ready(sk);
+       }
+}
+
 static void mptcp_worker(struct work_struct *work)
 {
        struct mptcp_sock *msk = container_of(work, struct mptcp_sock, work);
@@ -1047,6 +1077,9 @@ static void mptcp_worker(struct work_struct *work)
        __mptcp_flush_join_list(msk);
        __mptcp_move_skbs(msk);
 
+       if (test_and_clear_bit(MPTCP_WORK_EOF, &msk->flags))
+               mptcp_check_for_eof(msk);
+
        if (!test_and_clear_bit(MPTCP_WORK_RTX, &msk->flags))
                goto unlock;