Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2a9aa41f authored by David S. Miller's avatar David S. Miller
Browse files

Merge tag 'rxrpc-rewrite-20160923' of...

Merge tag 'rxrpc-rewrite-20160923' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs



David Howells says:

====================
rxrpc: Bug fixes and tracepoints

Here are a bunch of bug fixes:

 (1) Need to set the timestamp on a Tx packet before queueing it to avoid
     trouble with the retransmission function.

 (2) Don't send an ACK at the end of the service reply transmission; it's
     the responsibility of the client to send an ACK to close the call.
     The service can resend the last DATA packet or send a PING ACK.

 (3) Wake sendmsg() on abnormal call termination.

 (4) Use ktime_add_ms() not ktime_add_ns() to add millisecond offsets.

 (5) Use before_eq() & co. to compare serial numbers (which may wrap).

 (6) Start the resend timer on DATA packet transmission.

 (7) Don't accidentally cancel a retransmission upon receiving a NACK.

 (8) Fix the call timer setting function to deal with timeouts that are now
     or past.

 (9) Don't use a flag to communicate the presence of the last packet in the
     Tx buffer from sendmsg to the input routines where ACK and DATA
     reception is handled.  The problem is that there's a window between
     queueing the last packet for transmission and setting the flag in
     which ACKs or reply DATA packets can arrive, causing apparent state
     machine violation issues.

     Instead use the annotation buffer to mark the last packet and pick up
     and set the flag in the input routines.

(10) Don't call the tx_ack tracepoint and don't allocate a serial number if
     someone else nicked the ACK we were about to transmit.

There are also new tracepoints and one altered tracepoint used to track
down the above bugs:

(11) Call timer tracepoint.

(12) Data Tx tracepoint (and adjustments to ACK tracepoint).

(13) Injected Rx packet loss tracepoint.

(14) Ack proposal tracepoint.

(15) Retransmission selection tracepoint.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 3eb193e0 c6672e3f
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -123,6 +123,7 @@ struct rxrpc_ackpacket {
#define RXRPC_ACK_PING_RESPONSE		7	/* response to RXRPC_ACK_PING */
#define RXRPC_ACK_PING_RESPONSE		7	/* response to RXRPC_ACK_PING */
#define RXRPC_ACK_DELAY			8	/* nothing happened since received packet */
#define RXRPC_ACK_DELAY			8	/* nothing happened since received packet */
#define RXRPC_ACK_IDLE			9	/* ACK due to fully received ACK window */
#define RXRPC_ACK_IDLE			9	/* ACK due to fully received ACK window */
#define RXRPC_ACK__INVALID		10	/* Representation of invalid ACK reason */


	uint8_t		nAcks;		/* number of ACKs */
	uint8_t		nAcks;		/* number of ACKs */
#define RXRPC_MAXACKS	255
#define RXRPC_MAXACKS	255
+165 −9
Original line number Original line Diff line number Diff line
@@ -251,38 +251,72 @@ TRACE_EVENT(rxrpc_rx_ack,


	    TP_printk("c=%p %s f=%08x n=%u",
	    TP_printk("c=%p %s f=%08x n=%u",
		      __entry->call,
		      __entry->call,
		      rxrpc_acks(__entry->reason),
		      rxrpc_ack_names[__entry->reason],
		      __entry->first,
		      __entry->first,
		      __entry->n_acks)
		      __entry->n_acks)
	    );
	    );


TRACE_EVENT(rxrpc_tx_data,
	    TP_PROTO(struct rxrpc_call *call, rxrpc_seq_t seq,
		     rxrpc_serial_t serial, u8 flags, bool lose),

	    TP_ARGS(call, seq, serial, flags, lose),

	    TP_STRUCT__entry(
		    __field(struct rxrpc_call *,	call		)
		    __field(rxrpc_seq_t,		seq		)
		    __field(rxrpc_serial_t,		serial		)
		    __field(u8,				flags		)
		    __field(bool,			lose		)
			     ),

	    TP_fast_assign(
		    __entry->call = call;
		    __entry->seq = seq;
		    __entry->serial = serial;
		    __entry->flags = flags;
		    __entry->lose = lose;
			   ),

	    TP_printk("c=%p DATA %08x q=%08x fl=%02x%s",
		      __entry->call,
		      __entry->serial,
		      __entry->seq,
		      __entry->flags,
		      __entry->lose ? " *LOSE*" : "")
	    );

