1332 lines · 37.8 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* Processing of received RxRPC packets * * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include "ar-internal.h" /* Override priority when generating ACKs for received DATA */static const u8 rxrpc_ack_priority[RXRPC_ACK__INVALID] = {	[RXRPC_ACK_IDLE]		= 1,	[RXRPC_ACK_DELAY]		= 2,	[RXRPC_ACK_REQUESTED]		= 3,	[RXRPC_ACK_DUPLICATE]		= 4,	[RXRPC_ACK_EXCEEDS_WINDOW]	= 5,	[RXRPC_ACK_NOSPACE]		= 6,	[RXRPC_ACK_OUT_OF_SEQUENCE]	= 7,}; static void rxrpc_proto_abort(struct rxrpc_call *call, rxrpc_seq_t seq,			      enum rxrpc_abort_reason why){	rxrpc_abort_call(call, seq, RX_PROTOCOL_ERROR, -EBADMSG, why);} /* * Do TCP-style congestion management [RFC5681]. */static void rxrpc_congestion_management(struct rxrpc_call *call,					struct rxrpc_ack_summary *summary){	summary->change = rxrpc_cong_no_change;	summary->in_flight = rxrpc_tx_in_flight(call); 	if (test_and_clear_bit(RXRPC_CALL_RETRANS_TIMEOUT, &call->flags)) {		summary->retrans_timeo = true;		call->cong_ssthresh = umax(summary->in_flight / 2, 2);		call->cong_cwnd = 1;		if (call->cong_cwnd >= call->cong_ssthresh &&		    call->cong_ca_state == RXRPC_CA_SLOW_START) {			call->cong_ca_state = RXRPC_CA_CONGEST_AVOIDANCE;			call->cong_tstamp = call->acks_latest_ts;			call->cong_cumul_acks = 0;		}	} 	call->cong_cumul_acks += summary->nr_new_sacks;	call->cong_cumul_acks += summary->nr_new_hacks;	if (call->cong_cumul_acks > 255)		call->cong_cumul_acks = 255; 	switch (call->cong_ca_state) {	case RXRPC_CA_SLOW_START:		if (call->acks_nr_snacks > 0)			goto packet_loss_detected;		if (call->cong_cumul_acks > 0)			call->cong_cwnd += 1;		if (call->cong_cwnd >= call->cong_ssthresh) {			call->cong_ca_state = RXRPC_CA_CONGEST_AVOIDANCE;			call->cong_tstamp = call->acks_latest_ts;		}		goto out; 	case RXRPC_CA_CONGEST_AVOIDANCE:		if (call->acks_nr_snacks > 0)			goto packet_loss_detected; 		/* We analyse the number of packets that get ACK'd per RTT		 * period and increase the window if we managed to fill it.		 */		if (call->rtt_count == 0)			goto out;		if (ktime_before(call->acks_latest_ts,				 ktime_add_us(call->cong_tstamp,					      call->srtt_us >> 3)))			goto out_no_clear_ca;		summary->change = rxrpc_cong_rtt_window_end;		call->cong_tstamp = call->acks_latest_ts;		if (call->cong_cumul_acks >= call->cong_cwnd)			call->cong_cwnd++;		goto out; 	case RXRPC_CA_PACKET_LOSS:		if (call->acks_nr_snacks == 0)			goto resume_normality; 		if (summary->new_low_snack) {			summary->change = rxrpc_cong_new_low_nack;			call->cong_dup_acks = 1;			if (call->cong_extra > 1)				call->cong_extra = 1;			goto send_extra_data;		} 		call->cong_dup_acks++;		if (call->cong_dup_acks < 3)			goto send_extra_data; 		summary->change = rxrpc_cong_begin_retransmission;		call->cong_ca_state = RXRPC_CA_FAST_RETRANSMIT;		call->cong_ssthresh = umax(summary->in_flight / 2, 2);		call->cong_cwnd = call->cong_ssthresh + 3;		call->cong_extra = 0;		call->cong_dup_acks = 0;		summary->need_retransmit = true;		summary->in_fast_or_rto_recovery = true;		goto out; 	case RXRPC_CA_FAST_RETRANSMIT:		rxrpc_tlp_init(call);		summary->in_fast_or_rto_recovery = true;		if (!summary->new_low_snack) {			if (summary->nr_new_sacks == 0)				call->cong_cwnd += 1;			call->cong_dup_acks++;			if (call->cong_dup_acks == 2) {				summary->change = rxrpc_cong_retransmit_again;				call->cong_dup_acks = 0;				summary->need_retransmit = true;			}		} else {			summary->change = rxrpc_cong_progress;			call->cong_cwnd = call->cong_ssthresh;			if (call->acks_nr_snacks == 0) {				summary->exiting_fast_or_rto_recovery = true;				goto resume_normality;			}		}		goto out; 	default:		BUG();		goto out;	} resume_normality:	summary->change = rxrpc_cong_cleared_nacks;	call->cong_dup_acks = 0;	call->cong_extra = 0;	call->cong_tstamp = call->acks_latest_ts;	if (call->cong_cwnd < call->cong_ssthresh)		call->cong_ca_state = RXRPC_CA_SLOW_START;	else		call->cong_ca_state = RXRPC_CA_CONGEST_AVOIDANCE;out:	call->cong_cumul_acks = 0;out_no_clear_ca:	if (call->cong_cwnd >= RXRPC_TX_MAX_WINDOW)		call->cong_cwnd = RXRPC_TX_MAX_WINDOW;	trace_rxrpc_congest(call, summary);	return; packet_loss_detected:	summary->change = rxrpc_cong_saw_nack;	call->cong_ca_state = RXRPC_CA_PACKET_LOSS;	call->cong_dup_acks = 0;	goto send_extra_data; send_extra_data:	/* Send some previously unsent DATA if we have some to advance the ACK	 * state.	 */	if (test_bit(RXRPC_CALL_TX_LAST, &call->flags) ||	    call->acks_nr_sacks != call->tx_top - call->tx_bottom) {		call->cong_extra++;		wake_up(&call->waitq);	}	goto out_no_clear_ca;} /* * Degrade the congestion window if we haven't transmitted a packet for >1RTT. */void rxrpc_congestion_degrade(struct rxrpc_call *call){	ktime_t rtt, now, time_since; 	if (call->cong_ca_state != RXRPC_CA_SLOW_START &&	    call->cong_ca_state != RXRPC_CA_CONGEST_AVOIDANCE)		return;	if (__rxrpc_call_state(call) == RXRPC_CALL_CLIENT_AWAIT_ACK ||	    __rxrpc_call_state(call) == RXRPC_CALL_CLIENT_AWAIT_REPLY)		return; 	rtt = ns_to_ktime(call->srtt_us * (NSEC_PER_USEC / 8));	now = ktime_get_real();	time_since = ktime_sub(now, call->tx_last_sent);	if (ktime_before(time_since, rtt))		return; 	trace_rxrpc_reset_cwnd(call, time_since, rtt);	rxrpc_inc_stat(call->rxnet, stat_tx_data_cwnd_reset);	call->tx_last_sent = now;	call->cong_ca_state = RXRPC_CA_SLOW_START;	call->cong_ssthresh = umax(call->cong_ssthresh, call->cong_cwnd * 3 / 4);	call->cong_cwnd = umax(call->cong_cwnd / 2, RXRPC_MIN_CWND);} /* * Add an RTT sample derived from an ACK'd DATA packet. */static void rxrpc_add_data_rtt_sample(struct rxrpc_call *call,				      struct rxrpc_ack_summary *summary,				      struct rxrpc_txqueue *tq,				      int ix){	ktime_t xmit_ts = ktime_add_us(tq->xmit_ts_base, tq->segment_xmit_ts[ix]); 	rxrpc_call_add_rtt(call, rxrpc_rtt_rx_data_ack, -1,			   summary->acked_serial, summary->ack_serial,			   xmit_ts, call->acks_latest_ts);	__clear_bit(ix, &tq->rtt_samples); /* Prevent repeat RTT sample */} /* * Apply a hard ACK by advancing the Tx window. */static bool rxrpc_rotate_tx_window(struct rxrpc_call *call, rxrpc_seq_t to,				   struct rxrpc_ack_summary *summary){	struct rxrpc_txqueue *tq = call->tx_queue;	rxrpc_seq_t seq = call->tx_bottom + 1;	bool rot_last = false, trace = false; 	_enter("%x,%x", call->tx_bottom, to); 	trace_rxrpc_tx_rotate(call, seq, to);	trace_rxrpc_tq(call, tq, seq, rxrpc_tq_rotate); 	if (call->acks_lowest_nak == call->tx_bottom) {		call->acks_lowest_nak = to;	} else if (after(to, call->acks_lowest_nak)) {		summary->new_low_snack = true;		call->acks_lowest_nak = to;	} 	if (after(seq, to))		return false; 	/* We may have a left over fully-consumed buffer at the front that we	 * couldn't drop before (rotate_and_keep below).	 */	if (seq == call->tx_qbase + RXRPC_NR_TXQUEUE) {		call->tx_qbase += RXRPC_NR_TXQUEUE;		call->tx_queue = tq->next;		trace_rxrpc_tq(call, tq, seq, rxrpc_tq_rotate_and_free);		kfree(tq);		tq = call->tx_queue;	} 	while (before_eq(seq, to)) {		unsigned int ix = seq - call->tx_qbase; 		_debug("tq=%x seq=%x i=%d f=%x", tq->qbase, seq, ix, tq->bufs[ix]->flags);		if (tq->bufs[ix]->flags & RXRPC_LAST_PACKET) {			set_bit(RXRPC_CALL_TX_LAST, &call->flags);			rot_last = true;		} 		if (summary->acked_serial == tq->segment_serial[ix] &&		    test_bit(ix, &tq->rtt_samples))			rxrpc_add_data_rtt_sample(call, summary, tq, ix); 		if (ix == tq->nr_reported_acks) {			/* Packet directly hard ACK'd. */			tq->nr_reported_acks++;			rxrpc_input_rack_one(call, summary, tq, ix);			if (seq == call->tlp_seq)				summary->tlp_probe_acked = true;			summary->nr_new_hacks++;			__set_bit(ix, &tq->segment_acked);			trace_rxrpc_rotate(call, tq, summary, seq, rxrpc_rotate_trace_hack);		} else if (test_bit(ix, &tq->segment_acked)) {			/* Soft ACK -> hard ACK. */			call->acks_nr_sacks--;			trace_rxrpc_rotate(call, tq, summary, seq, rxrpc_rotate_trace_sack);		} else {			/* Soft NAK -> hard ACK. */			call->acks_nr_snacks--;			rxrpc_input_rack_one(call, summary, tq, ix);			if (seq == call->tlp_seq)				summary->tlp_probe_acked = true;			summary->nr_new_hacks++;			__set_bit(ix, &tq->segment_acked);			trace_rxrpc_rotate(call, tq, summary, seq, rxrpc_rotate_trace_snak);		} 		call->tx_nr_sent--;		if (__test_and_clear_bit(ix, &tq->segment_lost))			call->tx_nr_lost--;		if (__test_and_clear_bit(ix, &tq->segment_retransmitted))			call->tx_nr_resent--;		__clear_bit(ix, &tq->ever_retransmitted); 		rxrpc_put_txbuf(tq->bufs[ix], rxrpc_txbuf_put_rotated);		tq->bufs[ix] = NULL; 		WRITE_ONCE(call->tx_bottom, seq);		trace_rxrpc_txqueue(call, (rot_last ?					   rxrpc_txqueue_rotate_last :					   rxrpc_txqueue_rotate)); 		seq++;		trace = true;		if (!(seq & RXRPC_TXQ_MASK)) {			trace_rxrpc_rack_update(call, summary);			trace = false;			prefetch(tq->next);			if (tq != call->tx_qtail) {				call->tx_qbase += RXRPC_NR_TXQUEUE;				call->tx_queue = tq->next;				trace_rxrpc_tq(call, tq, seq, rxrpc_tq_rotate_and_free);				kfree(tq);				tq = call->tx_queue;			} else {				trace_rxrpc_tq(call, tq, seq, rxrpc_tq_rotate_and_keep);				tq = NULL;				break;			}		}	} 	if (trace)		trace_rxrpc_rack_update(call, summary); 	if (rot_last) {		set_bit(RXRPC_CALL_TX_ALL_ACKED, &call->flags);		if (tq) {			trace_rxrpc_tq(call, tq, seq, rxrpc_tq_rotate_and_free);			kfree(tq);			call->tx_queue = NULL;		}	} 	_debug("%x,%x,%x,%d", to, call->tx_bottom, call->tx_top, rot_last); 	wake_up(&call->waitq);	return rot_last;} /* * End the transmission phase of a call. * * This occurs when we get an ACKALL packet, the first DATA packet of a reply, * or a final ACK packet. */static void rxrpc_end_tx_phase(struct rxrpc_call *call, bool reply_begun,			       enum rxrpc_abort_reason abort_why){	ASSERT(test_bit(RXRPC_CALL_TX_LAST, &call->flags)); 	call->rack_timer_mode = RXRPC_CALL_RACKTIMER_OFF;	call->rack_timo_at = KTIME_MAX;	trace_rxrpc_rack_timer(call, 0, false);	trace_rxrpc_timer_can(call, rxrpc_timer_trace_rack_off + call->rack_timer_mode); 	switch (__rxrpc_call_state(call)) {	case RXRPC_CALL_CLIENT_SEND_REQUEST:	case RXRPC_CALL_CLIENT_AWAIT_ACK:	case RXRPC_CALL_CLIENT_AWAIT_REPLY:		if (reply_begun) {			rxrpc_set_call_state(call, RXRPC_CALL_CLIENT_RECV_REPLY);			trace_rxrpc_txqueue(call, rxrpc_txqueue_end);			break;		} 		rxrpc_set_call_state(call, RXRPC_CALL_CLIENT_AWAIT_REPLY);		trace_rxrpc_txqueue(call, rxrpc_txqueue_await_reply);		break; 	case RXRPC_CALL_SERVER_AWAIT_ACK:		rxrpc_call_completed(call);		trace_rxrpc_txqueue(call, rxrpc_txqueue_end);		break; 	default:		kdebug("end_tx %s", rxrpc_call_states[__rxrpc_call_state(call)]);		rxrpc_proto_abort(call, call->tx_top, abort_why);		break;	}} /* * Begin the reply reception phase of a call. */static bool rxrpc_receiving_reply(struct rxrpc_call *call){	struct rxrpc_ack_summary summary = { 0 };	rxrpc_seq_t top = READ_ONCE(call->tx_top); 	if (call->ackr_reason) {		call->delay_ack_at = KTIME_MAX;		trace_rxrpc_timer_can(call, rxrpc_timer_trace_delayed_ack);	} 	/* Deal with an apparent reply coming in before we've got the request	 * queued or transmitted.	 */	if (!test_bit(RXRPC_CALL_EXPOSED, &call->flags)) {		rxrpc_proto_abort(call, top, rxrpc_eproto_early_reply);		return false;	} 	if (!test_bit(RXRPC_CALL_TX_LAST, &call->flags)) {		if (!rxrpc_rotate_tx_window(call, top, &summary)) {			rxrpc_proto_abort(call, top, rxrpc_eproto_early_reply);			return false;		}	} 	rxrpc_end_tx_phase(call, true, rxrpc_eproto_unexpected_reply);	return true;} /* * End the packet reception phase. */static void rxrpc_end_rx_phase(struct rxrpc_call *call, rxrpc_serial_t serial){	rxrpc_seq_t whigh = READ_ONCE(call->rx_highest_seq); 	_enter("%d,%s", call->debug_id, rxrpc_call_states[__rxrpc_call_state(call)]); 	trace_rxrpc_receive(call, rxrpc_receive_end, 0, whigh); 	switch (__rxrpc_call_state(call)) {	case RXRPC_CALL_CLIENT_RECV_REPLY:		rxrpc_propose_delay_ACK(call, serial, rxrpc_propose_ack_terminal_ack);		rxrpc_call_completed(call);		break; 	case RXRPC_CALL_SERVER_RECV_REQUEST:		rxrpc_set_call_state(call, RXRPC_CALL_SERVER_ACK_REQUEST);		call->expect_req_by = KTIME_MAX;		rxrpc_propose_delay_ACK(call, serial, rxrpc_propose_ack_processing_op);		break; 	default:		break;	}} static void rxrpc_input_update_ack_window(struct rxrpc_call *call,					  rxrpc_seq_t window, rxrpc_seq_t wtop){	call->ackr_window = window;	call->ackr_wtop = wtop;} /* * Push a DATA packet onto the Rx queue. */static void rxrpc_input_queue_data(struct rxrpc_call *call, struct sk_buff *skb,				   rxrpc_seq_t window, rxrpc_seq_t wtop,				   enum rxrpc_receive_trace why){	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);	bool last = sp->hdr.flags & RXRPC_LAST_PACKET; 	spin_lock_irq(&call->recvmsg_queue.lock); 	__skb_queue_tail(&call->recvmsg_queue, skb);	rxrpc_input_update_ack_window(call, window, wtop);	trace_rxrpc_receive(call, last ? why + 1 : why, sp->hdr.serial, sp->hdr.seq);	if (last)		/* Change the state inside the lock so that recvmsg syncs		 * correctly with it and using sendmsg() to send a reply		 * doesn't race.		 */		rxrpc_end_rx_phase(call, sp->hdr.serial); 	spin_unlock_irq(&call->recvmsg_queue.lock);} /* * Process a DATA packet. */static void rxrpc_input_data_one(struct rxrpc_call *call, struct sk_buff *skb,				 bool *_notify, rxrpc_serial_t *_ack_serial, int *_ack_reason){	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);	struct sk_buff *oos;	rxrpc_serial_t serial = sp->hdr.serial;	unsigned int sack = call->ackr_sack_base;	rxrpc_seq_t window = call->ackr_window;	rxrpc_seq_t wtop = call->ackr_wtop;	rxrpc_seq_t wlimit = window + call->rx_winsize - 1;	rxrpc_seq_t seq = sp->hdr.seq;	bool last = sp->hdr.flags & RXRPC_LAST_PACKET;	int ack_reason = -1; 	rxrpc_inc_stat(call->rxnet, stat_rx_data);	if (sp->hdr.flags & RXRPC_REQUEST_ACK)		rxrpc_inc_stat(call->rxnet, stat_rx_data_reqack);	if (sp->hdr.flags & RXRPC_JUMBO_PACKET)		rxrpc_inc_stat(call->rxnet, stat_rx_data_jumbo); 

Lines 1–500 of 1332.