Say Y here to inject packet loss by discarding some received and some
          transmitted packets.
 
+config AF_RXRPC_INJECT_RX_DELAY
+       bool "Inject delay into packet reception"
+       depends on SYSCTL
+       help
+         Say Y here to inject a delay into packet reception, allowing an
+         extended RTT time to be modelled.  The delay can be configured using
+         /proc/sys/net/rxrpc/rxrpc_inject_rx_delay, setting a number of
+         milliseconds up to 0.5s (note that the granularity is actually in
+         jiffies).
 
 config AF_RXRPC_DEBUG
        bool "RxRPC dynamic debugging"
 
        struct completion       io_thread_ready; /* Indication that the I/O thread started */
        struct rxrpc_sock       *service;       /* Service(s) listening on this endpoint */
        struct rw_semaphore     defrag_sem;     /* control re-enablement of IP DF bit */
+#ifdef CONFIG_AF_RXRPC_INJECT_RX_DELAY
+       struct sk_buff_head     rx_delay_queue; /* Delay injection queue */
+#endif
        struct sk_buff_head     rx_queue;       /* Received packets */
        struct list_head        conn_attend_q;  /* Conns requiring immediate attention */
        struct list_head        call_attend_q;  /* Calls requiring immediate attention */
 extern unsigned int rxrpc_rx_window_size;
 extern unsigned int rxrpc_rx_mtu;
 extern unsigned int rxrpc_rx_jumbo_max;
+#ifdef CONFIG_AF_RXRPC_INJECT_RX_DELAY
+extern unsigned long rxrpc_inject_rx_delay;
+#endif
 
 /*
  * net_ns.c
 
  */
 int rxrpc_encap_rcv(struct sock *udp_sk, struct sk_buff *skb)
 {
+       struct sk_buff_head *rx_queue;
        struct rxrpc_local *local = rcu_dereference_sk_user_data(udp_sk);
 
        if (unlikely(!local)) {
 
        skb->mark = RXRPC_SKB_MARK_PACKET;
        rxrpc_new_skb(skb, rxrpc_skb_new_encap_rcv);
-       skb_queue_tail(&local->rx_queue, skb);
+       rx_queue = &local->rx_queue;
+#ifdef CONFIG_AF_RXRPC_INJECT_RX_DELAY
+       if (rxrpc_inject_rx_delay ||
+           !skb_queue_empty(&local->rx_delay_queue)) {
+               skb->tstamp = ktime_add_ms(skb->tstamp, rxrpc_inject_rx_delay);
+               rx_queue = &local->rx_delay_queue;
+       }
+#endif
+
+       skb_queue_tail(rx_queue, skb);
        rxrpc_wake_up_io_thread(local);
        return 0;
 }
        struct rxrpc_local *local = data;
        struct rxrpc_call *call;
        struct sk_buff *skb;
+#ifdef CONFIG_AF_RXRPC_INJECT_RX_DELAY
+       ktime_t now;
+#endif
        bool should_stop;
 
        complete(&local->io_thread_ready);
                        continue;
                }
 
+               /* Inject a delay into packets if requested. */
+#ifdef CONFIG_AF_RXRPC_INJECT_RX_DELAY
+               now = ktime_get_real();
+               while ((skb = skb_peek(&local->rx_delay_queue))) {
+                       if (ktime_before(now, skb->tstamp))
+                               break;
+                       skb = skb_dequeue(&local->rx_delay_queue);
+                       skb_queue_tail(&local->rx_queue, skb);
+               }
+#endif
+
                if (!skb_queue_empty(&local->rx_queue)) {
                        spin_lock_irq(&local->rx_queue.lock);
                        skb_queue_splice_tail_init(&local->rx_queue, &rx_queue);
 
                if (should_stop)
                        break;
+
+#ifdef CONFIG_AF_RXRPC_INJECT_RX_DELAY
+               skb = skb_peek(&local->rx_delay_queue);
+               if (skb) {
+                       unsigned long timeout;
+                       ktime_t tstamp = skb->tstamp;
+                       ktime_t now = ktime_get_real();
+                       s64 delay_ns = ktime_to_ns(ktime_sub(tstamp, now));
+
+                       if (delay_ns <= 0) {
+                               __set_current_state(TASK_RUNNING);
+                               continue;
+                       }
+
+                       timeout = nsecs_to_jiffies(delay_ns);
+                       timeout = max(timeout, 1UL);
+                       schedule_timeout(timeout);
+                       __set_current_state(TASK_RUNNING);
+                       continue;
+               }
+#endif
+
                schedule();
        }
 
 
                INIT_HLIST_NODE(&local->link);
                init_rwsem(&local->defrag_sem);
                init_completion(&local->io_thread_ready);
+#ifdef CONFIG_AF_RXRPC_INJECT_RX_DELAY
+               skb_queue_head_init(&local->rx_delay_queue);
+#endif
                skb_queue_head_init(&local->rx_queue);
                INIT_LIST_HEAD(&local->conn_attend_q);
                INIT_LIST_HEAD(&local->call_attend_q);
        /* At this point, there should be no more packets coming in to the
         * local endpoint.
         */
+#ifdef CONFIG_AF_RXRPC_INJECT_RX_DELAY
+       rxrpc_purge_queue(&local->rx_delay_queue);
+#endif
        rxrpc_purge_queue(&local->rx_queue);
        rxrpc_purge_client_connections(local);
 }
 
  * sender that we're willing to handle.
  */
 unsigned int rxrpc_rx_jumbo_max = 4;
+
+#ifdef CONFIG_AF_RXRPC_INJECT_RX_DELAY
+/*
+ * The delay to inject into packet reception.
+ */
+unsigned long rxrpc_inject_rx_delay;
+#endif
 
 static const unsigned int n_max_acks = 255;
 static const unsigned long one_jiffy = 1;
 static const unsigned long max_jiffies = MAX_JIFFY_OFFSET;
+#ifdef CONFIG_AF_RXRPC_INJECT_RX_DELAY
+static const unsigned long max_500 = 500;
+#endif
 
 /*
  * RxRPC operating parameters.
                .extra2         = (void *)&max_jiffies,
        },
 
+       /* Values used in milliseconds */
+#ifdef CONFIG_AF_RXRPC_INJECT_RX_DELAY
+       {
+               .procname       = "inject_rx_delay",
+               .data           = &rxrpc_inject_rx_delay,
+               .maxlen         = sizeof(unsigned long),
+               .mode           = 0644,
+               .proc_handler   = proc_doulongvec_minmax,
+               .extra1         = (void *)SYSCTL_LONG_ZERO,
+               .extra2         = (void *)&max_500,
+       },
+#endif
+
        /* Non-time values */
        {
                .procname       = "reap_client_conns",
                .extra1         = (void *)SYSCTL_ONE,
                .extra2         = (void *)&four,
        },
-
        { }
 };