TRACE_EVENT(rxrpc_tx_ack,
TRACE_EVENT(rxrpc_tx_ack,
	    TP_PROTO(struct rxrpc_call *call, rxrpc_seq_t first,
	    TP_PROTO(struct rxrpc_call *call, rxrpc_serial_t serial,
		     rxrpc_serial_t serial, u8 reason, u8 n_acks),
		     rxrpc_seq_t ack_first, rxrpc_serial_t ack_serial,
		     u8 reason, u8 n_acks),


	    TP_ARGS(call, first, serial, reason, n_acks),
	    TP_ARGS(call, serial, ack_first, ack_serial, reason, n_acks),


	    TP_STRUCT__entry(
	    TP_STRUCT__entry(
		    __field(struct rxrpc_call *,	call		)
		    __field(struct rxrpc_call *,	call		)
		    __field(rxrpc_seq_t,		first		)
		    __field(rxrpc_serial_t,		serial		)
		    __field(rxrpc_serial_t,		serial		)
		    __field(rxrpc_seq_t,		ack_first	)
		    __field(rxrpc_serial_t,		ack_serial	)
		    __field(u8,				reason		)
		    __field(u8,				reason		)
		    __field(u8,				n_acks		)
		    __field(u8,				n_acks		)
			     ),
			     ),


	    TP_fast_assign(
	    TP_fast_assign(
		    __entry->call = call;
		    __entry->call = call;
		    __entry->first = first;
		    __entry->serial = serial;
		    __entry->serial = serial;
		    __entry->ack_first = ack_first;
		    __entry->ack_serial = ack_serial;
		    __entry->reason = reason;
		    __entry->reason = reason;
		    __entry->n_acks = n_acks;
		    __entry->n_acks = n_acks;
			   ),
			   ),


	    TP_printk("c=%p %s f=%08x r=%08x n=%u",
	    TP_printk(" c=%p ACK  %08x %s f=%08x r=%08x n=%u",
		      __entry->call,
		      __entry->call,
		      rxrpc_acks(__entry->reason),
		      __entry->first,
		      __entry->serial,
		      __entry->serial,
		      rxrpc_ack_names[__entry->reason],
		      __entry->ack_first,
		      __entry->ack_serial,
		      __entry->n_acks)
		      __entry->n_acks)
	    );
	    );


@@ -414,6 +448,128 @@ TRACE_EVENT(rxrpc_rtt_rx,
		      __entry->avg)
		      __entry->avg)
	    );
	    );


