* Total data bytes retransmitted
                                 */
        u32     total_retrans;  /* Total retransmits for entire connection */
+       u32     rto_stamp;      /* Start time (ms) of last CA_Loss recovery */
+       u16     total_rto;      /* Total number of RTO timeouts, including
+                                * SYN/SYN-ACK and recurring timeouts.
+                                */
+       u16     total_rto_recoveries;   /* Total number of RTO recoveries,
+                                        * including any unfinished recovery.
+                                        */
+       u32     total_rto_time; /* ms spent in (completed) RTO recoveries. */
 
        u32     urg_seq;        /* Seq of received urgent pointer */
        unsigned int            keepalive_time;   /* time before keep alive takes place */
 
                                      */
 
        __u32   tcpi_rehash;         /* PLB or timeout triggered rehash attempts */
+
+       __u16   tcpi_total_rto; /* Total number of RTO timeouts, including
+                                * SYN/SYN-ACK and recurring timeouts.
+                                */
+       __u16   tcpi_total_rto_recoveries;      /* Total number of RTO
+                                                * recoveries, including any
+                                                * unfinished recovery.
+                                                */
+       __u32   tcpi_total_rto_time;    /* Total time spent in RTO recoveries
+                                        * in milliseconds, including any
+                                        * unfinished recovery.
+                                        */
 };
 
 /* netlink attributes types for SCM_TIMESTAMPING_OPT_STATS */
 
        info->tcpi_rcv_wnd = tp->rcv_wnd;
        info->tcpi_rehash = tp->plb_rehash + tp->timeout_rehash;
        info->tcpi_fastopen_client_fail = tp->fastopen_client_fail;
+
+       info->tcpi_total_rto = tp->total_rto;
+       info->tcpi_total_rto_recoveries = tp->total_rto_recoveries;
+       info->tcpi_total_rto_time = tp->total_rto_time;
+       if (tp->rto_stamp) {
+               info->tcpi_total_rto_time += tcp_time_stamp_raw() -
+                                               tp->rto_stamp;
+       }
+
        unlock_sock_fast(sk, slow);
 }
 EXPORT_SYMBOL_GPL(tcp_get_info);
 
        tp->undo_marker = 0;
        tp->undo_retrans = -1;
        tp->sacked_out = 0;
+       tp->rto_stamp = 0;
+       tp->total_rto = 0;
+       tp->total_rto_recoveries = 0;
+       tp->total_rto_time = 0;
 }
 
 static inline void tcp_init_undo(struct tcp_sock *tp)
        tcp_set_ca_state(sk, TCP_CA_Recovery);
 }
 
+static void tcp_update_rto_time(struct tcp_sock *tp)
+{
+       if (tp->rto_stamp) {
+               tp->total_rto_time += tcp_time_stamp(tp) - tp->rto_stamp;
+               tp->rto_stamp = 0;
+       }
+}
+
 /* Process an ACK in CA_Loss state. Move to CA_Open if lost data are
  * recovered or spurious. Otherwise retransmits more on partial ACKs.
  */
                break;
        case TCP_CA_Loss:
                tcp_process_loss(sk, flag, num_dupack, rexmit);
+               if (icsk->icsk_ca_state != TCP_CA_Loss)
+                       tcp_update_rto_time(tp);
                tcp_identify_packet_loss(sk, ack_flag);
                if (!(icsk->icsk_ca_state == TCP_CA_Open ||
                      (*ack_flag & FLAG_LOST_RETRANS)))
                tcp_try_undo_recovery(sk);
 
        /* Reset rtx states to prevent spurious retransmits_timed_out() */
+       tcp_update_rto_time(tp);
        tp->retrans_stamp = 0;
        inet_csk(sk)->icsk_retransmits = 0;
 
 
                newtp->undo_marker = treq->snt_isn;
                newtp->retrans_stamp = div_u64(treq->snt_synack,
                                               USEC_PER_SEC / TCP_TS_HZ);
+               newtp->total_rto = req->num_timeout;
+               newtp->total_rto_recoveries = 1;
+               newtp->total_rto_time = tcp_time_stamp_raw() -
+                                               newtp->retrans_stamp;
        }
        newtp->tsoffset = treq->ts_off;
 #ifdef CONFIG_TCP_MD5SIG
 
        }
 }
 
+static void tcp_update_rto_stats(struct sock *sk)
+{
+       struct inet_connection_sock *icsk = inet_csk(sk);
+       struct tcp_sock *tp = tcp_sk(sk);
+
+       if (!icsk->icsk_retransmits) {
+               tp->total_rto_recoveries++;
+               tp->rto_stamp = tcp_time_stamp(tp);
+       }
+       icsk->icsk_retransmits++;
+       tp->total_rto++;
+}
+
 /*
  *     Timer for Fast Open socket to retransmit SYNACK. Note that the
  *     sk here is the child socket, not the parent (listener) socket.
         */
        inet_rtx_syn_ack(sk, req);
        req->num_timeout++;
-       icsk->icsk_retransmits++;
+       tcp_update_rto_stats(sk);
        if (!tp->retrans_stamp)
                tp->retrans_stamp = tcp_time_stamp(tp);
        inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
 
        tcp_enter_loss(sk);
 
-       icsk->icsk_retransmits++;
+       tcp_update_rto_stats(sk);
        if (tcp_retransmit_skb(sk, tcp_rtx_queue_head(sk), 1) > 0) {
                /* Retransmission failed because of local congestion,
                 * Let senders fight for local resources conservatively.