rxrpc: Allow a delay to be injected into packet reception

If CONFIG_AF_RXRPC_DEBUG_RX_DELAY=y, then a delay is injected between
packets and errors being received and them being made available to the
processing code, thereby allowing the RTT to be artificially increased.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: linux-afs@lists.infradead.org
This commit is contained in:
David Howells 2022-10-17 08:54:57 +01:00
parent 223f59016f
commit af094824f2
6 changed files with 91 additions and 2 deletions

View File

@ -36,6 +36,15 @@ config AF_RXRPC_INJECT_LOSS
Say Y here to inject packet loss by discarding some received and some Say Y here to inject packet loss by discarding some received and some
transmitted packets. 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 config AF_RXRPC_DEBUG
bool "RxRPC dynamic debugging" bool "RxRPC dynamic debugging"

View File

@ -285,6 +285,9 @@ struct rxrpc_local {
struct completion io_thread_ready; /* Indication that the I/O thread started */ struct completion io_thread_ready; /* Indication that the I/O thread started */
struct rxrpc_sock *service; /* Service(s) listening on this endpoint */ struct rxrpc_sock *service; /* Service(s) listening on this endpoint */
struct rw_semaphore defrag_sem; /* control re-enablement of IP DF bit */ 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 sk_buff_head rx_queue; /* Received packets */
struct list_head conn_attend_q; /* Conns requiring immediate attention */ struct list_head conn_attend_q; /* Conns requiring immediate attention */
struct list_head call_attend_q; /* Calls requiring immediate attention */ struct list_head call_attend_q; /* Calls requiring immediate attention */
@ -1109,6 +1112,9 @@ extern unsigned long rxrpc_idle_ack_delay;
extern unsigned int rxrpc_rx_window_size; extern unsigned int rxrpc_rx_window_size;
extern unsigned int rxrpc_rx_mtu; extern unsigned int rxrpc_rx_mtu;
extern unsigned int rxrpc_rx_jumbo_max; 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 * net_ns.c

View File

@ -25,6 +25,7 @@ static int rxrpc_input_packet_on_conn(struct rxrpc_connection *conn,
*/ */
int rxrpc_encap_rcv(struct sock *udp_sk, struct sk_buff *skb) 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); struct rxrpc_local *local = rcu_dereference_sk_user_data(udp_sk);
if (unlikely(!local)) { if (unlikely(!local)) {
@ -36,7 +37,16 @@ int rxrpc_encap_rcv(struct sock *udp_sk, struct sk_buff *skb)
skb->mark = RXRPC_SKB_MARK_PACKET; skb->mark = RXRPC_SKB_MARK_PACKET;
rxrpc_new_skb(skb, rxrpc_skb_new_encap_rcv); 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); rxrpc_wake_up_io_thread(local);
return 0; return 0;
} }
@ -407,6 +417,9 @@ int rxrpc_io_thread(void *data)
struct rxrpc_local *local = data; struct rxrpc_local *local = data;
struct rxrpc_call *call; struct rxrpc_call *call;
struct sk_buff *skb; struct sk_buff *skb;
#ifdef CONFIG_AF_RXRPC_INJECT_RX_DELAY
ktime_t now;
#endif
bool should_stop; bool should_stop;
complete(&local->io_thread_ready); complete(&local->io_thread_ready);
@ -481,6 +494,17 @@ int rxrpc_io_thread(void *data)
continue; 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)) { if (!skb_queue_empty(&local->rx_queue)) {
spin_lock_irq(&local->rx_queue.lock); spin_lock_irq(&local->rx_queue.lock);
skb_queue_splice_tail_init(&local->rx_queue, &rx_queue); skb_queue_splice_tail_init(&local->rx_queue, &rx_queue);
@ -502,6 +526,28 @@ int rxrpc_io_thread(void *data)
if (should_stop) if (should_stop)
break; 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(); schedule();
} }

View File

@ -110,6 +110,9 @@ static struct rxrpc_local *rxrpc_alloc_local(struct net *net,
INIT_HLIST_NODE(&local->link); INIT_HLIST_NODE(&local->link);
init_rwsem(&local->defrag_sem); init_rwsem(&local->defrag_sem);
init_completion(&local->io_thread_ready); 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); skb_queue_head_init(&local->rx_queue);
INIT_LIST_HEAD(&local->conn_attend_q); INIT_LIST_HEAD(&local->conn_attend_q);
INIT_LIST_HEAD(&local->call_attend_q); INIT_LIST_HEAD(&local->call_attend_q);
@ -434,6 +437,9 @@ void rxrpc_destroy_local(struct rxrpc_local *local)
/* At this point, there should be no more packets coming in to the /* At this point, there should be no more packets coming in to the
* local endpoint. * 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_queue(&local->rx_queue);
rxrpc_purge_client_connections(local); rxrpc_purge_client_connections(local);
} }

View File

@ -53,3 +53,10 @@ unsigned int rxrpc_rx_mtu = 5692;
* sender that we're willing to handle. * sender that we're willing to handle.
*/ */
unsigned int rxrpc_rx_jumbo_max = 4; 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

View File

@ -17,6 +17,9 @@ static const unsigned int n_65535 = 65535;
static const unsigned int n_max_acks = 255; static const unsigned int n_max_acks = 255;
static const unsigned long one_jiffy = 1; static const unsigned long one_jiffy = 1;
static const unsigned long max_jiffies = MAX_JIFFY_OFFSET; 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. * RxRPC operating parameters.
@ -63,6 +66,19 @@ static struct ctl_table rxrpc_sysctl_table[] = {
.extra2 = (void *)&max_jiffies, .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 */ /* Non-time values */
{ {
.procname = "reap_client_conns", .procname = "reap_client_conns",
@ -109,7 +125,6 @@ static struct ctl_table rxrpc_sysctl_table[] = {
.extra1 = (void *)SYSCTL_ONE, .extra1 = (void *)SYSCTL_ONE,
.extra2 = (void *)&four, .extra2 = (void *)&four,
}, },
{ } { }
}; };