TRACE_EVENT(rxrpc_timer,
	    TP_PROTO(struct rxrpc_call *call, enum rxrpc_timer_trace why,
		     unsigned long now),

	    TP_ARGS(call, why, now),

	    TP_STRUCT__entry(
		    __field(struct rxrpc_call *,		call		)
		    __field(enum rxrpc_timer_trace,		why		)
		    __field(unsigned long,			now		)
		    __field(unsigned long,			expire_at	)
		    __field(unsigned long,			ack_at		)
		    __field(unsigned long,			resend_at	)
		    __field(unsigned 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->timer	= call->timer.expires;
			   ),

	    TP_printk("c=%p %s now=%lx x=%ld a=%ld r=%ld t=%ld",
		      __entry->call,
		      rxrpc_timer_traces[__entry->why],
		      __entry->now,
		      __entry->expire_at - __entry->now,
		      __entry->ack_at - __entry->now,
		      __entry->resend_at - __entry->now,
		      __entry->timer - __entry->now)
	    );

TRACE_EVENT(rxrpc_rx_lose,
	    TP_PROTO(struct rxrpc_skb_priv *sp),

	    TP_ARGS(sp),

	    TP_STRUCT__entry(
		    __field_struct(struct rxrpc_host_header,	hdr		)
			     ),

	    TP_fast_assign(
		    memcpy(&__entry->hdr, &sp->hdr, sizeof(__entry->hdr));
			   ),

	    TP_printk("%08x:%08x:%08x:%04x %08x %08x %02x %02x %s *LOSE*",
		      __entry->hdr.epoch, __entry->hdr.cid,
		      __entry->hdr.callNumber, __entry->hdr.serviceId,
		      __entry->hdr.serial, __entry->hdr.seq,
		      __entry->hdr.type, __entry->hdr.flags,
		      __entry->hdr.type <= 15 ? rxrpc_pkts[__entry->hdr.type] : "?UNK")
	    );

TRACE_EVENT(rxrpc_propose_ack,
	    TP_PROTO(struct rxrpc_call *call, enum rxrpc_propose_ack_trace why,
		     u8 ack_reason, rxrpc_serial_t serial, bool immediate,
		     bool background, enum rxrpc_propose_ack_outcome outcome),

	    TP_ARGS(call, why, ack_reason, serial, immediate, background,
		    outcome),

	    TP_STRUCT__entry(
		    __field(struct rxrpc_call *,		call		)
		    __field(enum rxrpc_propose_ack_trace,	why		)
		    __field(rxrpc_serial_t,			serial		)
		    __field(u8,					ack_reason	)
		    __field(bool,				immediate	)
		    __field(bool,				background	)
		    __field(enum rxrpc_propose_ack_outcome,	outcome		)
			     ),

	    TP_fast_assign(
		    __entry->call	= call;
		    __entry->why	= why;
		    __entry->serial	= serial;
		    __entry->ack_reason	= ack_reason;
		    __entry->immediate	= immediate;
		    __entry->background	= background;
		    __entry->outcome	= outcome;
			   ),

	    TP_printk("c=%p %s %s r=%08x i=%u b=%u%s",
		      __entry->call,
		      rxrpc_propose_ack_traces[__entry->why],
		      rxrpc_ack_names[__entry->ack_reason],
		      __entry->serial,
		      __entry->immediate,
		      __entry->background,
		      rxrpc_propose_ack_outcomes[__entry->outcome])
	    );

TRACE_EVENT(rxrpc_retransmit,
	    TP_PROTO(struct rxrpc_call *call, rxrpc_seq_t seq, u8 annotation,
		     s64 expiry),

	    TP_ARGS(call, seq, annotation, expiry),

	    TP_STRUCT__entry(
		    __field(struct rxrpc_call *,	call		)
		    __field(rxrpc_seq_t,		seq		)
		    __field(u8,				annotation	)
		    __field(s64,			expiry		)
			     ),

	    TP_fast_assign(
		    __entry->call = call;
		    __entry->seq = seq;
		    __entry->annotation = annotation;
		    __entry->expiry = expiry;
			   ),

	    TP_printk("c=%p q=%x a=%02x xp=%lld",
		      __entry->call,
		      __entry->seq,
		      __entry->annotation,
		      __entry->expiry)
	    );

#endif /* _TRACE_RXRPC_H */
#endif /* _TRACE_RXRPC_H */


/* This part must be outside protection */
/* This part must be outside protection */
+41 −4
Original line number Original line Diff line number Diff line
@@ -508,7 +508,9 @@ struct rxrpc_call {
#define RXRPC_TX_ANNO_NAK	2
#define RXRPC_TX_ANNO_NAK	2
#define RXRPC_TX_ANNO_RETRANS	3
#define RXRPC_TX_ANNO_RETRANS	3
#define RXRPC_TX_ANNO_MASK	0x03
#define RXRPC_TX_ANNO_MASK	0x03
#define RXRPC_TX_ANNO_RESENT	0x04
#define RXRPC_TX_ANNO_LAST	0x04
#define RXRPC_TX_ANNO_RESENT	0x08

#define RXRPC_RX_ANNO_JUMBO	0x3f		/* Jumbo subpacket number + 1 if not zero */
#define RXRPC_RX_ANNO_JUMBO	0x3f		/* Jumbo subpacket number + 1 if not zero */
#define RXRPC_RX_ANNO_JLAST	0x40		/* Set if last element of a jumbo packet */
#define RXRPC_RX_ANNO_JLAST	0x40		/* Set if last element of a jumbo packet */
#define RXRPC_RX_ANNO_VERIFIED	0x80		/* Set if verified and decrypted */
#define RXRPC_RX_ANNO_VERIFIED	0x80		/* Set if verified and decrypted */
@@ -621,9 +623,10 @@ extern const char rxrpc_call_traces[rxrpc_call__nr_trace][4];
enum rxrpc_transmit_trace {
enum rxrpc_transmit_trace {
	rxrpc_transmit_wait,
	rxrpc_transmit_wait,
	rxrpc_transmit_queue,
	rxrpc_transmit_queue,
	rxrpc_transmit_queue_reqack,
	rxrpc_transmit_queue_last,
	rxrpc_transmit_queue_last,
	rxrpc_transmit_rotate,
	rxrpc_transmit_rotate,
	rxrpc_transmit_rotate_last,
	rxrpc_transmit_await_reply,
	rxrpc_transmit_end,
	rxrpc_transmit_end,
	rxrpc_transmit__nr_trace
	rxrpc_transmit__nr_trace
};
};
@@ -675,8 +678,39 @@ enum rxrpc_rtt_rx_trace {


extern const char rxrpc_rtt_rx_traces[rxrpc_rtt_rx__nr_trace][5];
extern const char rxrpc_rtt_rx_traces[rxrpc_rtt_rx__nr_trace][5];


enum rxrpc_timer_trace {
	rxrpc_timer_begin,
	rxrpc_timer_expired,
	rxrpc_timer_set_for_ack,
	rxrpc_timer_set_for_resend,
	rxrpc_timer_set_for_send,
	rxrpc_timer__nr_trace
};

extern const char rxrpc_timer_traces[rxrpc_timer__nr_trace][8];

enum rxrpc_propose_ack_trace {
	rxrpc_propose_ack_input_data,
	rxrpc_propose_ack_ping_for_params,
	rxrpc_propose_ack_respond_to_ack,
	rxrpc_propose_ack_respond_to_ping,
	rxrpc_propose_ack_retry_tx,
	rxrpc_propose_ack_terminal_ack,
	rxrpc_propose_ack__nr_trace
};

enum rxrpc_propose_ack_outcome {
	rxrpc_propose_ack_use,
	rxrpc_propose_ack_update,
	rxrpc_propose_ack_subsume,
	rxrpc_propose_ack__nr_outcomes
};

extern const char rxrpc_propose_ack_traces[rxrpc_propose_ack__nr_trace][8];
extern const char *const rxrpc_propose_ack_outcomes[rxrpc_propose_ack__nr_outcomes];

extern const char *const rxrpc_pkts[];
extern const char *const rxrpc_pkts[];
extern const char *rxrpc_acks(u8 reason);
extern const char const rxrpc_ack_names[RXRPC_ACK__INVALID + 1][4];


#include <trace/events/rxrpc.h>
#include <trace/events/rxrpc.h>


@@ -704,7 +738,9 @@ int rxrpc_reject_call(struct rxrpc_sock *);
/*
/*
 * call_event.c
 * call_event.c
 */
 */
void rxrpc_propose_ACK(struct rxrpc_call *, u8, u16, u32, bool, bool);
void rxrpc_set_timer(struct rxrpc_call *, enum rxrpc_timer_trace);
void rxrpc_propose_ACK(struct rxrpc_call *, u8, u16, u32, bool, bool,
		       enum rxrpc_propose_ack_trace);
void rxrpc_process_call(struct work_struct *);
void rxrpc_process_call(struct work_struct *);


/*
/*
@@ -758,6 +794,7 @@ static inline bool __rxrpc_set_call_completion(struct rxrpc_call *call,
		call->error = error;
		call->error = error;
		call->completion = compl,
		call->completion = compl,
		call->state = RXRPC_CALL_COMPLETE;
		call->state = RXRPC_CALL_COMPLETE;
		wake_up(&call->waitq);
		return true;
		return true;
	}
	}
	return false;
	return false;
+35 −22
Original line number Original line Diff line number Diff line
@@ -24,28 +24,32 @@
/*
/*
 * Set the timer
 * Set the timer
 */
 */
static void rxrpc_set_timer(struct rxrpc_call *call)
void rxrpc_set_timer(struct rxrpc_call *call, enum rxrpc_timer_trace why)
{
{
	unsigned long t, now = jiffies;
	unsigned long t, now = jiffies;


	_enter("{%ld,%ld,%ld:%ld}",
	       call->ack_at - now, call->resend_at - now, call->expire_at - now,
	       call->timer.expires - now);

	read_lock_bh(&call->state_lock);
	read_lock_bh(&call->state_lock);


	if (call->state < RXRPC_CALL_COMPLETE) {
	if (call->state < RXRPC_CALL_COMPLETE) {
		t = call->ack_at;
		if (time_before(call->resend_at, t))
			t = call->resend_at;
		if (time_before(call->expire_at, t))
		t = call->expire_at;
		t = call->expire_at;
		if (!timer_pending(&call->timer) ||
		if (time_before_eq(t, now))
		    time_before(t, call->timer.expires)) {
			goto out;
			_debug("set timer %ld", t - now);

		if (time_after(call->resend_at, now) &&
		    time_before(call->resend_at, t))
			t = call->resend_at;

		if (time_after(call->ack_at, now) &&
		    time_before(call->ack_at, t))
			t = call->ack_at;

		if (call->timer.expires != t || !timer_pending(&call->timer)) {
			mod_timer(&call->timer, t);
			mod_timer(&call->timer, t);
			trace_rxrpc_timer(call, why, now);
		}
		}
	}
	}

out:
	read_unlock_bh(&call->state_lock);
	read_unlock_bh(&call->state_lock);
}
}


@@ -54,14 +58,13 @@ static void rxrpc_set_timer(struct rxrpc_call *call)
 */
 */
static void __rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason,
static void __rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason,
				u16 skew, u32 serial, bool immediate,
				u16 skew, u32 serial, bool immediate,
				bool background)
				bool background,
				enum rxrpc_propose_ack_trace why)
{
{
	enum rxrpc_propose_ack_outcome outcome = rxrpc_propose_ack_use;
	unsigned long now, ack_at, expiry = rxrpc_soft_ack_delay;
	unsigned long now, ack_at, expiry = rxrpc_soft_ack_delay;
	s8 prior = rxrpc_ack_priority[ack_reason];
	s8 prior = rxrpc_ack_priority[ack_reason];


	_enter("{%d},%s,%%%x,%u",
	       call->debug_id, rxrpc_acks(ack_reason), serial, immediate);

	/* Update DELAY, IDLE, REQUESTED and PING_RESPONSE ACK serial
	/* Update DELAY, IDLE, REQUESTED and PING_RESPONSE ACK serial
	 * numbers, but we don't alter the timeout.
	 * numbers, but we don't alter the timeout.
	 */
	 */
@@ -70,15 +73,18 @@ static void __rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason,
	       call->ackr_reason, rxrpc_ack_priority[call->ackr_reason]);
	       call->ackr_reason, rxrpc_ack_priority[call->ackr_reason]);
	if (ack_reason == call->ackr_reason) {
	if (ack_reason == call->ackr_reason) {
		if (RXRPC_ACK_UPDATEABLE & (1 << ack_reason)) {
		if (RXRPC_ACK_UPDATEABLE & (1 << ack_reason)) {
			outcome = rxrpc_propose_ack_update;
			call->ackr_serial = serial;
			call->ackr_serial = serial;
			call->ackr_skew = skew;
			call->ackr_skew = skew;
		}
		}
		if (!immediate)
		if (!immediate)
			return;
			goto trace;
	} else if (prior > rxrpc_ack_priority[call->ackr_reason]) {
	} else if (prior > rxrpc_ack_priority[call->ackr_reason]) {
		call->ackr_reason = ack_reason;
		call->ackr_reason = ack_reason;
		call->ackr_serial = serial;
		call->ackr_serial = serial;
		call->ackr_skew = skew;
		call->ackr_skew = skew;
	} else {
		outcome = rxrpc_propose_ack_subsume;
	}
	}


	switch (ack_reason) {
	switch (ack_reason) {
@@ -117,20 +123,25 @@ static void __rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason,
		_debug("deferred ACK %ld < %ld", expiry, call->ack_at - now);
		_debug("deferred ACK %ld < %ld", expiry, call->ack_at - now);
		if (time_before(ack_at, call->ack_at)) {
		if (time_before(ack_at, call->ack_at)) {
			call->ack_at = ack_at;
			call->ack_at = ack_at;
			rxrpc_set_timer(call);
			rxrpc_set_timer(call, rxrpc_timer_set_for_ack);
		}
		}
	}
	}

trace:
	trace_rxrpc_propose_ack(call, why, ack_reason, serial, immediate,
				background, outcome);
}
}


