Add MP_FAIL retrans support.
Signed-off-by: Geliang Tang <geliang.tang@suse.com>
---
net/mptcp/pm.c | 3 +++
net/mptcp/protocol.c | 1 +
net/mptcp/protocol.h | 3 +++
net/mptcp/subflow.c | 25 +++++++++++++++++++++++++
4 files changed, 32 insertions(+)
diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
index 5e133b249492..e85acdf49d95 100644
--- a/net/mptcp/pm.c
+++ b/net/mptcp/pm.c
@@ -311,11 +311,14 @@ void mptcp_pm_mp_fail_echoed(struct sock *sk, u64 fail_seq)
if (tmp != subflow && tmp->fail_seq == fail_seq) {
struct sock *ssk = mptcp_subflow_tcp_sock(tmp);
+ sk_stop_timer_sync(ssk, &tmp->mp_fail_timer);
ssk->sk_err = EBADMSG;
tcp_set_state(ssk, TCP_CLOSE);
subflow_sched_work_if_closed(msk, ssk);
}
}
+ } else {
+ sk_stop_timer_sync(sk, &subflow->mp_fail_timer);
}
}
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index 4599bde215b2..85f7c76aeb90 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -2307,6 +2307,7 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk,
sock_orphan(ssk);
subflow->disposable = 1;
+ sk_stop_timer_sync(ssk, &subflow->mp_fail_timer);
/* if ssk hit tcp_done(), tcp_cleanup_ulp() cleared the related ops
* the ssk has been already destroyed, we just need to release the
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 595222b08fbd..9f6e8774b069 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -467,6 +467,7 @@ struct mptcp_subflow_context {
u8 reset_transient:1;
u8 reset_reason:4;
u8 stale_count;
+ u8 retrans_times;
long delegated_status;
@@ -483,6 +484,8 @@ struct mptcp_subflow_context {
void (*tcp_state_change)(struct sock *sk);
void (*tcp_error_report)(struct sock *sk);
+ struct timer_list mp_fail_timer;
+
struct rcu_head rcu;
};
diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
index a8b5b8bf45e5..9bbc157fd713 100644
--- a/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -27,6 +27,8 @@
#include <trace/events/mptcp.h>
+#define MP_FAIL_RETRANS_MAX 3
+
static void mptcp_subflow_ops_undo_override(struct sock *ssk);
static void SUBFLOW_REQ_INC_STATS(struct request_sock *req,
@@ -1099,9 +1101,29 @@ void subflow_sched_work_if_closed(struct mptcp_sock *msk, struct sock *ssk)
}
}
+static void mptcp_mp_fail_timer(struct timer_list *timer)
+{
+ struct mptcp_subflow_context *subflow = from_timer(subflow, timer, mp_fail_timer);
+ struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
+ struct net *net = sock_net(ssk);
+
+ if (inet_sk_state_load(ssk) == TCP_CLOSE)
+ return;
+
+ subflow->fail_seq = subflow->map_seq;
+ subflow->send_mp_fail = 1;
+ MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_MPFAILTX);
+ pr_debug("retransmit MP_FAIL %u fail_seq=%llu",
+ subflow->retrans_times++, subflow->fail_seq);
+
+ if (subflow->retrans_times < MP_FAIL_RETRANS_MAX)
+ sk_reset_timer(ssk, timer, jiffies + mptcp_get_mp_fail_timeout(net));
+}
+
static bool subflow_check_data_avail(struct sock *ssk)
{
struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk);
+ struct net *net = sock_net(ssk);
enum mapping_status status;
struct mptcp_sock *msk;
struct sk_buff *skb;
@@ -1173,6 +1195,9 @@ static bool subflow_check_data_avail(struct sock *ssk)
sk_eat_skb(ssk, skb);
}
WRITE_ONCE(subflow->data_avail, MPTCP_SUBFLOW_NODATA);
+ timer_setup(&subflow->mp_fail_timer, mptcp_mp_fail_timer, 0);
+ sk_reset_timer(ssk, &subflow->mp_fail_timer,
+ jiffies + mptcp_get_mp_fail_timeout(net));
return true;
}
--
2.34.1