@@ -25,6 +25,7 @@ static const struct snmp_mib mptcp_snmp_list[] = {
SNMP_MIB_ITEM("OFOQueueTail", MPTCP_MIB_OFOQUEUETAIL),
SNMP_MIB_ITEM("OFOQueue", MPTCP_MIB_OFOQUEUE),
SNMP_MIB_ITEM("OFOMerge", MPTCP_MIB_OFOMERGE),
+ SNMP_MIB_ITEM("OFOPrune", MPTCP_MIB_OFOPRUNE),
SNMP_MIB_ITEM("NoDSSInWindow", MPTCP_MIB_NODSSWINDOW),
SNMP_MIB_ITEM("DuplicateData", MPTCP_MIB_DUPDATA),
SNMP_MIB_ITEM("AddAddr", MPTCP_MIB_ADDADDR),
@@ -18,6 +18,7 @@ enum linux_mptcp_mib_field {
MPTCP_MIB_OFOQUEUETAIL, /* Segments inserted into OoO queue tail */
MPTCP_MIB_OFOQUEUE, /* Segments inserted into OoO queue */
MPTCP_MIB_OFOMERGE, /* Segments merged in OoO queue */
+ MPTCP_MIB_OFOPRUNE, /* Segments pruned from OoO queue */
MPTCP_MIB_NODSSWINDOW, /* Segments not in MPTCP windows */
MPTCP_MIB_DUPDATA, /* Segments discarded due to duplicate DSS */
MPTCP_MIB_ADDADDR, /* Received ADD_ADDR with echo-flag=0 */
@@ -658,8 +658,17 @@ void mptcp_data_ready(struct sock *sk, struct sock *ssk)
sk_rbuf = ssk_rbuf;
/* over limit? can't append more skbs to msk */
- if (atomic_read(&sk->sk_rmem_alloc) > sk_rbuf)
- goto wake;
+ if (atomic_read(&sk->sk_rmem_alloc) > sk_rbuf) {
+ if (likely(!skb_queue_empty(&sk->sk_receive_queue)))
+ goto wake;
+
+ /* Entire recvbuf occupied by OOO skbs? Prune time. */
+ if (!test_and_set_bit(MPTCP_WORK_PRUNE_OFO, &msk->flags) &&
+ schedule_work(&msk->work))
+ sock_hold(sk);
+
+ return;
+ }
if (move_skbs_to_msk(msk, ssk))
goto wake;
@@ -1797,6 +1806,38 @@ static bool mptcp_check_close_timeout(const struct sock *sk)
return true;
}
+static void mptcp_prune_ofo(struct mptcp_sock *msk)
+{
+ struct sock *sk = &msk->sk.icsk_inet.sk;
+ struct sk_buff *skb, *prev = NULL;
+ int goal;
+
+ if (!skb_queue_empty(&sk->sk_receive_queue) ||
+ atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf)
+ return;
+
+ if (WARN_ON_ONCE(RB_EMPTY_ROOT(&msk->out_of_order_queue)))
+ return;
+
+ MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_OFOPRUNE);
+
+ goal = READ_ONCE(sk->sk_rcvbuf) >> 3;
+ skb = msk->ooo_last_skb;
+
+ while (skb) {
+ prev = skb_rb_prev(skb);
+ rb_erase(&skb->rbnode, &msk->out_of_order_queue);
+ goal -= skb->truesize;
+ mptcp_drop(sk, skb);
+
+ if (goal <= 0)
+ break;
+ skb = prev;
+ }
+
+ msk->ooo_last_skb = prev;
+}
+
static void mptcp_worker(struct work_struct *work)
{
struct mptcp_sock *msk = container_of(work, struct mptcp_sock, work);
@@ -1819,6 +1860,9 @@ static void mptcp_worker(struct work_struct *work)
if (mptcp_send_head(sk))
mptcp_push_pending(sk, 0);
+ if (test_and_clear_bit(MPTCP_WORK_PRUNE_OFO, &msk->flags))
+ mptcp_prune_ofo(msk);
+
if (msk->pm.status)
pm_work(msk);
@@ -91,6 +91,7 @@
#define MPTCP_WORK_EOF 3
#define MPTCP_FALLBACK_DONE 4
#define MPTCP_WORKER_RUNNING 5
+#define MPTCP_WORK_PRUNE_OFO 6
static inline bool before64(__u64 seq1, __u64 seq2)
{