rxrpc: Fix call timeouts
Fix the rxrpc call expiration timeouts and make them settable from userspace. By analogy with other rx implementations, there should be three timeouts: (1) "Normal timeout" This is set for all calls and is triggered if we haven't received any packets from the peer in a while. It is measured from the last time we received any packet on that call. This is not reset by any connection packets (such as CHALLENGE/RESPONSE packets). If a service operation takes a long time, the server should generate PING ACKs at a duration that's substantially less than the normal timeout so is to keep both sides alive. This is set at 1/6 of normal timeout. (2) "Idle timeout" This is set only for a service call and is triggered if we stop receiving the DATA packets that comprise the request data. It is measured from the last time we received a DATA packet. (3) "Hard timeout" This can be set for a call and specified the maximum lifetime of that call. It should not be specified by default. Some operations (such as volume transfer) take a long time. Allow userspace to set/change the timeouts on a call with sendmsg, using a control message: RXRPC_SET_CALL_TIMEOUTS The data to the message is a number of 32-bit words, not all of which need be given: u32 hard_timeout; /* sec from first packet */ u32 idle_timeout; /* msec from packet Rx */ u32 normal_timeout; /* msec from data Rx */ This can be set in combination with any other sendmsg() that affects a call. Signed-off-by: David Howells <dhowells@redhat.com>
This commit is contained in:
parent
4812417894
commit
a158bdd324
11 changed files with 289 additions and 200 deletions
|
@ -138,10 +138,20 @@ enum rxrpc_rtt_rx_trace {
|
|||
|
||||
enum rxrpc_timer_trace {
|
||||
rxrpc_timer_begin,
|
||||
rxrpc_timer_exp_ack,
|
||||
rxrpc_timer_exp_hard,
|
||||
rxrpc_timer_exp_idle,
|
||||
rxrpc_timer_exp_normal,
|
||||
rxrpc_timer_exp_ping,
|
||||
rxrpc_timer_exp_resend,
|
||||
rxrpc_timer_expired,
|
||||
rxrpc_timer_init_for_reply,
|
||||
rxrpc_timer_init_for_send_reply,
|
||||
rxrpc_timer_restart,
|
||||
rxrpc_timer_set_for_ack,
|
||||
rxrpc_timer_set_for_hard,
|
||||
rxrpc_timer_set_for_idle,
|
||||
rxrpc_timer_set_for_normal,
|
||||
rxrpc_timer_set_for_ping,
|
||||
rxrpc_timer_set_for_resend,
|
||||
rxrpc_timer_set_for_send,
|
||||
|
@ -296,12 +306,22 @@ enum rxrpc_congest_change {
|
|||
#define rxrpc_timer_traces \
|
||||
EM(rxrpc_timer_begin, "Begin ") \
|
||||
EM(rxrpc_timer_expired, "*EXPR*") \
|
||||
EM(rxrpc_timer_exp_ack, "ExpAck") \
|
||||
EM(rxrpc_timer_exp_hard, "ExpHrd") \
|
||||
EM(rxrpc_timer_exp_idle, "ExpIdl") \
|
||||
EM(rxrpc_timer_exp_normal, "ExpNml") \
|
||||
EM(rxrpc_timer_exp_ping, "ExpPng") \
|
||||
EM(rxrpc_timer_exp_resend, "ExpRsn") \
|
||||
EM(rxrpc_timer_init_for_reply, "IniRpl") \
|
||||
EM(rxrpc_timer_init_for_send_reply, "SndRpl") \
|
||||
EM(rxrpc_timer_restart, "Restrt") \
|
||||
EM(rxrpc_timer_set_for_ack, "SetAck") \
|
||||
EM(rxrpc_timer_set_for_hard, "SetHrd") \
|
||||
EM(rxrpc_timer_set_for_idle, "SetIdl") \
|
||||
EM(rxrpc_timer_set_for_normal, "SetNml") \
|
||||
EM(rxrpc_timer_set_for_ping, "SetPng") \
|
||||
EM(rxrpc_timer_set_for_resend, "SetRTx") \
|
||||
E_(rxrpc_timer_set_for_send, "SetTx ")
|
||||
E_(rxrpc_timer_set_for_send, "SetSnd")
|
||||
|
||||
#define rxrpc_propose_ack_traces \
|
||||
EM(rxrpc_propose_ack_client_tx_end, "ClTxEnd") \
|
||||
|
@ -932,39 +952,44 @@ TRACE_EVENT(rxrpc_rtt_rx,
|
|||
|
||||
TRACE_EVENT(rxrpc_timer,
|
||||
TP_PROTO(struct rxrpc_call *call, enum rxrpc_timer_trace why,
|
||||
ktime_t now, unsigned long now_j),
|
||||
unsigned long now),
|
||||
|
||||
TP_ARGS(call, why, now, now_j),
|
||||
TP_ARGS(call, why, now),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(struct rxrpc_call *, call )
|
||||
__field(enum rxrpc_timer_trace, why )
|
||||
__field_struct(ktime_t, now )
|
||||
__field_struct(ktime_t, expire_at )
|
||||
__field_struct(ktime_t, ack_at )
|
||||
__field_struct(ktime_t, resend_at )
|
||||
__field(unsigned long, now_j )
|
||||
__field(unsigned long, timer )
|
||||
__field(long, now )
|
||||
__field(long, ack_at )
|
||||
__field(long, resend_at )
|
||||
__field(long, ping_at )
|
||||
__field(long, expect_rx_by )
|
||||
__field(long, expect_req_by )
|
||||
__field(long, expect_term_by )
|
||||
__field(long, timer )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->call = call;
|
||||
__entry->why = why;
|
||||
__entry->now = now;
|
||||
__entry->expire_at = call->expire_at;
|
||||
__entry->ack_at = call->ack_at;
|
||||
__entry->resend_at = call->resend_at;
|
||||
__entry->now_j = now_j;
|
||||
__entry->timer = call->timer.expires;
|
||||
__entry->call = call;
|
||||
__entry->why = why;
|
||||
__entry->now = now;
|
||||
__entry->ack_at = call->ack_at;
|
||||
__entry->resend_at = call->resend_at;
|
||||
__entry->expect_rx_by = call->expect_rx_by;
|
||||
__entry->expect_req_by = call->expect_req_by;
|
||||
__entry->expect_term_by = call->expect_term_by;
|
||||
__entry->timer = call->timer.expires;
|
||||
),
|
||||
|
||||
TP_printk("c=%p %s x=%lld a=%lld r=%lld t=%ld",
|
||||
TP_printk("c=%p %s a=%ld r=%ld xr=%ld xq=%ld xt=%ld t=%ld",
|
||||
__entry->call,
|
||||
__print_symbolic(__entry->why, rxrpc_timer_traces),
|
||||
ktime_to_ns(ktime_sub(__entry->expire_at, __entry->now)),
|
||||
ktime_to_ns(ktime_sub(__entry->ack_at, __entry->now)),
|
||||
ktime_to_ns(ktime_sub(__entry->resend_at, __entry->now)),
|
||||
__entry->timer - __entry->now_j)
|
||||
__entry->ack_at - __entry->now,
|
||||
__entry->resend_at - __entry->now,
|
||||
__entry->expect_rx_by - __entry->now,
|
||||
__entry->expect_req_by - __entry->now,
|
||||
__entry->expect_term_by - __entry->now,
|
||||
__entry->timer - __entry->now)
|
||||
);
|
||||
|
||||
TRACE_EVENT(rxrpc_rx_lose,
|
||||
|
|
|
@ -59,6 +59,7 @@ enum rxrpc_cmsg_type {
|
|||
RXRPC_EXCLUSIVE_CALL = 10, /* s-: Call should be on exclusive connection */
|
||||
RXRPC_UPGRADE_SERVICE = 11, /* s-: Request service upgrade for client call */
|
||||
RXRPC_TX_LENGTH = 12, /* s-: Total length of Tx data */
|
||||
RXRPC_SET_CALL_TIMEOUT = 13, /* s-: Set one or more call timeouts */
|
||||
RXRPC__SUPPORTED
|
||||
};
|
||||
|
||||
|
|
|
@ -468,9 +468,9 @@ enum rxrpc_call_flag {
|
|||
enum rxrpc_call_event {
|
||||
RXRPC_CALL_EV_ACK, /* need to generate ACK */
|
||||
RXRPC_CALL_EV_ABORT, /* need to generate abort */
|
||||
RXRPC_CALL_EV_TIMER, /* Timer expired */
|
||||
RXRPC_CALL_EV_RESEND, /* Tx resend required */
|
||||
RXRPC_CALL_EV_PING, /* Ping send required */
|
||||
RXRPC_CALL_EV_EXPIRED, /* Expiry occurred */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -514,10 +514,14 @@ struct rxrpc_call {
|
|||
struct rxrpc_peer *peer; /* Peer record for remote address */
|
||||
struct rxrpc_sock __rcu *socket; /* socket responsible */
|
||||
struct mutex user_mutex; /* User access mutex */
|
||||
ktime_t ack_at; /* When deferred ACK needs to happen */
|
||||
ktime_t resend_at; /* When next resend needs to happen */
|
||||
ktime_t ping_at; /* When next to send a ping */
|
||||
ktime_t expire_at; /* When the call times out */
|
||||
unsigned long ack_at; /* When deferred ACK needs to happen */
|
||||
unsigned long resend_at; /* When next resend needs to happen */
|
||||
unsigned long ping_at; /* When next to send a ping */
|
||||
unsigned long expect_rx_by; /* When we expect to get a packet by */
|
||||
unsigned long expect_req_by; /* When we expect to get a request DATA packet by */
|
||||
unsigned long expect_term_by; /* When we expect call termination by */
|
||||
u32 next_rx_timo; /* Timeout for next Rx packet (jif) */
|
||||
u32 next_req_timo; /* Timeout for next Rx request packet (jif) */
|
||||
struct timer_list timer; /* Combined event timer */
|
||||
struct work_struct processor; /* Event processor */
|
||||
rxrpc_notify_rx_t notify_rx; /* kernel service Rx notification function */
|
||||
|
@ -697,12 +701,19 @@ int rxrpc_reject_call(struct rxrpc_sock *);
|
|||
/*
|
||||
* call_event.c
|
||||
*/
|
||||
void __rxrpc_set_timer(struct rxrpc_call *, enum rxrpc_timer_trace, ktime_t);
|
||||
void rxrpc_set_timer(struct rxrpc_call *, enum rxrpc_timer_trace, ktime_t);
|
||||
void rxrpc_propose_ACK(struct rxrpc_call *, u8, u16, u32, bool, bool,
|
||||
enum rxrpc_propose_ack_trace);
|
||||
void rxrpc_process_call(struct work_struct *);
|
||||
|
||||
static inline void rxrpc_reduce_call_timer(struct rxrpc_call *call,
|
||||
unsigned long expire_at,
|
||||
unsigned long now,
|
||||
enum rxrpc_timer_trace why)
|
||||
{
|
||||
trace_rxrpc_timer(call, why, now);
|
||||
timer_reduce(&call->timer, expire_at);
|
||||
}
|
||||
|
||||
/*
|
||||
* call_object.c
|
||||
*/
|
||||
|
@ -843,8 +854,8 @@ static inline bool __rxrpc_abort_eproto(struct rxrpc_call *call,
|
|||
*/
|
||||
extern unsigned int rxrpc_max_client_connections;
|
||||
extern unsigned int rxrpc_reap_client_connections;
|
||||
extern unsigned int rxrpc_conn_idle_client_expiry;
|
||||
extern unsigned int rxrpc_conn_idle_client_fast_expiry;
|
||||
extern unsigned long rxrpc_conn_idle_client_expiry;
|
||||
extern unsigned long rxrpc_conn_idle_client_fast_expiry;
|
||||
extern struct idr rxrpc_client_conn_ids;
|
||||
|
||||
void rxrpc_destroy_client_conn_ids(void);
|
||||
|
@ -976,13 +987,13 @@ static inline void rxrpc_queue_local(struct rxrpc_local *local)
|
|||
* misc.c
|
||||
*/
|
||||
extern unsigned int rxrpc_max_backlog __read_mostly;
|
||||
extern unsigned int rxrpc_requested_ack_delay;
|
||||
extern unsigned int rxrpc_soft_ack_delay;
|
||||
extern unsigned int rxrpc_idle_ack_delay;
|
||||
extern unsigned long rxrpc_requested_ack_delay;
|
||||
extern unsigned long rxrpc_soft_ack_delay;
|
||||
extern unsigned long rxrpc_idle_ack_delay;
|
||||
extern unsigned int rxrpc_rx_window_size;
|
||||
extern unsigned int rxrpc_rx_mtu;
|
||||
extern unsigned int rxrpc_rx_jumbo_max;
|
||||
extern unsigned int rxrpc_resend_timeout;
|
||||
extern unsigned long rxrpc_resend_timeout;
|
||||
|
||||
extern const s8 rxrpc_ack_priority[];
|
||||
|
||||
|
|
|
@ -21,80 +21,6 @@
|
|||
#include <net/af_rxrpc.h>
|
||||
#include "ar-internal.h"
|
||||
|
||||
/*
|
||||
* Set the timer
|
||||
*/
|
||||
void __rxrpc_set_timer(struct rxrpc_call *call, enum rxrpc_timer_trace why,
|
||||
ktime_t now)
|
||||
{
|
||||
unsigned long t_j, now_j = jiffies;
|
||||
ktime_t t;
|
||||
bool queue = false;
|
||||
|
||||
if (call->state < RXRPC_CALL_COMPLETE) {
|
||||
t = call->expire_at;
|
||||
if (!ktime_after(t, now)) {
|
||||
trace_rxrpc_timer(call, why, now, now_j);
|
||||
queue = true;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ktime_after(call->resend_at, now)) {
|
||||
call->resend_at = call->expire_at;
|
||||
if (!test_and_set_bit(RXRPC_CALL_EV_RESEND, &call->events))
|
||||
queue = true;
|
||||
} else if (ktime_before(call->resend_at, t)) {
|
||||
t = call->resend_at;
|
||||
}
|
||||
|
||||
if (!ktime_after(call->ack_at, now)) {
|
||||
call->ack_at = call->expire_at;
|
||||
if (!test_and_set_bit(RXRPC_CALL_EV_ACK, &call->events))
|
||||
queue = true;
|
||||
} else if (ktime_before(call->ack_at, t)) {
|
||||
t = call->ack_at;
|
||||
}
|
||||
|
||||
if (!ktime_after(call->ping_at, now)) {
|
||||
call->ping_at = call->expire_at;
|
||||
if (!test_and_set_bit(RXRPC_CALL_EV_PING, &call->events))
|
||||
queue = true;
|
||||
} else if (ktime_before(call->ping_at, t)) {
|
||||
t = call->ping_at;
|
||||
}
|
||||
|
||||
t_j = nsecs_to_jiffies(ktime_to_ns(ktime_sub(t, now)));
|
||||
t_j += jiffies;
|
||||
|
||||
/* We have to make sure that the calculated jiffies value falls
|
||||
* at or after the nsec value, or we may loop ceaselessly
|
||||
* because the timer times out, but we haven't reached the nsec
|
||||
* timeout yet.
|
||||
*/
|
||||
t_j++;
|
||||
|
||||
if (call->timer.expires != t_j || !timer_pending(&call->timer)) {
|
||||
mod_timer(&call->timer, t_j);
|
||||
trace_rxrpc_timer(call, why, now, now_j);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (queue)
|
||||
rxrpc_queue_call(call);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the timer
|
||||
*/
|
||||
void rxrpc_set_timer(struct rxrpc_call *call, enum rxrpc_timer_trace why,
|
||||
ktime_t now)
|
||||
{
|
||||
read_lock_bh(&call->state_lock);
|
||||
__rxrpc_set_timer(call, why, now);
|
||||
read_unlock_bh(&call->state_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Propose a PING ACK be sent.
|
||||
*/
|
||||
|
@ -106,12 +32,13 @@ static void rxrpc_propose_ping(struct rxrpc_call *call,
|
|||
!test_and_set_bit(RXRPC_CALL_EV_PING, &call->events))
|
||||
rxrpc_queue_call(call);
|
||||
} else {
|
||||
ktime_t now = ktime_get_real();
|
||||
ktime_t ping_at = ktime_add_ms(now, rxrpc_idle_ack_delay);
|
||||
unsigned long now = jiffies;
|
||||
unsigned long ping_at = now + rxrpc_idle_ack_delay;
|
||||
|
||||
if (ktime_before(ping_at, call->ping_at)) {
|
||||
call->ping_at = ping_at;
|
||||
rxrpc_set_timer(call, rxrpc_timer_set_for_ping, now);
|
||||
if (time_before(ping_at, call->ping_at)) {
|
||||
WRITE_ONCE(call->ping_at, ping_at);
|
||||
rxrpc_reduce_call_timer(call, ping_at, now,
|
||||
rxrpc_timer_set_for_ping);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -125,8 +52,7 @@ static void __rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason,
|
|||
enum rxrpc_propose_ack_trace why)
|
||||
{
|
||||
enum rxrpc_propose_ack_outcome outcome = rxrpc_propose_ack_use;
|
||||
unsigned int expiry = rxrpc_soft_ack_delay;
|
||||
ktime_t now, ack_at;
|
||||
unsigned long now, ack_at, expiry = rxrpc_soft_ack_delay;
|
||||
s8 prior = rxrpc_ack_priority[ack_reason];
|
||||
|
||||
/* Pings are handled specially because we don't want to accidentally
|
||||
|
@ -190,11 +116,12 @@ static void __rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason,
|
|||
background)
|
||||
rxrpc_queue_call(call);
|
||||
} else {
|
||||
now = ktime_get_real();
|
||||
ack_at = ktime_add_ms(now, expiry);
|
||||
if (ktime_before(ack_at, call->ack_at)) {
|
||||
call->ack_at = ack_at;
|
||||
rxrpc_set_timer(call, rxrpc_timer_set_for_ack, now);
|
||||
now = jiffies;
|
||||
ack_at = jiffies + expiry;
|
||||
if (time_before(ack_at, call->ack_at)) {
|
||||
WRITE_ONCE(call->ack_at, ack_at);
|
||||
rxrpc_reduce_call_timer(call, ack_at, now,
|
||||
rxrpc_timer_set_for_ack);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -227,18 +154,20 @@ static void rxrpc_congestion_timeout(struct rxrpc_call *call)
|
|||
/*
|
||||
* Perform retransmission of NAK'd and unack'd packets.
|
||||
*/
|
||||
static void rxrpc_resend(struct rxrpc_call *call, ktime_t now)
|
||||
static void rxrpc_resend(struct rxrpc_call *call, unsigned long now_j)
|
||||
{
|
||||
struct rxrpc_skb_priv *sp;
|
||||
struct sk_buff *skb;
|
||||
unsigned long resend_at;
|
||||
rxrpc_seq_t cursor, seq, top;
|
||||
ktime_t max_age, oldest, ack_ts;
|
||||
ktime_t now, max_age, oldest, ack_ts;
|
||||
int ix;
|
||||
u8 annotation, anno_type, retrans = 0, unacked = 0;
|
||||
|
||||
_enter("{%d,%d}", call->tx_hard_ack, call->tx_top);
|
||||
|
||||
max_age = ktime_sub_ms(now, rxrpc_resend_timeout);
|
||||
now = ktime_get_real();
|
||||
max_age = ktime_sub_ms(now, rxrpc_resend_timeout * 1000 / HZ);
|
||||
|
||||
spin_lock_bh(&call->lock);
|
||||
|
||||
|
@ -282,7 +211,9 @@ static void rxrpc_resend(struct rxrpc_call *call, ktime_t now)
|
|||
ktime_to_ns(ktime_sub(skb->tstamp, max_age)));
|
||||
}
|
||||
|
||||
call->resend_at = ktime_add_ms(oldest, rxrpc_resend_timeout);
|
||||
resend_at = nsecs_to_jiffies(ktime_to_ns(ktime_sub(oldest, now)));
|
||||
resend_at += jiffies + rxrpc_resend_timeout;
|
||||
WRITE_ONCE(call->resend_at, resend_at);
|
||||
|
||||
if (unacked)
|
||||
rxrpc_congestion_timeout(call);
|
||||
|
@ -292,7 +223,8 @@ static void rxrpc_resend(struct rxrpc_call *call, ktime_t now)
|
|||
* retransmitting data.
|
||||
*/
|
||||
if (!retrans) {
|
||||
rxrpc_set_timer(call, rxrpc_timer_set_for_resend, now);
|
||||
rxrpc_reduce_call_timer(call, resend_at, now,
|
||||
rxrpc_timer_set_for_resend);
|
||||
spin_unlock_bh(&call->lock);
|
||||
ack_ts = ktime_sub(now, call->acks_latest_ts);
|
||||
if (ktime_to_ns(ack_ts) < call->peer->rtt)
|
||||
|
@ -364,7 +296,7 @@ void rxrpc_process_call(struct work_struct *work)
|
|||
{
|
||||
struct rxrpc_call *call =
|
||||
container_of(work, struct rxrpc_call, processor);
|
||||
ktime_t now;
|
||||
unsigned long now, next, t;
|
||||
|
||||
rxrpc_see_call(call);
|
||||
|
||||
|
@ -384,8 +316,50 @@ void rxrpc_process_call(struct work_struct *work)
|
|||
goto out_put;
|
||||
}
|
||||
|
||||
now = ktime_get_real();
|
||||
if (ktime_before(call->expire_at, now)) {
|
||||
/* Work out if any timeouts tripped */
|
||||
now = jiffies;
|
||||
t = READ_ONCE(call->expect_rx_by);
|
||||
if (time_after_eq(now, t)) {
|
||||
trace_rxrpc_timer(call, rxrpc_timer_exp_normal, now);
|
||||
set_bit(RXRPC_CALL_EV_EXPIRED, &call->events);
|
||||
}
|
||||
|
||||
t = READ_ONCE(call->expect_req_by);
|
||||
if (call->state == RXRPC_CALL_SERVER_RECV_REQUEST &&
|
||||
time_after_eq(now, t)) {
|
||||
trace_rxrpc_timer(call, rxrpc_timer_exp_idle, now);
|
||||
set_bit(RXRPC_CALL_EV_EXPIRED, &call->events);
|
||||
}
|
||||
|
||||
t = READ_ONCE(call->expect_term_by);
|
||||
if (time_after_eq(now, t)) {
|
||||
trace_rxrpc_timer(call, rxrpc_timer_exp_hard, now);
|
||||
set_bit(RXRPC_CALL_EV_EXPIRED, &call->events);
|
||||
}
|
||||
|
||||
t = READ_ONCE(call->ack_at);
|
||||
if (time_after_eq(now, t)) {
|
||||
trace_rxrpc_timer(call, rxrpc_timer_exp_ack, now);
|
||||
cmpxchg(&call->ack_at, t, now + MAX_JIFFY_OFFSET);
|
||||
set_bit(RXRPC_CALL_EV_ACK, &call->events);
|
||||
}
|
||||
|
||||
t = READ_ONCE(call->ping_at);
|
||||
if (time_after_eq(now, t)) {
|
||||
trace_rxrpc_timer(call, rxrpc_timer_exp_ping, now);
|
||||
cmpxchg(&call->ping_at, t, now + MAX_JIFFY_OFFSET);
|
||||
set_bit(RXRPC_CALL_EV_PING, &call->events);
|
||||
}
|
||||
|
||||
t = READ_ONCE(call->resend_at);
|
||||
if (time_after_eq(now, t)) {
|
||||
trace_rxrpc_timer(call, rxrpc_timer_exp_resend, now);
|
||||
cmpxchg(&call->resend_at, t, now + MAX_JIFFY_OFFSET);
|
||||
set_bit(RXRPC_CALL_EV_RESEND, &call->events);
|
||||
}
|
||||
|
||||
/* Process events */
|
||||
if (test_and_clear_bit(RXRPC_CALL_EV_EXPIRED, &call->events)) {
|
||||
rxrpc_abort_call("EXP", call, 0, RX_USER_ABORT, -ETIME);
|
||||
set_bit(RXRPC_CALL_EV_ABORT, &call->events);
|
||||
goto recheck_state;
|
||||
|
@ -408,7 +382,22 @@ void rxrpc_process_call(struct work_struct *work)
|
|||
goto recheck_state;
|
||||
}
|
||||
|
||||
rxrpc_set_timer(call, rxrpc_timer_set_for_resend, now);
|
||||
/* Make sure the timer is restarted */
|
||||
next = call->expect_rx_by;
|
||||
|
||||
#define set(T) { t = READ_ONCE(T); if (time_before(t, next)) next = t; }
|
||||
|
||||
set(call->expect_req_by);
|
||||
set(call->expect_term_by);
|
||||
set(call->ack_at);
|
||||
set(call->resend_at);
|
||||
set(call->ping_at);
|
||||
|
||||
now = jiffies;
|
||||
if (time_after_eq(now, next))
|
||||
goto recheck_state;
|
||||
|
||||
rxrpc_reduce_call_timer(call, next, now, rxrpc_timer_restart);
|
||||
|
||||
/* other events may have been raised since we started checking */
|
||||
if (call->events && call->state < RXRPC_CALL_COMPLETE) {
|
||||
|
|
|
@ -51,8 +51,10 @@ static void rxrpc_call_timer_expired(unsigned long _call)
|
|||
|
||||
_enter("%d", call->debug_id);
|
||||
|
||||
if (call->state < RXRPC_CALL_COMPLETE)
|
||||
rxrpc_set_timer(call, rxrpc_timer_expired, ktime_get_real());
|
||||
if (call->state < RXRPC_CALL_COMPLETE) {
|
||||
trace_rxrpc_timer(call, rxrpc_timer_expired, jiffies);
|
||||
rxrpc_queue_call(call);
|
||||
}
|
||||
}
|
||||
|
||||
static struct lock_class_key rxrpc_call_user_mutex_lock_class_key;
|
||||
|
@ -139,6 +141,8 @@ struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *rx, gfp_t gfp)
|
|||
atomic_set(&call->usage, 1);
|
||||
call->debug_id = atomic_inc_return(&rxrpc_debug_id);
|
||||
call->tx_total_len = -1;
|
||||
call->next_rx_timo = 20 * HZ;
|
||||
call->next_req_timo = 1 * HZ;
|
||||
|
||||
memset(&call->sock_node, 0xed, sizeof(call->sock_node));
|
||||
|
||||
|
@ -189,15 +193,16 @@ static struct rxrpc_call *rxrpc_alloc_client_call(struct rxrpc_sock *rx,
|
|||
*/
|
||||
static void rxrpc_start_call_timer(struct rxrpc_call *call)
|
||||
{
|
||||
ktime_t now = ktime_get_real(), expire_at;
|
||||
unsigned long now = jiffies;
|
||||
unsigned long j = now + MAX_JIFFY_OFFSET;
|
||||
|
||||
expire_at = ktime_add_ms(now, rxrpc_max_call_lifetime);
|
||||
call->expire_at = expire_at;
|
||||
call->ack_at = expire_at;
|
||||
call->ping_at = expire_at;
|
||||
call->resend_at = expire_at;
|
||||
call->timer.expires = jiffies + LONG_MAX / 2;
|
||||
rxrpc_set_timer(call, rxrpc_timer_begin, now);
|
||||
call->ack_at = j;
|
||||
call->resend_at = j;
|
||||
call->ping_at = j;
|
||||
call->expect_rx_by = j;
|
||||
call->expect_req_by = j;
|
||||
call->expect_term_by = j;
|
||||
call->timer.expires = now;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -85,8 +85,8 @@
|
|||
|
||||
__read_mostly unsigned int rxrpc_max_client_connections = 1000;
|
||||
__read_mostly unsigned int rxrpc_reap_client_connections = 900;
|
||||
__read_mostly unsigned int rxrpc_conn_idle_client_expiry = 2 * 60 * HZ;
|
||||
__read_mostly unsigned int rxrpc_conn_idle_client_fast_expiry = 2 * HZ;
|
||||
__read_mostly unsigned long rxrpc_conn_idle_client_expiry = 2 * 60 * HZ;
|
||||
__read_mostly unsigned long rxrpc_conn_idle_client_fast_expiry = 2 * HZ;
|
||||
|
||||
/*
|
||||
* We use machine-unique IDs for our client connections.
|
||||
|
|
|
@ -318,16 +318,18 @@ static bool rxrpc_end_tx_phase(struct rxrpc_call *call, bool reply_begun,
|
|||
static bool rxrpc_receiving_reply(struct rxrpc_call *call)
|
||||
{
|
||||
struct rxrpc_ack_summary summary = { 0 };
|
||||
unsigned long now, timo;
|
||||
rxrpc_seq_t top = READ_ONCE(call->tx_top);
|
||||
|
||||
if (call->ackr_reason) {
|
||||
spin_lock_bh(&call->lock);
|
||||
call->ackr_reason = 0;
|
||||
call->resend_at = call->expire_at;
|
||||
call->ack_at = call->expire_at;
|
||||
spin_unlock_bh(&call->lock);
|
||||
rxrpc_set_timer(call, rxrpc_timer_init_for_reply,
|
||||
ktime_get_real());
|
||||
now = jiffies;
|
||||
timo = now + MAX_JIFFY_OFFSET;
|
||||
WRITE_ONCE(call->resend_at, timo);
|
||||
WRITE_ONCE(call->ack_at, timo);
|
||||
trace_rxrpc_timer(call, rxrpc_timer_init_for_reply, now);
|
||||
}
|
||||
|
||||
if (!test_bit(RXRPC_CALL_TX_LAST, &call->flags))
|
||||
|
@ -437,6 +439,19 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb,
|
|||
if (state >= RXRPC_CALL_COMPLETE)
|
||||
return;
|
||||
|
||||
if (call->state == RXRPC_CALL_SERVER_RECV_REQUEST) {
|
||||
unsigned long timo = READ_ONCE(call->next_req_timo);
|
||||
unsigned long now, expect_req_by;
|
||||
|
||||
if (timo) {
|
||||
now = jiffies;
|
||||
expect_req_by = now + timo;
|
||||
WRITE_ONCE(call->expect_req_by, expect_req_by);
|
||||
rxrpc_reduce_call_timer(call, expect_req_by, now,
|
||||
rxrpc_timer_set_for_idle);
|
||||
}
|
||||
}
|
||||
|
||||
/* Received data implicitly ACKs all of the request packets we sent
|
||||
* when we're acting as a client.
|
||||
*/
|
||||
|
@ -908,9 +923,20 @@ static void rxrpc_input_call_packet(struct rxrpc_call *call,
|
|||
struct sk_buff *skb, u16 skew)
|
||||
{
|
||||
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
|
||||
unsigned long timo;
|
||||
|
||||
_enter("%p,%p", call, skb);
|
||||
|
||||
timo = READ_ONCE(call->next_rx_timo);
|
||||
if (timo) {
|
||||
unsigned long now = jiffies, expect_rx_by;
|
||||
|
||||
expect_rx_by = jiffies + timo;
|
||||
WRITE_ONCE(call->expect_rx_by, expect_rx_by);
|
||||
rxrpc_reduce_call_timer(call, expect_rx_by, now,
|
||||
rxrpc_timer_set_for_normal);
|
||||
}
|
||||
|
||||
switch (sp->hdr.type) {
|
||||
case RXRPC_PACKET_TYPE_DATA:
|
||||
rxrpc_input_data(call, skb, skew);
|
||||
|
|
|
@ -20,34 +20,29 @@
|
|||
*/
|
||||
unsigned int rxrpc_max_backlog __read_mostly = 10;
|
||||
|
||||
/*
|
||||
* Maximum lifetime of a call (in mx).
|
||||
*/
|
||||
unsigned int rxrpc_max_call_lifetime = 60 * 1000;
|
||||
|
||||
/*
|
||||
* How long to wait before scheduling ACK generation after seeing a
|
||||
* packet with RXRPC_REQUEST_ACK set (in ms).
|
||||
* packet with RXRPC_REQUEST_ACK set (in jiffies).
|
||||
*/
|
||||
unsigned int rxrpc_requested_ack_delay = 1;
|
||||
unsigned long rxrpc_requested_ack_delay = 1;
|
||||
|
||||
/*
|
||||
* How long to wait before scheduling an ACK with subtype DELAY (in ms).
|
||||
* How long to wait before scheduling an ACK with subtype DELAY (in jiffies).
|
||||
*
|
||||
* We use this when we've received new data packets. If those packets aren't
|
||||
* all consumed within this time we will send a DELAY ACK if an ACK was not
|
||||
* requested to let the sender know it doesn't need to resend.
|
||||
*/
|
||||
unsigned int rxrpc_soft_ack_delay = 1 * 1000;
|
||||
unsigned long rxrpc_soft_ack_delay = HZ;
|
||||
|
||||
/*
|
||||
* How long to wait before scheduling an ACK with subtype IDLE (in ms).
|
||||
* How long to wait before scheduling an ACK with subtype IDLE (in jiffies).
|
||||
*
|
||||
* We use this when we've consumed some previously soft-ACK'd packets when
|
||||
* further packets aren't immediately received to decide when to send an IDLE
|
||||
* ACK let the other end know that it can free up its Tx buffer space.
|
||||
*/
|
||||
unsigned int rxrpc_idle_ack_delay = 0.5 * 1000;
|
||||
unsigned long rxrpc_idle_ack_delay = HZ / 2;
|
||||
|
||||
/*
|
||||
* Receive window size in packets. This indicates the maximum number of
|
||||
|
@ -75,7 +70,7 @@ unsigned int rxrpc_rx_jumbo_max = 4;
|
|||
/*
|
||||
* Time till packet resend (in milliseconds).
|
||||
*/
|
||||
unsigned int rxrpc_resend_timeout = 4 * 1000;
|
||||
unsigned long rxrpc_resend_timeout = 4 * HZ;
|
||||
|
||||
const s8 rxrpc_ack_priority[] = {
|
||||
[0] = 0,
|
||||
|
|
|
@ -163,7 +163,7 @@ static void rxrpc_end_rx_phase(struct rxrpc_call *call, rxrpc_serial_t serial)
|
|||
case RXRPC_CALL_SERVER_RECV_REQUEST:
|
||||
call->tx_phase = true;
|
||||
call->state = RXRPC_CALL_SERVER_ACK_REQUEST;
|
||||
call->ack_at = call->expire_at;
|
||||
call->expect_req_by = jiffies + MAX_JIFFY_OFFSET;
|
||||
write_unlock_bh(&call->state_lock);
|
||||
rxrpc_propose_ACK(call, RXRPC_ACK_DELAY, 0, serial, false, true,
|
||||
rxrpc_propose_ack_processing_op);
|
||||
|
|
|
@ -158,6 +158,7 @@ static void rxrpc_queue_packet(struct rxrpc_sock *rx, struct rxrpc_call *call,
|
|||
rxrpc_notify_end_tx_t notify_end_tx)
|
||||
{
|
||||
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
|
||||
unsigned long now;
|
||||
rxrpc_seq_t seq = sp->hdr.seq;
|
||||
int ret, ix;
|
||||
u8 annotation = RXRPC_TX_ANNO_UNACK;
|
||||
|
@ -197,11 +198,11 @@ static void rxrpc_queue_packet(struct rxrpc_sock *rx, struct rxrpc_call *call,
|
|||
break;
|
||||
case RXRPC_CALL_SERVER_ACK_REQUEST:
|
||||
call->state = RXRPC_CALL_SERVER_SEND_REPLY;
|
||||
call->ack_at = call->expire_at;
|
||||
now = jiffies;
|
||||
WRITE_ONCE(call->ack_at, now + MAX_JIFFY_OFFSET);
|
||||
if (call->ackr_reason == RXRPC_ACK_DELAY)
|
||||
call->ackr_reason = 0;
|
||||
__rxrpc_set_timer(call, rxrpc_timer_init_for_send_reply,
|
||||
ktime_get_real());
|
||||
trace_rxrpc_timer(call, rxrpc_timer_init_for_send_reply, now);
|
||||
if (!last)
|
||||
break;
|
||||
/* Fall through */
|
||||
|
@ -223,14 +224,12 @@ static void rxrpc_queue_packet(struct rxrpc_sock *rx, struct rxrpc_call *call,
|
|||
_debug("need instant resend %d", ret);
|
||||
rxrpc_instant_resend(call, ix);
|
||||
} else {
|
||||
ktime_t now = ktime_get_real(), resend_at;
|
||||
unsigned long now = jiffies, resend_at;
|
||||
|
||||
resend_at = ktime_add_ms(now, rxrpc_resend_timeout);
|
||||
|
||||
if (ktime_before(resend_at, call->resend_at)) {
|
||||
call->resend_at = resend_at;
|
||||
rxrpc_set_timer(call, rxrpc_timer_set_for_send, now);
|
||||
}
|
||||
resend_at = now + rxrpc_resend_timeout;
|
||||
WRITE_ONCE(call->resend_at, resend_at);
|
||||
rxrpc_reduce_call_timer(call, resend_at, now,
|
||||
rxrpc_timer_set_for_send);
|
||||
}
|
||||
|
||||
rxrpc_free_skb(skb, rxrpc_skb_tx_freed);
|
||||
|
@ -513,6 +512,19 @@ static int rxrpc_sendmsg_cmsg(struct msghdr *msg, struct rxrpc_send_params *p)
|
|||
return -EINVAL;
|
||||
break;
|
||||
|
||||
case RXRPC_SET_CALL_TIMEOUT:
|
||||
if (len & 3 || len < 4 || len > 12)
|
||||
return -EINVAL;
|
||||
memcpy(&p->call.timeouts, CMSG_DATA(cmsg), len);
|
||||
p->call.nr_timeouts = len / 4;
|
||||
if (p->call.timeouts.hard > INT_MAX / HZ)
|
||||
return -ERANGE;
|
||||
if (p->call.nr_timeouts >= 2 && p->call.timeouts.idle > 60 * 60 * 1000)
|
||||
return -ERANGE;
|
||||
if (p->call.nr_timeouts >= 3 && p->call.timeouts.normal > 60 * 60 * 1000)
|
||||
return -ERANGE;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -577,11 +589,13 @@ int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len)
|
|||
{
|
||||
enum rxrpc_call_state state;
|
||||
struct rxrpc_call *call;
|
||||
unsigned long now, j;
|
||||
int ret;
|
||||
|
||||
struct rxrpc_send_params p = {
|
||||
.call.tx_total_len = -1,
|
||||
.call.user_call_ID = 0,
|
||||
.call.nr_timeouts = 0,
|
||||
.abort_code = 0,
|
||||
.command = RXRPC_CMD_SEND_DATA,
|
||||
.exclusive = false,
|
||||
|
@ -646,6 +660,31 @@ int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len)
|
|||
}
|
||||
}
|
||||
|
||||
switch (p.call.nr_timeouts) {
|
||||
case 3:
|
||||
j = msecs_to_jiffies(p.call.timeouts.normal);
|
||||
if (p.call.timeouts.normal > 0 && j == 0)
|
||||
j = 1;
|
||||
WRITE_ONCE(call->next_rx_timo, j);
|
||||
/* Fall through */
|
||||
case 2:
|
||||
j = msecs_to_jiffies(p.call.timeouts.idle);
|
||||
if (p.call.timeouts.idle > 0 && j == 0)
|
||||
j = 1;
|
||||
WRITE_ONCE(call->next_req_timo, j);
|
||||
/* Fall through */
|
||||
case 1:
|
||||
if (p.call.timeouts.hard > 0) {
|
||||
j = msecs_to_jiffies(p.call.timeouts.hard);
|
||||
now = jiffies;
|
||||
j += now;
|
||||
WRITE_ONCE(call->expect_term_by, j);
|
||||
rxrpc_reduce_call_timer(call, j, now,
|
||||
rxrpc_timer_set_for_hard);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
state = READ_ONCE(call->state);
|
||||
_debug("CALL %d USR %lx ST %d on CONN %p",
|
||||
call->debug_id, call->user_call_ID, state, call->conn);
|
||||
|
|
|
@ -21,6 +21,8 @@ static const unsigned int four = 4;
|
|||
static const unsigned int thirtytwo = 32;
|
||||
static const unsigned int n_65535 = 65535;
|
||||
static const unsigned int n_max_acks = RXRPC_RXTX_BUFF_SIZE - 1;
|
||||
static const unsigned long one_jiffy = 1;
|
||||
static const unsigned long max_jiffies = MAX_JIFFY_OFFSET;
|
||||
|
||||
/*
|
||||
* RxRPC operating parameters.
|
||||
|
@ -29,64 +31,60 @@ static const unsigned int n_max_acks = RXRPC_RXTX_BUFF_SIZE - 1;
|
|||
* information on the individual parameters.
|
||||
*/
|
||||
static struct ctl_table rxrpc_sysctl_table[] = {
|
||||
/* Values measured in milliseconds */
|
||||
/* Values measured in milliseconds but used in jiffies */
|
||||
{
|
||||
.procname = "req_ack_delay",
|
||||
.data = &rxrpc_requested_ack_delay,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.maxlen = sizeof(unsigned long),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec,
|
||||
.extra1 = (void *)&zero,
|
||||
.proc_handler = proc_doulongvec_ms_jiffies_minmax,
|
||||
.extra1 = (void *)&one_jiffy,
|
||||
.extra2 = (void *)&max_jiffies,
|
||||
},
|
||||
{
|
||||
.procname = "soft_ack_delay",
|
||||
.data = &rxrpc_soft_ack_delay,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.maxlen = sizeof(unsigned long),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec,
|
||||
.extra1 = (void *)&one,
|
||||
.proc_handler = proc_doulongvec_ms_jiffies_minmax,
|
||||
.extra1 = (void *)&one_jiffy,
|
||||
.extra2 = (void *)&max_jiffies,
|
||||
},
|
||||
{
|
||||
.procname = "idle_ack_delay",
|
||||
.data = &rxrpc_idle_ack_delay,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.maxlen = sizeof(unsigned long),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec,
|
||||
.extra1 = (void *)&one,
|
||||
},
|
||||
{
|
||||
.procname = "resend_timeout",
|
||||
.data = &rxrpc_resend_timeout,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec,
|
||||
.extra1 = (void *)&one,
|
||||
.proc_handler = proc_doulongvec_ms_jiffies_minmax,
|
||||
.extra1 = (void *)&one_jiffy,
|
||||
.extra2 = (void *)&max_jiffies,
|
||||
},
|
||||
{
|
||||
.procname = "idle_conn_expiry",
|
||||
.data = &rxrpc_conn_idle_client_expiry,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.maxlen = sizeof(unsigned long),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_ms_jiffies,
|
||||
.extra1 = (void *)&one,
|
||||
.proc_handler = proc_doulongvec_ms_jiffies_minmax,
|
||||
.extra1 = (void *)&one_jiffy,
|
||||
.extra2 = (void *)&max_jiffies,
|
||||
},
|
||||
{
|
||||
.procname = "idle_conn_fast_expiry",
|
||||
.data = &rxrpc_conn_idle_client_fast_expiry,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.maxlen = sizeof(unsigned long),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_ms_jiffies,
|
||||
.extra1 = (void *)&one,
|
||||
.proc_handler = proc_doulongvec_ms_jiffies_minmax,
|
||||
.extra1 = (void *)&one_jiffy,
|
||||
.extra2 = (void *)&max_jiffies,
|
||||
},
|
||||
|
||||
/* Values measured in seconds but used in jiffies */
|
||||
{
|
||||
.procname = "max_call_lifetime",
|
||||
.data = &rxrpc_max_call_lifetime,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.procname = "resend_timeout",
|
||||
.data = &rxrpc_resend_timeout,
|
||||
.maxlen = sizeof(unsigned long),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec,
|
||||
.extra1 = (void *)&one,
|
||||
.proc_handler = proc_doulongvec_ms_jiffies_minmax,
|
||||
.extra1 = (void *)&one_jiffy,
|
||||
.extra2 = (void *)&max_jiffies,
|
||||
},
|
||||
|
||||
/* Non-time values */
|
||||
|
|
Loading…
Reference in a new issue