/*
/*
 * propose an ACK be sent, locking the call structure
 * propose an ACK be sent, locking the call structure
 */
 */
void rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason,
void rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason,
		       u16 skew, u32 serial, bool immediate, bool background)
		       u16 skew, u32 serial, bool immediate, bool background,
		       enum rxrpc_propose_ack_trace why)
{
{
	spin_lock_bh(&call->lock);
	spin_lock_bh(&call->lock);
	__rxrpc_propose_ACK(call, ack_reason, skew, serial,
	__rxrpc_propose_ACK(call, ack_reason, skew, serial,
			    immediate, background);
			    immediate, background, why);
	spin_unlock_bh(&call->lock);
	spin_unlock_bh(&call->lock);
}
}


@@ -185,9 +196,11 @@ static void rxrpc_resend(struct rxrpc_call *call)


		/* Okay, we need to retransmit a packet. */
		/* Okay, we need to retransmit a packet. */
		call->rxtx_annotations[ix] = RXRPC_TX_ANNO_RETRANS | annotation;
		call->rxtx_annotations[ix] = RXRPC_TX_ANNO_RETRANS | annotation;
		trace_rxrpc_retransmit(call, seq, annotation | anno_type,
				       ktime_to_ns(ktime_sub(skb->tstamp, max_age)));
	}
	}


	resend_at = ktime_sub(ktime_add_ns(oldest, rxrpc_resend_timeout), now);
	resend_at = ktime_sub(ktime_add_ms(oldest, rxrpc_resend_timeout), now);
	call->resend_at = jiffies + nsecs_to_jiffies(ktime_to_ns(resend_at));
	call->resend_at = jiffies + nsecs_to_jiffies(ktime_to_ns(resend_at));


	/* Now go through the Tx window and perform the retransmissions.  We
	/* Now go through the Tx window and perform the retransmissions.  We
@@ -290,7 +303,7 @@ void rxrpc_process_call(struct work_struct *work)
		goto recheck_state;
		goto recheck_state;
	}
	}


	rxrpc_set_timer(call);
	rxrpc_set_timer(call, rxrpc_timer_set_for_resend);


	/* other events may have been raised since we started checking */
	/* other events may have been raised since we started checking */
	if (call->events && call->state < RXRPC_CALL_COMPLETE) {
	if (call->events && call->state < RXRPC_CALL_COMPLETE) {
+5 −3
Original line number Original line Diff line number Diff line
@@ -76,9 +76,11 @@ static void rxrpc_call_timer_expired(unsigned long _call)


	_enter("%d", call->debug_id);
	_enter("%d", call->debug_id);


	if (call->state < RXRPC_CALL_COMPLETE)
	if (call->state < RXRPC_CALL_COMPLETE) {
		trace_rxrpc_timer(call, rxrpc_timer_expired, jiffies);
		rxrpc_queue_call(call);
		rxrpc_queue_call(call);
	}
	}
}


/*
/*
 * find an extant server call
 * find an extant server call
@@ -199,8 +201,8 @@ static void rxrpc_start_call_timer(struct rxrpc_call *call)
	call->expire_at = expire_at;
	call->expire_at = expire_at;
	call->ack_at = expire_at;
	call->ack_at = expire_at;
	call->resend_at = expire_at;
	call->resend_at = expire_at;
	call->timer.expires = expire_at;
	call->timer.expires = expire_at + 1;
	add_timer(&call->timer);
	rxrpc_set_timer(call, rxrpc_timer_begin);
}
}


/*
/*
Loading