From: Greg Kroah-Hartman Date: Fri, 7 Aug 2026 13:07:26 +0000 (+0200) Subject: 6.12-stable patches X-Git-Tag: v6.6.151~18 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6ef63f56430b2d10405c0dc2c3feffb3c4b5d139;p=thirdparty%2Fkernel%2Fstable-queue.git 6.12-stable patches added patches: can-isotp-fix-timer-drain-order-wakeup-handling-and-tx_gen-ordering.patch can-use-skb-hash-instead-of-private-variable-in-headroom.patch rxrpc-adjust-the-rxrpc_rtt_rx-tracepoint.patch rxrpc-fix-irq-disabled-in-local_bh_enable.patch rxrpc-fix-the-calculation-and-use-of-rto.patch rxrpc-generate-rtt_min.patch rxrpc-manage-rtt-per-call-rather-than-per-peer.patch --- diff --git a/queue-6.12/can-isotp-fix-timer-drain-order-wakeup-handling-and-tx_gen-ordering.patch b/queue-6.12/can-isotp-fix-timer-drain-order-wakeup-handling-and-tx_gen-ordering.patch new file mode 100644 index 0000000000..a29747ab55 --- /dev/null +++ b/queue-6.12/can-isotp-fix-timer-drain-order-wakeup-handling-and-tx_gen-ordering.patch @@ -0,0 +1,746 @@ +From stable+bounces-297170-greg=kroah.com@vger.kernel.org Fri Aug 7 09:50:59 2026 +From: Oliver Hartkopp +Date: Fri, 7 Aug 2026 09:50:25 +0200 +Subject: can: isotp: fix timer drain order, wakeup handling and tx_gen ordering +To: stable@vger.kernel.org +Cc: Oliver Hartkopp , Marc Kleine-Budde +Message-ID: <20260807075025.104370-2-socketcan@hartkopp.net> + +From: Oliver Hartkopp + +commit 050f010f920da17c1044a4f174766ad553e770b6 upstream. + +This patch is a follow-up to commit cf070fe33bfb ("can: isotp: serialize +TX state transitions under so->rx_lock") which addresses following +sashiko-bot findings: + +- isotp_sendmsg(): drain so->txfrtimer first so a stale callback can't + re-arm echotimer after the claim + +- isotp_release(): wake so->wait after forcing ISOTP_SHUTDOWN so a + sleeping sendmsg() claim isn't stranded + +- isotp_sendmsg(): have both wait_event_interruptible() calls in + isotp_sendmsg() also wake on ISOTP_SHUTDOWN and do not return claim to + IDLE to avoid corrupting a concurrent isotp_release() process. + +- isotp_sendmsg(): handle potential claim of a new transfer when + the wait_event_interruptible() call returns in CAN_ISOTP_WAIT_TX_DONE + mode. Don't touch timers and states of the new transfer if a new thread + incremented so->tx_gen before getting the lock at err_event_drop. + +- isotp_sendmsg(): handle a stuck can_send() and omit timer and state + changes if a new transfer was claimed. wait_tx_done() returns the error + recorded in so->tx_result[], tagged with the caller's own generation. + +- isotp_tx_timeout(): on a claimed timeout, record the ECOMM error for + the timed-out transfer's own generation in so->tx_result[]; sk->sk_err + is raised unconditionally, same as every other error path here. + +- isotp_tx_gen_done()/isotp_tx_timeout(): always read tx.state (acquire) + before tx_gen - the reverse order let a weakly ordered CPU pair a fresh + tx.state with a stale tx_gen/tx_result slot. + +- isotp_sendmsg(): wait_tx_done: drain sk_err via sock_error() once we + have read the result from so->tx_result[], so an already-reported error + doesn't stay latched for a later poll()/SO_ERROR. + +Also align the remaining lock-free so->tx.state/rx.state/cfecho accesses +and use skb->hash as unique loopback echo frame indicator. + +Fixes: cf070fe33bfb ("can: isotp: serialize TX state transitions under so->rx_lock") +Signed-off-by: Oliver Hartkopp +Link: https://patch.msgid.link/20260724181525.43556-1-socketcan@hartkopp.net +Cc: stable@kernel.org +Signed-off-by: Marc Kleine-Budde +Signed-off-by: Oliver Hartkopp +Signed-off-by: Greg Kroah-Hartman +--- + net/can/isotp.c | 317 ++++++++++++++++++++++++++++++++++++++++---------------- + 1 file changed, 230 insertions(+), 87 deletions(-) + +--- a/net/can/isotp.c ++++ b/net/can/isotp.c +@@ -126,6 +126,15 @@ MODULE_PARM_DESC(max_pdu_size, "maximum + #define ISOTP_FC_TIMEOUT 1 /* 1 sec */ + #define ISOTP_ECHO_TIMEOUT 2 /* 2 secs */ + ++/* so->tx_result[so->tx_gen % ISOTP_TX_RESULT_SLOTS] holds the packed value ++ * (err << ISOTP_TX_RESULT_GEN_BITS | gen) for each tx generation slot, so it ++ * can be handled with a single READ_ONCE()/WRITE_ONCE() access. ++ */ ++#define ISOTP_TX_RESULT_SLOTS 4 ++#define ISOTP_TX_RESULT_GEN_BITS 24 ++#define ISOTP_TX_RESULT_GEN_MASK ((1U << ISOTP_TX_RESULT_GEN_BITS) - 1) ++#define ISOTP_TX_RESULT_ERR_MASK 0xFF ++ + enum { + ISOTP_IDLE = 0, + ISOTP_WAIT_FIRST_FC, +@@ -164,7 +173,8 @@ struct isotp_sock { + u32 force_tx_stmin; + u32 force_rx_stmin; + u32 cfecho; /* consecutive frame echo tag */ +- u32 tx_gen; /* generation, bumped per new tx transfer */ ++ u32 tx_gen; /* transfer generation, increased per new tx transfer */ ++ u32 tx_result[ISOTP_TX_RESULT_SLOTS]; /* per-generation result slots */ + struct tpcon rx, tx; + struct list_head notifier; + wait_queue_head_t wait; +@@ -175,6 +185,65 @@ static LIST_HEAD(isotp_notifier_list); + static DEFINE_SPINLOCK(isotp_notifier_lock); + static struct isotp_sock *isotp_busy_notifier; + ++/* increase (24 bit) tx generation value */ ++static u32 isotp_inc_tx_gen(u32 gen) ++{ ++ return (gen + 1) & ISOTP_TX_RESULT_GEN_MASK; ++} ++ ++/* store 8 bit error and 24 bit tx generation values in packed u32 element */ ++static u32 isotp_pack_tx_result(u32 gen, int err) ++{ ++ return gen | ((u32)err << ISOTP_TX_RESULT_GEN_BITS); ++} ++ ++/* get the 24 bit tx generation value from the tx result */ ++static u32 isotp_get_tx_gen(u32 gen_err) ++{ ++ return gen_err & ISOTP_TX_RESULT_GEN_MASK; ++} ++ ++/* get the 8 bit error value from the tx result */ ++static u32 isotp_get_tx_err(u32 gen_err) ++{ ++ return (gen_err >> ISOTP_TX_RESULT_GEN_BITS) & ISOTP_TX_RESULT_ERR_MASK; ++} ++ ++/* store transfer result in per-generation%4 so->tx_result[] slot */ ++static void isotp_set_tx_result(struct isotp_sock *so, u32 gen, int err) ++{ ++ WRITE_ONCE(so->tx_result[gen % ISOTP_TX_RESULT_SLOTS], ++ isotp_pack_tx_result(gen, err)); ++} ++ ++/* fetch the result recorded for 'gen', as a (negative) errno (0 for success) */ ++static int isotp_get_tx_result(struct isotp_sock *so, u32 gen) ++{ ++ u32 result = READ_ONCE(so->tx_result[gen % ISOTP_TX_RESULT_SLOTS]); ++ ++ if (isotp_get_tx_gen(result) != gen) { ++ pr_notice_once("can-isotp: tx_result[] slot reused before read\n"); ++ ++ /* report failure rather than risk a false success */ ++ return -ECOMM; ++ } ++ ++ return -(isotp_get_tx_err(result)); ++} ++ ++/* true if done, shut down or superseded ('gen' is no longer the active ++ * transfer). Reads tx.state first (acquire) so tx_gen/tx_result reads ++ * below see at least what that state write published (common sequence). ++ */ ++static bool isotp_tx_gen_done(struct isotp_sock *so, u32 gen) ++{ ++ /* read tx.state first for the common sequence */ ++ u32 state = smp_load_acquire(&so->tx.state); ++ ++ return state == ISOTP_IDLE || state == ISOTP_SHUTDOWN || ++ READ_ONCE(so->tx_gen) != gen; ++} ++ + static inline struct isotp_sock *isotp_sk(const struct sock *sk) + { + return (struct isotp_sock *)sk; +@@ -197,7 +266,7 @@ static enum hrtimer_restart isotp_rx_tim + rxtimer); + struct sock *sk = &so->sk; + +- if (so->rx.state == ISOTP_WAIT_DATA) { ++ if (READ_ONCE(so->rx.state) == ISOTP_WAIT_DATA) { + /* we did not get new data frames in time */ + + /* report 'connection timed out' */ +@@ -206,7 +275,7 @@ static enum hrtimer_restart isotp_rx_tim + sk_error_report(sk); + + /* reset rx state */ +- so->rx.state = ISOTP_IDLE; ++ WRITE_ONCE(so->rx.state, ISOTP_IDLE); + } + + return HRTIMER_NORESTART; +@@ -363,20 +432,19 @@ static void isotp_send_cframe(struct iso + static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae) + { + struct sock *sk = &so->sk; ++ int tx_err = EBADMSG; /* default for unknown FC status */ + +- if (so->tx.state != ISOTP_WAIT_FC && +- so->tx.state != ISOTP_WAIT_FIRST_FC) ++ if (READ_ONCE(so->tx.state) != ISOTP_WAIT_FC && ++ READ_ONCE(so->tx.state) != ISOTP_WAIT_FIRST_FC) + return 0; + + hrtimer_cancel(&so->txtimer); + + /* isotp_tx_timeout() may have given up on this job while +- * hrtimer_cancel() above waited for it to finish; so->rx_lock +- * (held by our caller isotp_rcv()) rules out a concurrent claim, +- * so a plain recheck is enough here. ++ * hrtimer_cancel() above waited for it to finish => recheck + */ +- if (so->tx.state != ISOTP_WAIT_FC && +- so->tx.state != ISOTP_WAIT_FIRST_FC) ++ if (READ_ONCE(so->tx.state) != ISOTP_WAIT_FC && ++ READ_ONCE(so->tx.state) != ISOTP_WAIT_FIRST_FC) + return 1; + + if ((cf->len < ae + FC_CONTENT_SZ) || +@@ -387,13 +455,15 @@ static int isotp_rcv_fc(struct isotp_soc + if (!sock_flag(sk, SOCK_DEAD)) + sk_error_report(sk); + +- so->tx.state = ISOTP_IDLE; ++ isotp_set_tx_result(so, so->tx_gen, EBADMSG); ++ /* set to IDLE after publishing tx_result */ ++ smp_store_release(&so->tx.state, ISOTP_IDLE); + wake_up_interruptible(&so->wait); + return 1; + } + + /* get static/dynamic communication params from first/every FC frame */ +- if (so->tx.state == ISOTP_WAIT_FIRST_FC || ++ if (READ_ONCE(so->tx.state) == ISOTP_WAIT_FIRST_FC || + so->opt.flags & CAN_ISOTP_DYN_FC_PARMS) { + so->txfc.bs = cf->data[ae + 1]; + so->txfc.stmin = cf->data[ae + 2]; +@@ -417,13 +487,13 @@ static int isotp_rcv_fc(struct isotp_soc + so->tx_gap = ktime_add_ns(so->tx_gap, + (so->txfc.stmin - 0xF0) + * 100000); +- so->tx.state = ISOTP_WAIT_FC; ++ WRITE_ONCE(so->tx.state, ISOTP_WAIT_FC); + } + + switch (cf->data[ae] & 0x0F) { + case ISOTP_FC_CTS: + so->tx.bs = 0; +- so->tx.state = ISOTP_SENDING; ++ WRITE_ONCE(so->tx.state, ISOTP_SENDING); + /* send CF frame and enable echo timeout handling */ + hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0), + HRTIMER_MODE_REL_SOFT); +@@ -438,14 +508,19 @@ static int isotp_rcv_fc(struct isotp_soc + + case ISOTP_FC_OVFLW: + /* overflow on receiver side - report 'message too long' */ +- sk->sk_err = EMSGSIZE; +- if (!sock_flag(sk, SOCK_DEAD)) +- sk_error_report(sk); ++ tx_err = EMSGSIZE; + fallthrough; + + default: +- /* stop this tx job */ +- so->tx.state = ISOTP_IDLE; ++ /* reserved/unknown flow status (tx_err defaults to EBADMSG) */ ++ ++ sk->sk_err = tx_err; ++ if (!sock_flag(sk, SOCK_DEAD)) ++ sk_error_report(sk); ++ ++ isotp_set_tx_result(so, so->tx_gen, tx_err); ++ /* set to IDLE after publishing tx_result */ ++ smp_store_release(&so->tx.state, ISOTP_IDLE); + wake_up_interruptible(&so->wait); + } + return 0; +@@ -458,7 +533,7 @@ static int isotp_rcv_sf(struct sock *sk, + struct sk_buff *nskb; + + hrtimer_cancel(&so->rxtimer); +- so->rx.state = ISOTP_IDLE; ++ WRITE_ONCE(so->rx.state, ISOTP_IDLE); + + if (!len || len > cf->len - pcilen) + return 1; +@@ -492,7 +567,7 @@ static int isotp_rcv_ff(struct sock *sk, + int ff_pci_sz; + + hrtimer_cancel(&so->rxtimer); +- so->rx.state = ISOTP_IDLE; ++ WRITE_ONCE(so->rx.state, ISOTP_IDLE); + + /* get the used sender LL_DL from the (first) CAN frame data length */ + so->rx.ll_dl = padlen(cf->len); +@@ -546,7 +621,7 @@ static int isotp_rcv_ff(struct sock *sk, + + /* initial setup for this pdu reception */ + so->rx.sn = 1; +- so->rx.state = ISOTP_WAIT_DATA; ++ WRITE_ONCE(so->rx.state, ISOTP_WAIT_DATA); + + /* no creation of flow control frames */ + if (so->opt.flags & CAN_ISOTP_LISTEN_MODE) +@@ -564,7 +639,7 @@ static int isotp_rcv_cf(struct sock *sk, + struct sk_buff *nskb; + int i; + +- if (so->rx.state != ISOTP_WAIT_DATA) ++ if (READ_ONCE(so->rx.state) != ISOTP_WAIT_DATA) + return 0; + + /* drop if timestamp gap is less than force_rx_stmin nano secs */ +@@ -579,11 +654,9 @@ static int isotp_rcv_cf(struct sock *sk, + hrtimer_cancel(&so->rxtimer); + + /* isotp_rx_timer_handler() may have raced us for so->rx.state +- * while hrtimer_cancel() above waited for it to finish, already +- * reporting ETIMEDOUT and resetting the reception; don't process +- * this CF into a reassembly that has already been given up on. ++ * while hrtimer_cancel() above waited for it to finish => recheck + */ +- if (so->rx.state != ISOTP_WAIT_DATA) ++ if (READ_ONCE(so->rx.state) != ISOTP_WAIT_DATA) + return 1; + + /* CFs are never longer than the FF */ +@@ -604,7 +677,7 @@ static int isotp_rcv_cf(struct sock *sk, + sk_error_report(sk); + + /* reset rx state */ +- so->rx.state = ISOTP_IDLE; ++ WRITE_ONCE(so->rx.state, ISOTP_IDLE); + return 1; + } + so->rx.sn++; +@@ -618,7 +691,7 @@ static int isotp_rcv_cf(struct sock *sk, + + if (so->rx.idx >= so->rx.len) { + /* we are done */ +- so->rx.state = ISOTP_IDLE; ++ WRITE_ONCE(so->rx.state, ISOTP_IDLE); + + if ((so->opt.flags & ISOTP_CHECK_PADDING) && + check_pad(so, cf, i + 1, so->opt.rxpad_content)) { +@@ -689,8 +762,10 @@ static void isotp_rcv(struct sk_buff *sk + + if (so->opt.flags & CAN_ISOTP_HALF_DUPLEX) { + /* check rx/tx path half duplex expectations */ +- if ((so->tx.state != ISOTP_IDLE && n_pci_type != N_PCI_FC) || +- (so->rx.state != ISOTP_IDLE && n_pci_type == N_PCI_FC)) ++ if ((READ_ONCE(so->tx.state) != ISOTP_IDLE && ++ n_pci_type != N_PCI_FC) || ++ (READ_ONCE(so->rx.state) != ISOTP_IDLE && ++ n_pci_type == N_PCI_FC)) + goto out_unlock; + } + +@@ -784,6 +859,7 @@ static void isotp_send_cframe(struct iso + struct canfd_frame *cf; + int can_send_ret; + int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0; ++ u32 old_cfecho; + + dev = dev_get_by_index(sock_net(sk), so->ifindex); + if (!dev) +@@ -798,6 +874,9 @@ static void isotp_send_cframe(struct iso + can_skb_reserve(skb); + can_skb_prv(skb)->ifindex = dev->ifindex; + ++ /* set uid in tx skb to identify CF echo frames */ ++ can_set_skb_uid(skb); ++ + cf = (struct canfd_frame *)skb->data; + skb_put_zero(skb, so->ll.mtu); + +@@ -814,12 +893,15 @@ static void isotp_send_cframe(struct iso + skb->dev = dev; + can_skb_set_owner(skb, sk); + +- /* cfecho should have been zero'ed by init/isotp_rcv_echo() */ +- if (so->cfecho) +- pr_notice_once("can-isotp: cfecho is %08X != 0\n", so->cfecho); ++ /* zero'ed by init/isotp_rcv_echo(); reached lock-free via ++ * isotp_txfr_timer_handler() too, so use READ_ONCE()/WRITE_ONCE() ++ */ ++ old_cfecho = READ_ONCE(so->cfecho); ++ if (old_cfecho) ++ pr_notice_once("can-isotp: cfecho is %08X != 0\n", old_cfecho); + + /* set consecutive frame echo tag */ +- so->cfecho = *(u32 *)cf->data; ++ WRITE_ONCE(so->cfecho, skb->hash); + + /* send frame with local echo enabled */ + can_send_ret = can_send(skb, 1); +@@ -871,7 +953,6 @@ static void isotp_rcv_echo(struct sk_buf + { + struct sock *sk = (struct sock *)data; + struct isotp_sock *so = isotp_sk(sk); +- struct canfd_frame *cf = (struct canfd_frame *)skb->data; + + /* only handle my own local echo CF/SF skb's (no FF!) */ + if (skb->sk != sk) +@@ -883,32 +964,35 @@ static void isotp_rcv_echo(struct sk_buf + spin_lock(&so->rx_lock); + + /* so->cfecho may since belong to a new transfer; recheck under lock */ +- if (so->cfecho != *(u32 *)cf->data) ++ if (READ_ONCE(so->cfecho) != skb->hash) + goto out_unlock; + + /* cancel local echo timeout */ + hrtimer_cancel(&so->echotimer); + + /* local echo skb with consecutive frame has been consumed */ +- so->cfecho = 0; ++ WRITE_ONCE(so->cfecho, 0); + + /* claiming a transfer also takes so->rx_lock, so a plain recheck + * is enough: so->tx.state can't have flipped to ISOTP_SENDING for + * a new claim while we're still in here + */ +- if (so->tx.state != ISOTP_SENDING) ++ if (READ_ONCE(so->tx.state) != ISOTP_SENDING) + goto out_unlock; + + if (so->tx.idx >= so->tx.len) { + /* we are done */ +- so->tx.state = ISOTP_IDLE; ++ ++ isotp_set_tx_result(so, so->tx_gen, 0); ++ /* set to IDLE after publishing tx_result */ ++ smp_store_release(&so->tx.state, ISOTP_IDLE); + wake_up_interruptible(&so->wait); + goto out_unlock; + } + + if (so->txfc.bs && so->tx.bs >= so->txfc.bs) { + /* stop and wait for FC with timeout */ +- so->tx.state = ISOTP_WAIT_FC; ++ WRITE_ONCE(so->tx.state, ISOTP_WAIT_FC); + hrtimer_start(&so->txtimer, ktime_set(ISOTP_FC_TIMEOUT, 0), + HRTIMER_MODE_REL_SOFT); + goto out_unlock; +@@ -930,16 +1014,20 @@ out_unlock: + spin_unlock(&so->rx_lock); + } + +-/* shared by so->txtimer's and so->echotimer's callbacks. Both timers get +- * cancelled under so->rx_lock elsewhere, so this must stay lock-free to +- * avoid deadlocking with that; uses so->tx_gen instead to avoid tainting +- * a new transfer with an error from the one that just timed out. ++/* isotp_tx_timeout: we did not get any flow control or echo frame in time ++ * ++ * Shared by so->txtimer's and so->echotimer's callbacks. Both timers get ++ * cancelled under so->rx_lock elsewhere, so this must stay lock-free. ++ * ++ * tx.state is acquired before tx_gen. Common sequence in isotp_tx_gen_done(). ++ * cmpxchg() only orders itself, not the two preceding loads. + */ + static enum hrtimer_restart isotp_tx_timeout(struct isotp_sock *so) + { + struct sock *sk = &so->sk; ++ /* read tx.state first for the common sequence */ ++ u32 old_state = smp_load_acquire(&so->tx.state); + u32 gen = READ_ONCE(so->tx_gen); +- u32 old_state = READ_ONCE(so->tx.state); + + /* don't handle timeouts in IDLE or SHUTDOWN state */ + if (old_state == ISOTP_IDLE || old_state == ISOTP_SHUTDOWN) +@@ -949,14 +1037,14 @@ static enum hrtimer_restart isotp_tx_tim + if (cmpxchg(&so->tx.state, old_state, ISOTP_IDLE) != old_state) + return HRTIMER_NORESTART; + +- /* we did not get any flow control or echo frame in time */ ++ /* detected timeout: report 'communication error on send' */ + +- if (READ_ONCE(so->tx_gen) == gen) { +- /* report 'communication error on send' */ +- sk->sk_err = ECOMM; +- if (!sock_flag(sk, SOCK_DEAD)) +- sk_error_report(sk); +- } ++ /* a stale read of this slot by a waiter still falls back to ECOMM */ ++ isotp_set_tx_result(so, gen, ECOMM); ++ ++ sk->sk_err = ECOMM; ++ if (!sock_flag(sk, SOCK_DEAD)) ++ sk_error_report(sk); + + wake_up_interruptible(&so->wait); + +@@ -991,7 +1079,7 @@ static enum hrtimer_restart isotp_txfr_t + HRTIMER_MODE_REL_SOFT); + + /* cfecho should be consumed by isotp_rcv_echo() here */ +- if (so->tx.state == ISOTP_SENDING && !so->cfecho) ++ if (READ_ONCE(so->tx.state) == ISOTP_SENDING && !READ_ONCE(so->cfecho)) + isotp_send_cframe(so); + + return HRTIMER_NORESTART; +@@ -1009,10 +1097,12 @@ static int isotp_sendmsg(struct socket * + s64 hrtimer_sec = ISOTP_ECHO_TIMEOUT; + struct hrtimer *tx_hrt = &so->echotimer; + u32 new_state = ISOTP_SENDING; ++ u32 my_gen; ++ u32 old_cfecho; + int off; + int err; + +- if (!so->bound || so->tx.state == ISOTP_SHUTDOWN) ++ if (!so->bound || READ_ONCE(so->tx.state) == ISOTP_SHUTDOWN) + return -EADDRNOTAVAIL; + + /* claim the socket under so->rx_lock: this serializes the claim +@@ -1029,29 +1119,33 @@ static int isotp_sendmsg(struct socket * + if (msg->msg_flags & MSG_DONTWAIT) + return -EAGAIN; + +- if (so->tx.state == ISOTP_SHUTDOWN) ++ if (READ_ONCE(so->tx.state) == ISOTP_SHUTDOWN) + return -EADDRNOTAVAIL; + + /* wait for complete transmission of current pdu */ + err = wait_event_interruptible(so->wait, +- so->tx.state == ISOTP_IDLE); ++ READ_ONCE(so->tx.state) == ISOTP_IDLE || ++ READ_ONCE(so->tx.state) == ISOTP_SHUTDOWN); + if (err) + return err; + } + +- /* new transfer: bump so->tx_gen and drain the old one's timers, +- * still under the so->rx_lock we just claimed the socket with +- */ +- WRITE_ONCE(so->tx.state, ISOTP_SENDING); +- WRITE_ONCE(so->tx_gen, READ_ONCE(so->tx_gen) + 1); ++ /* txfrtimer's callback re-arms echotimer lock-free: drain it first */ ++ hrtimer_cancel(&so->txfrtimer); + hrtimer_cancel(&so->txtimer); + hrtimer_cancel(&so->echotimer); +- hrtimer_cancel(&so->txfrtimer); +- so->cfecho = 0; ++ ++ /* new transfer: increment so->tx_gen and set tx.state after barrier */ ++ my_gen = isotp_inc_tx_gen(READ_ONCE(so->tx_gen)); ++ isotp_set_tx_result(so, my_gen, ECOMM); /* prevent stale slot matching */ ++ WRITE_ONCE(so->tx_gen, my_gen); ++ smp_wmb(); /* see smp_load_acquire() in isotp_tx_[timeout|gen_done] */ ++ WRITE_ONCE(so->tx.state, ISOTP_SENDING); ++ WRITE_ONCE(so->cfecho, 0); + spin_unlock_bh(&so->rx_lock); + + /* so->bound is only checked once above - a wakeup may have +- * unbound/rebound the socket meanwhile, so re-validate it ++ * unbound/rebound the socket meanwhile => recheck + */ + if (!so->bound) { + err = -EADDRNOTAVAIL; +@@ -1103,6 +1197,9 @@ static int isotp_sendmsg(struct socket * + can_skb_reserve(skb); + can_skb_prv(skb)->ifindex = dev->ifindex; + ++ /* set uid in tx skb to identify CF echo frames */ ++ can_set_skb_uid(skb); ++ + so->tx.len = size; + so->tx.idx = 0; + +@@ -1110,8 +1207,9 @@ static int isotp_sendmsg(struct socket * + skb_put_zero(skb, so->ll.mtu); + + /* cfecho should have been zero'ed by init / former isotp_rcv_echo() */ +- if (so->cfecho) +- pr_notice_once("can-isotp: uninit cfecho %08X\n", so->cfecho); ++ old_cfecho = READ_ONCE(so->cfecho); ++ if (old_cfecho) ++ pr_notice_once("can-isotp: uninit cfecho %08X\n", old_cfecho); + + /* check for single frame transmission depending on TX_DL */ + if (size <= so->tx.ll_dl - SF_PCI_SZ4 - ae - off) { +@@ -1139,7 +1237,7 @@ static int isotp_sendmsg(struct socket * + cf->data[ae] |= size; + + /* set CF echo tag for isotp_rcv_echo() (SF-mode) */ +- so->cfecho = *(u32 *)cf->data; ++ WRITE_ONCE(so->cfecho, skb->hash); + } else { + /* send first frame */ + +@@ -1156,7 +1254,7 @@ static int isotp_sendmsg(struct socket * + so->txfc.bs = 0; + + /* set CF echo tag for isotp_rcv_echo() (CF-mode) */ +- so->cfecho = *(u32 *)cf->data; ++ WRITE_ONCE(so->cfecho, skb->hash); + } else { + /* standard flow control check */ + new_state = ISOTP_WAIT_FIRST_FC; +@@ -1166,12 +1264,12 @@ static int isotp_sendmsg(struct socket * + tx_hrt = &so->txtimer; + + /* no CF echo tag for isotp_rcv_echo() (FF-mode) */ +- so->cfecho = 0; ++ WRITE_ONCE(so->cfecho, 0); + } + } + + spin_lock_bh(&so->rx_lock); +- if (so->tx.state == ISOTP_SHUTDOWN) { ++ if (READ_ONCE(so->tx.state) == ISOTP_SHUTDOWN) { + /* isotp_release() has since taken over and already drained + * our timers - don't send into a socket that's going away + */ +@@ -1182,7 +1280,7 @@ static int isotp_sendmsg(struct socket * + return -EADDRNOTAVAIL; + } + /* WAIT_FIRST_FC for standard FF, else stays ISOTP_SENDING */ +- so->tx.state = new_state; ++ WRITE_ONCE(so->tx.state, new_state); + hrtimer_start(tx_hrt, ktime_set(hrtimer_sec, 0), + HRTIMER_MODE_REL_SOFT); + spin_unlock_bh(&so->rx_lock); +@@ -1199,20 +1297,49 @@ static int isotp_sendmsg(struct socket * + __func__, ERR_PTR(err)); + + spin_lock_bh(&so->rx_lock); ++ ++ /* new transfer already claimed by a concurrent completion, ++ * timeout or sendmsg() while we were stuck in can_send()? ++ */ ++ if (READ_ONCE(so->tx_gen) != my_gen) { ++ /* don't touch timers and state of the new transfer */ ++ spin_unlock_bh(&so->rx_lock); ++ return err; ++ } ++ + /* no transmission -> no timeout monitoring */ + hrtimer_cancel(tx_hrt); + goto err_out_drop_locked; + } + + if (wait_tx_done) { +- /* wait for complete transmission of current pdu */ +- err = wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE); ++ /* wake up for: ++ * - concurrent sendmsg() claiming a new transfer ++ * - complete transmission of current PDU ++ * - shutdown state change in isotp_release() ++ * isotp_tx_gen_done() uses common tx.state/tx_gen read sequence ++ */ ++ err = wait_event_interruptible(so->wait, ++ isotp_tx_gen_done(so, my_gen)); + if (err) + goto err_event_drop; + +- err = sock_error(sk); +- if (err) +- return err; ++ /* still our claim, but isotp_release() force-shut it down */ ++ if (smp_load_acquire(&so->tx.state) == ISOTP_SHUTDOWN && ++ READ_ONCE(so->tx_gen) == my_gen) { ++ err = -EADDRNOTAVAIL; ++ goto err_event_drop; ++ } ++ ++ /* own completion, or tx_gen moved on - either way this is ++ * what isotp_get_tx_result() recorded for my_gen ++ */ ++ err = isotp_get_tx_result(so, my_gen); ++ ++ /* drain to avoid stale error for a later poll()/SO_ERROR */ ++ sock_error(sk); ++ ++ return err ? err : size; + } + + return size; +@@ -1222,15 +1349,26 @@ err_out_drop: + spin_lock_bh(&so->rx_lock); + goto err_out_drop_locked; + err_event_drop: +- /* interrupted waiting on our own transfer - drain its timers */ ++ /* interrupted or shut down while waiting on our own transfer */ + spin_lock_bh(&so->rx_lock); ++ ++ /* new transfer already started by concurrent sendmsg()? */ ++ if (READ_ONCE(so->tx_gen) != my_gen) { ++ /* don't touch timers and states of the new transfer */ ++ spin_unlock_bh(&so->rx_lock); ++ return err; ++ } ++ + hrtimer_cancel(&so->txfrtimer); + hrtimer_cancel(&so->txtimer); + hrtimer_cancel(&so->echotimer); + err_out_drop_locked: + /* release the claim; so->rx_lock still held from above */ +- so->cfecho = 0; +- so->tx.state = ISOTP_IDLE; ++ WRITE_ONCE(so->cfecho, 0); ++ ++ /* only claim to IDLE if isotp_release() has not taken over */ ++ if (READ_ONCE(so->tx.state) != ISOTP_SHUTDOWN) ++ WRITE_ONCE(so->tx.state, ISOTP_IDLE); + spin_unlock_bh(&so->rx_lock); + wake_up_interruptible(&so->wait); + +@@ -1296,8 +1434,9 @@ static int isotp_release(struct socket * + /* best-effort: wait for a running pdu to finish, but don't block on + * it forever - give up after the first signal + */ +- while (so->tx.state != ISOTP_IDLE && +- wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE) == 0) ++ while (READ_ONCE(so->tx.state) != ISOTP_IDLE && ++ wait_event_interruptible(so->wait, ++ READ_ONCE(so->tx.state) == ISOTP_IDLE) == 0) + ; + + /* claim the socket under so->rx_lock like sendmsg() does, so its +@@ -1305,9 +1444,12 @@ static int isotp_release(struct socket * + * unconditionally, even when a signal cut the wait above short + */ + spin_lock_bh(&so->rx_lock); +- so->tx.state = ISOTP_SHUTDOWN; ++ WRITE_ONCE(so->tx.state, ISOTP_SHUTDOWN); + spin_unlock_bh(&so->rx_lock); +- so->rx.state = ISOTP_IDLE; ++ WRITE_ONCE(so->rx.state, ISOTP_IDLE); ++ ++ /* forced SHUTDOWN may have skipped IDLE (gave up on a signal) */ ++ wake_up_interruptible(&so->wait); + + spin_lock(&isotp_notifier_lock); + while (isotp_busy_notifier == so) { +@@ -1422,7 +1564,8 @@ static int isotp_bind(struct socket *soc + * with so->bound in the same lock_sock() section above, so there is + * no window in which a concurrent isotp_notify() could be missed. + */ +- if (so->tx.state != ISOTP_IDLE || so->rx.state != ISOTP_IDLE) { ++ if (READ_ONCE(so->tx.state) != ISOTP_IDLE || ++ READ_ONCE(so->rx.state) != ISOTP_IDLE) { + err = -EAGAIN; + goto out; + } +@@ -1456,7 +1599,7 @@ static int isotp_bind(struct socket *soc + isotp_rcv, sk, "isotp", sk); + + /* no consecutive frame echo skb in flight */ +- so->cfecho = 0; ++ WRITE_ONCE(so->cfecho, 0); + + /* register for echo skb's */ + can_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id), +@@ -1822,7 +1965,7 @@ static __poll_t isotp_poll(struct file * + poll_wait(file, &so->wait, wait); + + /* Check for false positives due to TX state */ +- if ((mask & EPOLLWRNORM) && (so->tx.state != ISOTP_IDLE)) ++ if ((mask & EPOLLWRNORM) && (READ_ONCE(so->tx.state) != ISOTP_IDLE)) + mask &= ~(EPOLLOUT | EPOLLWRNORM); + + return mask; diff --git a/queue-6.12/can-use-skb-hash-instead-of-private-variable-in-headroom.patch b/queue-6.12/can-use-skb-hash-instead-of-private-variable-in-headroom.patch new file mode 100644 index 0000000000..2a94fc18e7 --- /dev/null +++ b/queue-6.12/can-use-skb-hash-instead-of-private-variable-in-headroom.patch @@ -0,0 +1,221 @@ +From stable+bounces-297169-greg=kroah.com@vger.kernel.org Fri Aug 7 09:55:48 2026 +From: Oliver Hartkopp +Date: Fri, 7 Aug 2026 09:50:24 +0200 +Subject: can: use skb hash instead of private variable in headroom +To: stable@vger.kernel.org +Cc: Oliver Hartkopp , Marc Kleine-Budde +Message-ID: <20260807075025.104370-1-socketcan@hartkopp.net> + +From: Oliver Hartkopp + +commit d4fb6514ff8ed6912a71294e6b66a5d59ee88007 upstream. + +The can_skb_priv::skbcnt variable is used to identify CAN skbs in the RX +path analogue to the skb->hash. + +As the skb hash is not filled in CAN skbs move the private skbcnt value to +skb->hash and set skb->sw_hash accordingly. The skb->hash is a value used +for RPS to identify skbs. Use it as intended. + +Signed-off-by: Marc Kleine-Budde +Signed-off-by: Oliver Hartkopp +Link: https://patch.msgid.link/20260201-can_skb_ext-v8-1-3635d790fe8b@hartkopp.net +Signed-off-by: Paolo Abeni +Signed-off-by: Oliver Hartkopp +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/can/dev/skb.c | 2 -- + include/linux/can/core.h | 1 + + include/linux/can/skb.h | 2 -- + net/can/af_can.c | 14 +++++++++++--- + net/can/bcm.c | 2 -- + net/can/isotp.c | 3 --- + net/can/j1939/socket.c | 1 - + net/can/j1939/transport.c | 2 -- + net/can/raw.c | 7 +++---- + 9 files changed, 15 insertions(+), 19 deletions(-) + +--- a/drivers/net/can/dev/skb.c ++++ b/drivers/net/can/dev/skb.c +@@ -202,7 +202,6 @@ static void init_can_skb_reserve(struct + skb_reset_transport_header(skb); + + can_skb_reserve(skb); +- can_skb_prv(skb)->skbcnt = 0; + } + + struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf) +@@ -312,7 +311,6 @@ static bool can_skb_headroom_valid(struc + if (skb->ip_summed == CHECKSUM_NONE) { + /* init headroom */ + can_skb_prv(skb)->ifindex = dev->ifindex; +- can_skb_prv(skb)->skbcnt = 0; + + skb->ip_summed = CHECKSUM_UNNECESSARY; + +--- a/include/linux/can/core.h ++++ b/include/linux/can/core.h +@@ -58,6 +58,7 @@ extern void can_rx_unregister(struct net + void *data); + + extern int can_send(struct sk_buff *skb, int loop); ++void can_set_skb_uid(struct sk_buff *skb); + void can_sock_destruct(struct sock *sk); + + #endif /* !_CAN_CORE_H */ +--- a/include/linux/can/skb.h ++++ b/include/linux/can/skb.h +@@ -49,13 +49,11 @@ bool can_dropped_invalid_skb(struct net_ + /** + * struct can_skb_priv - private additional data inside CAN sk_buffs + * @ifindex: ifindex of the first interface the CAN frame appeared on +- * @skbcnt: atomic counter to have an unique id together with skb pointer + * @frame_len: length of CAN frame in data link layer + * @cf: align to the following CAN frame at skb->data + */ + struct can_skb_priv { + int ifindex; +- int skbcnt; + unsigned int frame_len; + struct can_frame cf[]; + }; +--- a/net/can/af_can.c ++++ b/net/can/af_can.c +@@ -639,6 +639,16 @@ static int can_rcv_filter(struct can_dev + return matches; + } + ++void can_set_skb_uid(struct sk_buff *skb) ++{ ++ /* create non-zero unique skb identifier together with *skb */ ++ while (!(skb->hash)) ++ skb->hash = atomic_inc_return(&skbcounter); ++ ++ skb->sw_hash = 1; ++} ++EXPORT_SYMBOL(can_set_skb_uid); ++ + static void can_receive(struct sk_buff *skb, struct net_device *dev) + { + struct can_dev_rcv_lists *dev_rcv_lists; +@@ -650,9 +660,7 @@ static void can_receive(struct sk_buff * + atomic_long_inc(&pkg_stats->rx_frames); + atomic_long_inc(&pkg_stats->rx_frames_delta); + +- /* create non-zero unique skb identifier together with *skb */ +- while (!(can_skb_prv(skb)->skbcnt)) +- can_skb_prv(skb)->skbcnt = atomic_inc_return(&skbcounter); ++ can_set_skb_uid(skb); + + rcu_read_lock(); + +--- a/net/can/bcm.c ++++ b/net/can/bcm.c +@@ -350,7 +350,6 @@ static void bcm_can_tx(struct bcm_op *op + + can_skb_reserve(skb); + can_skb_prv(skb)->ifindex = dev->ifindex; +- can_skb_prv(skb)->skbcnt = 0; + + skb_put_data(skb, cf, op->cfsiz); + +@@ -1624,7 +1623,6 @@ static int bcm_tx_send(struct msghdr *ms + } + + can_skb_prv(skb)->ifindex = dev->ifindex; +- can_skb_prv(skb)->skbcnt = 0; + skb->dev = dev; + can_skb_set_owner(skb, sk); + err = can_send(skb, 1); /* send with loopback */ +--- a/net/can/isotp.c ++++ b/net/can/isotp.c +@@ -232,7 +232,6 @@ static int isotp_send_fc(struct sock *sk + + can_skb_reserve(nskb); + can_skb_prv(nskb)->ifindex = dev->ifindex; +- can_skb_prv(nskb)->skbcnt = 0; + + nskb->dev = dev; + can_skb_set_owner(nskb, sk); +@@ -798,7 +797,6 @@ static void isotp_send_cframe(struct iso + + can_skb_reserve(skb); + can_skb_prv(skb)->ifindex = dev->ifindex; +- can_skb_prv(skb)->skbcnt = 0; + + cf = (struct canfd_frame *)skb->data; + skb_put_zero(skb, so->ll.mtu); +@@ -1104,7 +1102,6 @@ static int isotp_sendmsg(struct socket * + + can_skb_reserve(skb); + can_skb_prv(skb)->ifindex = dev->ifindex; +- can_skb_prv(skb)->skbcnt = 0; + + so->tx.len = size; + so->tx.idx = 0; +--- a/net/can/j1939/socket.c ++++ b/net/can/j1939/socket.c +@@ -889,7 +889,6 @@ static struct sk_buff *j1939_sk_alloc_sk + + can_skb_reserve(skb); + can_skb_prv(skb)->ifindex = ndev->ifindex; +- can_skb_prv(skb)->skbcnt = 0; + skb_reserve(skb, offsetof(struct can_frame, data)); + + ret = memcpy_from_msg(skb_put(skb, size), msg, size); +--- a/net/can/j1939/transport.c ++++ b/net/can/j1939/transport.c +@@ -613,7 +613,6 @@ sk_buff *j1939_tp_tx_dat_new(struct j193 + skb->dev = priv->ndev; + can_skb_reserve(skb); + can_skb_prv(skb)->ifindex = priv->ndev->ifindex; +- can_skb_prv(skb)->skbcnt = 0; + /* reserve CAN header */ + skb_reserve(skb, offsetof(struct can_frame, data)); + +@@ -1553,7 +1552,6 @@ j1939_session *j1939_session_fresh_new(s + skb->dev = priv->ndev; + can_skb_reserve(skb); + can_skb_prv(skb)->ifindex = priv->ndev->ifindex; +- can_skb_prv(skb)->skbcnt = 0; + skcb = j1939_skb_to_cb(skb); + memcpy(skcb, rel_skcb, sizeof(*skcb)); + +--- a/net/can/raw.c ++++ b/net/can/raw.c +@@ -75,8 +75,8 @@ MODULE_ALIAS("can-proto-1"); + */ + + struct uniqframe { +- int skbcnt; + const struct sk_buff *skb; ++ u32 hash; + unsigned int join_rx_count; + }; + +@@ -163,7 +163,7 @@ static void raw_rcv(struct sk_buff *oskb + + /* eliminate multiple filter matches for the same skb */ + if (this_cpu_ptr(ro->uniq)->skb == oskb && +- this_cpu_ptr(ro->uniq)->skbcnt == can_skb_prv(oskb)->skbcnt) { ++ this_cpu_ptr(ro->uniq)->hash == oskb->hash) { + if (!ro->join_filters) + return; + +@@ -173,7 +173,7 @@ static void raw_rcv(struct sk_buff *oskb + return; + } else { + this_cpu_ptr(ro->uniq)->skb = oskb; +- this_cpu_ptr(ro->uniq)->skbcnt = can_skb_prv(oskb)->skbcnt; ++ this_cpu_ptr(ro->uniq)->hash = oskb->hash; + this_cpu_ptr(ro->uniq)->join_rx_count = 1; + /* drop first frame to check all enabled filters? */ + if (ro->join_filters && ro->count > 1) +@@ -945,7 +945,6 @@ static int raw_sendmsg(struct socket *so + + can_skb_reserve(skb); + can_skb_prv(skb)->ifindex = dev->ifindex; +- can_skb_prv(skb)->skbcnt = 0; + + /* fill the skb before testing for valid CAN frames */ + err = memcpy_from_msg(skb_put(skb, size), msg, size); diff --git a/queue-6.12/rxrpc-adjust-the-rxrpc_rtt_rx-tracepoint.patch b/queue-6.12/rxrpc-adjust-the-rxrpc_rtt_rx-tracepoint.patch new file mode 100644 index 0000000000..d090c5911e --- /dev/null +++ b/queue-6.12/rxrpc-adjust-the-rxrpc_rtt_rx-tracepoint.patch @@ -0,0 +1,113 @@ +From stable+bounces-296835-greg=kroah.com@vger.kernel.org Thu Aug 6 15:55:57 2026 +From: Sasha Levin +Date: Thu, 6 Aug 2026 09:50:00 -0400 +Subject: rxrpc: Adjust the rxrpc_rtt_rx tracepoint +To: stable@vger.kernel.org +Cc: David Howells , Marc Dionne , linux-afs@lists.infradead.org, Jakub Kicinski , Sasha Levin +Message-ID: <20260806135003.602726-2-sashal@kernel.org> + +From: David Howells + +[ Upstream commit 93dfca65a1df42a3c8b1094299dc42ab8f18e5c8 ] + +Adjust the rxrpc_rtt_rx tracepoint in the following ways: + + (1) Display the collected RTT sample in the rxrpc_rtt_rx trace. + + (2) Move the division of srtt by 8 to the TP_printk() rather doing it + before invoking the trace point. + + (3) Display the min_rtt value. + +Signed-off-by: David Howells +cc: Marc Dionne +cc: linux-afs@lists.infradead.org +Signed-off-by: Jakub Kicinski +Stable-dep-of: e4d2878369d5 ("rxrpc: Fix irq-disabled in local_bh_enable()") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + include/trace/events/rxrpc.h | 14 ++++++++++---- + net/rxrpc/input.c | 4 ++-- + net/rxrpc/rtt.c | 2 +- + 3 files changed, 13 insertions(+), 7 deletions(-) + +--- a/include/trace/events/rxrpc.h ++++ b/include/trace/events/rxrpc.h +@@ -1308,9 +1308,9 @@ TRACE_EVENT(rxrpc_rtt_rx, + TP_PROTO(struct rxrpc_call *call, enum rxrpc_rtt_rx_trace why, + int slot, + rxrpc_serial_t send_serial, rxrpc_serial_t resp_serial, +- u32 rtt, u32 rto), ++ u32 rtt, u32 srtt, u32 rto), + +- TP_ARGS(call, why, slot, send_serial, resp_serial, rtt, rto), ++ TP_ARGS(call, why, slot, send_serial, resp_serial, rtt, srtt, rto), + + TP_STRUCT__entry( + __field(unsigned int, call) +@@ -1319,7 +1319,9 @@ TRACE_EVENT(rxrpc_rtt_rx, + __field(rxrpc_serial_t, send_serial) + __field(rxrpc_serial_t, resp_serial) + __field(u32, rtt) ++ __field(u32, srtt) + __field(u32, rto) ++ __field(u32, min_rtt) + ), + + TP_fast_assign( +@@ -1329,17 +1331,21 @@ TRACE_EVENT(rxrpc_rtt_rx, + __entry->send_serial = send_serial; + __entry->resp_serial = resp_serial; + __entry->rtt = rtt; ++ __entry->srtt = srtt; + __entry->rto = rto; ++ __entry->min_rtt = minmax_get(&call->peer->min_rtt) + ), + +- TP_printk("c=%08x [%d] %s sr=%08x rr=%08x rtt=%u rto=%u", ++ TP_printk("c=%08x [%d] %s sr=%08x rr=%08x rtt=%u srtt=%u rto=%u min=%u", + __entry->call, + __entry->slot, + __print_symbolic(__entry->why, rxrpc_rtt_rx_traces), + __entry->send_serial, + __entry->resp_serial, + __entry->rtt, +- __entry->rto) ++ __entry->srtt / 8, ++ __entry->rto, ++ __entry->min_rtt) + ); + + TRACE_EVENT(rxrpc_timer_set, +--- a/net/rxrpc/input.c ++++ b/net/rxrpc/input.c +@@ -674,7 +674,7 @@ static void rxrpc_complete_rtt_probe(str + */ + if (after(acked_serial, orig_serial)) { + trace_rxrpc_rtt_rx(call, rxrpc_rtt_rx_obsolete, i, +- orig_serial, acked_serial, 0, 0); ++ orig_serial, acked_serial, 0, 0, 0); + clear_bit(i + RXRPC_CALL_RTT_PEND_SHIFT, &call->rtt_avail); + smp_wmb(); + set_bit(i, &call->rtt_avail); +@@ -682,7 +682,7 @@ static void rxrpc_complete_rtt_probe(str + } + + if (!matched) +- trace_rxrpc_rtt_rx(call, rxrpc_rtt_rx_lost, 9, 0, acked_serial, 0, 0); ++ trace_rxrpc_rtt_rx(call, rxrpc_rtt_rx_lost, 9, 0, acked_serial, 0, 0, 0); + } + + /* +--- a/net/rxrpc/rtt.c ++++ b/net/rxrpc/rtt.c +@@ -175,7 +175,7 @@ void rxrpc_peer_add_rtt(struct rxrpc_cal + spin_unlock(&peer->rtt_input_lock); + + trace_rxrpc_rtt_rx(call, why, rtt_slot, send_serial, resp_serial, +- peer->srtt_us >> 3, peer->rto_us); ++ rtt_us, peer->srtt_us, peer->rto_us); + } + + /* diff --git a/queue-6.12/rxrpc-fix-irq-disabled-in-local_bh_enable.patch b/queue-6.12/rxrpc-fix-irq-disabled-in-local_bh_enable.patch new file mode 100644 index 0000000000..d3e5aa3455 --- /dev/null +++ b/queue-6.12/rxrpc-fix-irq-disabled-in-local_bh_enable.patch @@ -0,0 +1,147 @@ +From stable+bounces-296838-greg=kroah.com@vger.kernel.org Thu Aug 6 15:52:26 2026 +From: Sasha Levin +Date: Thu, 6 Aug 2026 09:50:03 -0400 +Subject: rxrpc: Fix irq-disabled in local_bh_enable() +To: stable@vger.kernel.org +Cc: David Howells , Jeffrey Altman , Marc Dionne , "Junvyyang, Tencent Zhuque Lab" , LePremierHomme , Simon Horman , linux-afs@lists.infradead.org, Jakub Kicinski , Sasha Levin +Message-ID: <20260806135003.602726-5-sashal@kernel.org> + +From: David Howells + +[ Upstream commit e4d2878369d590bf8455e3678a644e503172eafa ] + +The rxrpc_assess_MTU_size() function calls down into the IP layer to find +out the MTU size for a route. When accepting an incoming call, this is +called from rxrpc_new_incoming_call() which holds interrupts disabled +across the code that calls down to it. Unfortunately, the IP layer uses +local_bh_enable() which, config dependent, throws a warning if IRQs are +enabled: + +WARNING: CPU: 1 PID: 5544 at kernel/softirq.c:387 __local_bh_enable_ip+0x43/0xd0 +... +RIP: 0010:__local_bh_enable_ip+0x43/0xd0 +... +Call Trace: + + rt_cache_route+0x7e/0xa0 + rt_set_nexthop.isra.0+0x3b3/0x3f0 + __mkroute_output+0x43a/0x460 + ip_route_output_key_hash+0xf7/0x140 + ip_route_output_flow+0x1b/0x90 + rxrpc_assess_MTU_size.isra.0+0x2a0/0x590 + rxrpc_new_incoming_peer+0x46/0x120 + rxrpc_alloc_incoming_call+0x1b1/0x400 + rxrpc_new_incoming_call+0x1da/0x5e0 + rxrpc_input_packet+0x827/0x900 + rxrpc_io_thread+0x403/0xb60 + kthread+0x2f7/0x310 + ret_from_fork+0x2a/0x230 + ret_from_fork_asm+0x1a/0x30 +... +hardirqs last enabled at (23): _raw_spin_unlock_irq+0x24/0x50 +hardirqs last disabled at (24): _raw_read_lock_irq+0x17/0x70 +softirqs last enabled at (0): copy_process+0xc61/0x2730 +softirqs last disabled at (25): rt_add_uncached_list+0x3c/0x90 + +Fix this by moving the call to rxrpc_assess_MTU_size() out of +rxrpc_init_peer() and further up the stack where it can be done without +interrupts disabled. + +It shouldn't be a problem for rxrpc_new_incoming_call() to do it after the +locks are dropped as pmtud is going to be performed by the I/O thread - and +we're in the I/O thread at this point. + +Fixes: a2ea9a907260 ("rxrpc: Use irq-disabling spinlocks between app and I/O thread") +Signed-off-by: David Howells +Reviewed-by: Jeffrey Altman +cc: Marc Dionne +cc: Junvyyang, Tencent Zhuque Lab +cc: LePremierHomme +cc: Simon Horman +cc: linux-afs@lists.infradead.org +Link: https://patch.msgid.link/20250717074350.3767366-2-dhowells@redhat.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/rxrpc/ar-internal.h | 1 + + net/rxrpc/call_accept.c | 1 + + net/rxrpc/peer_object.c | 12 +++++++----- + 3 files changed, 9 insertions(+), 5 deletions(-) + +--- a/net/rxrpc/ar-internal.h ++++ b/net/rxrpc/ar-internal.h +@@ -1183,6 +1183,7 @@ struct rxrpc_peer *rxrpc_lookup_peer_rcu + const struct sockaddr_rxrpc *); + struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_local *local, + struct sockaddr_rxrpc *srx, gfp_t gfp); ++void rxrpc_assess_MTU_size(struct rxrpc_local *local, struct rxrpc_peer *peer); + struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *, gfp_t, + enum rxrpc_peer_trace); + void rxrpc_new_incoming_peer(struct rxrpc_local *local, struct rxrpc_peer *peer); +--- a/net/rxrpc/call_accept.c ++++ b/net/rxrpc/call_accept.c +@@ -407,6 +407,7 @@ bool rxrpc_new_incoming_call(struct rxrp + + spin_unlock(&rx->incoming_lock); + read_unlock_irq(&local->services_lock); ++ rxrpc_assess_MTU_size(local, call->peer); + + if (hlist_unhashed(&call->error_link)) { + spin_lock_irq(&call->peer->lock); +--- a/net/rxrpc/peer_object.c ++++ b/net/rxrpc/peer_object.c +@@ -149,8 +149,7 @@ struct rxrpc_peer *rxrpc_lookup_peer_rcu + * assess the MTU size for the network interface through which this peer is + * reached + */ +-static void rxrpc_assess_MTU_size(struct rxrpc_local *local, +- struct rxrpc_peer *peer) ++void rxrpc_assess_MTU_size(struct rxrpc_local *local, struct rxrpc_peer *peer) + { + struct net *net = local->net; + struct dst_entry *dst; +@@ -162,6 +161,8 @@ static void rxrpc_assess_MTU_size(struct + #endif + + peer->if_mtu = 1500; ++ peer->mtu = peer->if_mtu; ++ peer->maxdata = peer->mtu - peer->hdrsize; + + memset(&fl, 0, sizeof(fl)); + switch (peer->srx.transport.family) { +@@ -201,6 +202,9 @@ static void rxrpc_assess_MTU_size(struct + peer->if_mtu = dst_mtu(dst); + dst_release(dst); + ++ peer->mtu = peer->if_mtu; ++ peer->maxdata = peer->mtu - peer->hdrsize; ++ + _leave(" [if_mtu %u]", peer->if_mtu); + } + +@@ -239,8 +243,6 @@ static void rxrpc_init_peer(struct rxrpc + unsigned long hash_key) + { + peer->hash_key = hash_key; +- rxrpc_assess_MTU_size(local, peer); +- peer->mtu = peer->if_mtu; + + switch (peer->srx.transport.family) { + case AF_INET: +@@ -264,7 +266,6 @@ static void rxrpc_init_peer(struct rxrpc + } + + peer->hdrsize += sizeof(struct rxrpc_wire_header); +- peer->maxdata = peer->mtu - peer->hdrsize; + } + + /* +@@ -283,6 +284,7 @@ static struct rxrpc_peer *rxrpc_create_p + if (peer) { + memcpy(&peer->srx, srx, sizeof(*srx)); + rxrpc_init_peer(local, peer, hash_key); ++ rxrpc_assess_MTU_size(local, peer); + } + + _leave(" = %p", peer); diff --git a/queue-6.12/rxrpc-fix-the-calculation-and-use-of-rto.patch b/queue-6.12/rxrpc-fix-the-calculation-and-use-of-rto.patch new file mode 100644 index 0000000000..c5bbe07754 --- /dev/null +++ b/queue-6.12/rxrpc-fix-the-calculation-and-use-of-rto.patch @@ -0,0 +1,65 @@ +From stable+bounces-296836-greg=kroah.com@vger.kernel.org Thu Aug 6 15:51:40 2026 +From: Sasha Levin +Date: Thu, 6 Aug 2026 09:50:01 -0400 +Subject: rxrpc: Fix the calculation and use of RTO +To: stable@vger.kernel.org +Cc: David Howells , Simon Wilkinson , Marc Dionne , linux-afs@lists.infradead.org, Jakub Kicinski , Sasha Levin +Message-ID: <20260806135003.602726-3-sashal@kernel.org> + +From: David Howells + +[ Upstream commit 5c0ceba23bb47085d6c9c53bff08a29634ee4e7e ] + +Make the following changes to the calculation and use of RTO: + + (1) Fix rxrpc_resend() to use the backed-off RTO value obtained by calling + rxrpc_get_rto_backoff() rather than extracting the value itself. + Without this, it may retransmit packets too early. + + (2) The RTO value being similar to the RTT causes a lot of extraneous + resends because the RTT doesn't end up taking account of clearing out + of the receive queue on the server. Worse, responses to PING-ACKs are + made as fast as possible and so are less than the DATA-requested-ACK + RTT and so skew the RTT down. + + Fix this by putting a lower bound on the RTO by adding 100ms to it and + limiting the lower end to 200ms. + +Fixes: c410bf01933e ("rxrpc: Fix the excessive initial retransmission timeout") +Fixes: 37473e416234 ("rxrpc: Clean up the resend algorithm") +Signed-off-by: David Howells +Suggested-by: Simon Wilkinson +cc: Marc Dionne +cc: linux-afs@lists.infradead.org +Signed-off-by: Jakub Kicinski +Stable-dep-of: e4d2878369d5 ("rxrpc: Fix irq-disabled in local_bh_enable()") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/rxrpc/call_event.c | 3 ++- + net/rxrpc/rtt.c | 2 +- + 2 files changed, 3 insertions(+), 2 deletions(-) + +--- a/net/rxrpc/call_event.c ++++ b/net/rxrpc/call_event.c +@@ -71,7 +71,8 @@ void rxrpc_resend(struct rxrpc_call *cal + struct rxrpc_skb_priv *sp; + struct rxrpc_txbuf *txb; + rxrpc_seq_t transmitted = call->tx_transmitted; +- ktime_t next_resend = KTIME_MAX, rto = ns_to_ktime(call->peer->rto_us * NSEC_PER_USEC); ++ ktime_t next_resend = KTIME_MAX; ++ ktime_t rto = rxrpc_get_rto_backoff(call->peer, false); + ktime_t resend_at = KTIME_MAX, now, delay; + bool unacked = false, did_send = false; + unsigned int i; +--- a/net/rxrpc/rtt.c ++++ b/net/rxrpc/rtt.c +@@ -27,7 +27,7 @@ static u32 __rxrpc_set_rto(const struct + + static u32 rxrpc_bound_rto(u32 rto) + { +- return min(rto, RXRPC_RTO_MAX); ++ return clamp(200000, rto + 100000, RXRPC_RTO_MAX); + } + + /* diff --git a/queue-6.12/rxrpc-generate-rtt_min.patch b/queue-6.12/rxrpc-generate-rtt_min.patch new file mode 100644 index 0000000000..7d4dd527bd --- /dev/null +++ b/queue-6.12/rxrpc-generate-rtt_min.patch @@ -0,0 +1,91 @@ +From stable+bounces-296834-greg=kroah.com@vger.kernel.org Thu Aug 6 15:52:10 2026 +From: Sasha Levin +Date: Thu, 6 Aug 2026 09:49:59 -0400 +Subject: rxrpc: Generate rtt_min +To: stable@vger.kernel.org +Cc: David Howells , Marc Dionne , linux-afs@lists.infradead.org, Jakub Kicinski , Sasha Levin +Message-ID: <20260806135003.602726-1-sashal@kernel.org> + +From: David Howells + +[ Upstream commit c637bd066841de6d0a204898a62f1d9bb8fa1b7f ] + +Generate rtt_min as this is required by RACK-TLP. + +Signed-off-by: David Howells +cc: Marc Dionne +cc: linux-afs@lists.infradead.org +Link: https://patch.msgid.link/20241204074710.990092-27-dhowells@redhat.com +Signed-off-by: Jakub Kicinski +Stable-dep-of: e4d2878369d5 ("rxrpc: Fix irq-disabled in local_bh_enable()") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + lib/win_minmax.c | 1 + + net/rxrpc/ar-internal.h | 2 ++ + net/rxrpc/rtt.c | 20 ++++++++++++++++---- + 3 files changed, 19 insertions(+), 4 deletions(-) + +--- a/lib/win_minmax.c ++++ b/lib/win_minmax.c +@@ -97,3 +97,4 @@ u32 minmax_running_min(struct minmax *m, + + return minmax_subwin_update(m, win, &val); + } ++EXPORT_SYMBOL(minmax_running_min); +--- a/net/rxrpc/ar-internal.h ++++ b/net/rxrpc/ar-internal.h +@@ -349,6 +349,8 @@ struct rxrpc_peer { + spinlock_t rtt_input_lock; /* RTT lock for input routine */ + ktime_t rtt_last_req; /* Time of last RTT request */ + unsigned int rtt_count; /* Number of samples we've got */ ++ unsigned int rtt_taken; /* Number of samples taken (wrapping) */ ++ struct minmax min_rtt; /* Estimated minimum RTT */ + + u32 srtt_us; /* smoothed round trip time << 3 in usecs */ + u32 mdev_us; /* medium deviation */ +--- a/net/rxrpc/rtt.c ++++ b/net/rxrpc/rtt.c +@@ -127,16 +127,27 @@ static void rxrpc_set_rto(struct rxrpc_p + peer->rto_us = rxrpc_bound_rto(rto); + } + +-static void rxrpc_ack_update_rtt(struct rxrpc_peer *peer, long rtt_us) ++static void rxrpc_update_rtt_min(struct rxrpc_peer *peer, ktime_t resp_time, long rtt_us) ++{ ++ /* Window size 5mins in approx usec (ipv4.sysctl_tcp_min_rtt_wlen) */ ++ u32 wlen_us = 5ULL * NSEC_PER_SEC / 1024; ++ ++ minmax_running_min(&peer->min_rtt, wlen_us, resp_time / 1024, ++ (u32)rtt_us ? : jiffies_to_usecs(1)); ++} ++ ++static void rxrpc_ack_update_rtt(struct rxrpc_peer *peer, ktime_t resp_time, long rtt_us) + { + if (rtt_us < 0) + return; + +- //rxrpc_update_rtt_min(peer, rtt_us); ++ /* Update RACK min RTT [RFC8985 6.1 Step 1]. */ ++ rxrpc_update_rtt_min(peer, resp_time, rtt_us); ++ + rxrpc_rtt_estimator(peer, rtt_us); + rxrpc_set_rto(peer); + +- /* RFC6298: only reset backoff on valid RTT measurement. */ ++ /* Only reset backoff on valid RTT measurement [RFC6298]. */ + peer->backoff = 0; + } + +@@ -157,9 +168,10 @@ void rxrpc_peer_add_rtt(struct rxrpc_cal + return; + + spin_lock(&peer->rtt_input_lock); +- rxrpc_ack_update_rtt(peer, rtt_us); ++ rxrpc_ack_update_rtt(peer, resp_time, rtt_us); + if (peer->rtt_count < 3) + peer->rtt_count++; ++ peer->rtt_taken++; + spin_unlock(&peer->rtt_input_lock); + + trace_rxrpc_rtt_rx(call, why, rtt_slot, send_serial, resp_serial, diff --git a/queue-6.12/rxrpc-manage-rtt-per-call-rather-than-per-peer.patch b/queue-6.12/rxrpc-manage-rtt-per-call-rather-than-per-peer.patch new file mode 100644 index 0000000000..5fb8b07652 --- /dev/null +++ b/queue-6.12/rxrpc-manage-rtt-per-call-rather-than-per-peer.patch @@ -0,0 +1,547 @@ +From stable+bounces-296837-greg=kroah.com@vger.kernel.org Thu Aug 6 15:51:40 2026 +From: Sasha Levin +Date: Thu, 6 Aug 2026 09:50:02 -0400 +Subject: rxrpc: Manage RTT per-call rather than per-peer +To: stable@vger.kernel.org +Cc: David Howells , Marc Dionne , linux-afs@lists.infradead.org, Jakub Kicinski , Sasha Levin +Message-ID: <20260806135003.602726-4-sashal@kernel.org> + +From: David Howells + +[ Upstream commit b40ef2b85a7d117dd323b5910e504899e0a3e7dc ] + +Manage the determination of RTT on a per-call (ie. per-RPC op) basis rather +than on a per-peer basis, averaging across all calls going to that peer. +The problem is that the RTT measurements from the initial packets on a call +may be off because the server may do some setting up (such as getting a +lock on a file) before accepting the rest of the data in the RPC and, +further, the RTT may be affected by server-side file operations, for +instance if a large amount of data is being written or read. + +Note: When handling the FS.StoreData-type RPCs, for example, the server +uses the userStatus field in the header of ACK packets as supplementary +flow control to aid in managing this. AF_RXRPC does not yet support this, +but it should be added. + +Signed-off-by: David Howells +cc: Marc Dionne +cc: linux-afs@lists.infradead.org +Signed-off-by: Jakub Kicinski +Stable-dep-of: e4d2878369d5 ("rxrpc: Fix irq-disabled in local_bh_enable()") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + include/trace/events/rxrpc.h | 2 + net/rxrpc/ar-internal.h | 39 +++++++++--------- + net/rxrpc/call_event.c | 18 ++++---- + net/rxrpc/call_object.c | 2 + net/rxrpc/input.c | 8 +-- + net/rxrpc/output.c | 14 +++--- + net/rxrpc/peer_object.c | 8 --- + net/rxrpc/proc.c | 6 +- + net/rxrpc/rtt.c | 93 +++++++++++++++++++++---------------------- + net/rxrpc/sendmsg.c | 2 + 10 files changed, 97 insertions(+), 95 deletions(-) + +--- a/include/trace/events/rxrpc.h ++++ b/include/trace/events/rxrpc.h +@@ -1333,7 +1333,7 @@ TRACE_EVENT(rxrpc_rtt_rx, + __entry->rtt = rtt; + __entry->srtt = srtt; + __entry->rto = rto; +- __entry->min_rtt = minmax_get(&call->peer->min_rtt) ++ __entry->min_rtt = minmax_get(&call->min_rtt) + ), + + TP_printk("c=%08x [%d] %s sr=%08x rr=%08x rtt=%u srtt=%u rto=%u min=%u", +--- a/net/rxrpc/ar-internal.h ++++ b/net/rxrpc/ar-internal.h +@@ -344,20 +344,9 @@ struct rxrpc_peer { + int debug_id; /* debug ID for printks */ + struct sockaddr_rxrpc srx; /* remote address */ + +- /* calculated RTT cache */ +-#define RXRPC_RTT_CACHE_SIZE 32 +- spinlock_t rtt_input_lock; /* RTT lock for input routine */ +- ktime_t rtt_last_req; /* Time of last RTT request */ +- unsigned int rtt_count; /* Number of samples we've got */ +- unsigned int rtt_taken; /* Number of samples taken (wrapping) */ +- struct minmax min_rtt; /* Estimated minimum RTT */ +- +- u32 srtt_us; /* smoothed round trip time << 3 in usecs */ +- u32 mdev_us; /* medium deviation */ +- u32 mdev_max_us; /* maximal mdev for the last rtt period */ +- u32 rttvar_us; /* smoothed mdev_max */ +- u32 rto_us; /* Retransmission timeout in usec */ +- u8 backoff; /* Backoff timeout (as shift) */ ++ /* Calculated RTT cache */ ++ unsigned int recent_srtt_us; ++ unsigned int recent_rto_us; + + u8 cong_ssthresh; /* Congestion slow-start threshold */ + }; +@@ -742,6 +731,18 @@ struct rxrpc_call { + rxrpc_seq_t acks_hard_ack; /* Latest hard-ack point */ + rxrpc_seq_t acks_lowest_nak; /* Lowest NACK in the buffer (or ==tx_hard_ack) */ + rxrpc_serial_t acks_highest_serial; /* Highest serial number ACK'd */ ++ ++ /* Calculated RTT cache */ ++ ktime_t rtt_last_req; /* Time of last RTT request */ ++ unsigned int rtt_count; /* Number of samples we've got */ ++ unsigned int rtt_taken; /* Number of samples taken (wrapping) */ ++ struct minmax min_rtt; /* Estimated minimum RTT */ ++ u32 srtt_us; /* smoothed round trip time << 3 in usecs */ ++ u32 mdev_us; /* medium deviation */ ++ u32 mdev_max_us; /* maximal mdev for the last rtt period */ ++ u32 rttvar_us; /* smoothed mdev_max */ ++ u32 rto_us; /* Retransmission timeout in usec */ ++ u8 backoff; /* Backoff timeout (as shift) */ + }; + + /* +@@ -1222,10 +1223,12 @@ static inline int rxrpc_abort_eproto(str + /* + * rtt.c + */ +-void rxrpc_peer_add_rtt(struct rxrpc_call *, enum rxrpc_rtt_rx_trace, int, +- rxrpc_serial_t, rxrpc_serial_t, ktime_t, ktime_t); +-ktime_t rxrpc_get_rto_backoff(struct rxrpc_peer *peer, bool retrans); +-void rxrpc_peer_init_rtt(struct rxrpc_peer *); ++void rxrpc_call_add_rtt(struct rxrpc_call *call, enum rxrpc_rtt_rx_trace why, ++ int rtt_slot, ++ rxrpc_serial_t send_serial, rxrpc_serial_t resp_serial, ++ ktime_t send_time, ktime_t resp_time); ++ktime_t rxrpc_get_rto_backoff(struct rxrpc_call *call, bool retrans); ++void rxrpc_call_init_rtt(struct rxrpc_call *call); + + /* + * rxkad.c +--- a/net/rxrpc/call_event.c ++++ b/net/rxrpc/call_event.c +@@ -44,8 +44,8 @@ void rxrpc_propose_delay_ACK(struct rxrp + + trace_rxrpc_propose_ack(call, why, RXRPC_ACK_DELAY, serial); + +- if (call->peer->srtt_us) +- delay = (call->peer->srtt_us >> 3) * NSEC_PER_USEC; ++ if (call->srtt_us) ++ delay = (call->srtt_us >> 3) * NSEC_PER_USEC; + else + delay = ms_to_ktime(READ_ONCE(rxrpc_soft_ack_delay)); + ktime_add_ms(delay, call->tx_backoff); +@@ -72,7 +72,7 @@ void rxrpc_resend(struct rxrpc_call *cal + struct rxrpc_txbuf *txb; + rxrpc_seq_t transmitted = call->tx_transmitted; + ktime_t next_resend = KTIME_MAX; +- ktime_t rto = rxrpc_get_rto_backoff(call->peer, false); ++ ktime_t rto = rxrpc_get_rto_backoff(call, false); + ktime_t resend_at = KTIME_MAX, now, delay; + bool unacked = false, did_send = false; + unsigned int i; +@@ -174,7 +174,7 @@ void rxrpc_resend(struct rxrpc_call *cal + no_further_resend: + no_resend: + if (resend_at < KTIME_MAX) { +- delay = rxrpc_get_rto_backoff(call->peer, did_send); ++ delay = rxrpc_get_rto_backoff(call, did_send); + resend_at = ktime_add(resend_at, delay); + trace_rxrpc_timer_set(call, resend_at - now, rxrpc_timer_trace_resend_reset); + } +@@ -189,7 +189,7 @@ no_resend: + */ + if (!did_send) { + ktime_t next_ping = ktime_add_us(call->acks_latest_ts, +- call->peer->srtt_us >> 3); ++ call->srtt_us >> 3); + + if (ktime_sub(next_ping, now) <= 0) + rxrpc_send_ACK(call, RXRPC_ACK_PING, 0, +@@ -306,8 +306,8 @@ static void rxrpc_transmit_some_data(str + */ + static void rxrpc_send_initial_ping(struct rxrpc_call *call) + { +- if (call->peer->rtt_count < 3 || +- ktime_before(ktime_add_ms(call->peer->rtt_last_req, 1000), ++ if (call->rtt_count < 3 || ++ ktime_before(ktime_add_ms(call->rtt_last_req, 1000), + ktime_get_real())) + rxrpc_send_ACK(call, RXRPC_ACK_PING, 0, + rxrpc_propose_ack_ping_for_params); +@@ -438,10 +438,10 @@ bool rxrpc_input_call_event(struct rxrpc + rxrpc_propose_ack_rx_idle); + + if (call->ackr_nr_unacked > 2) { +- if (call->peer->rtt_count < 3) ++ if (call->rtt_count < 3) + rxrpc_send_ACK(call, RXRPC_ACK_PING, 0, + rxrpc_propose_ack_ping_for_rtt); +- else if (ktime_before(ktime_add_ms(call->peer->rtt_last_req, 1000), ++ else if (ktime_before(ktime_add_ms(call->rtt_last_req, 1000), + ktime_get_real())) + rxrpc_send_ACK(call, RXRPC_ACK_PING, 0, + rxrpc_propose_ack_ping_for_old_rtt); +--- a/net/rxrpc/call_object.c ++++ b/net/rxrpc/call_object.c +@@ -178,6 +178,8 @@ struct rxrpc_call *rxrpc_alloc_call(stru + call->cong_cwnd = RXRPC_MIN_CWND; + call->cong_ssthresh = RXRPC_TX_MAX_WINDOW; + ++ rxrpc_call_init_rtt(call); ++ + call->rxnet = rxnet; + call->rtt_avail = RXRPC_CALL_RTT_AVAIL_MASK; + atomic_inc(&rxnet->nr_calls); +--- a/net/rxrpc/input.c ++++ b/net/rxrpc/input.c +@@ -83,11 +83,11 @@ static void rxrpc_congestion_management( + /* 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->peer->rtt_count == 0) ++ if (call->rtt_count == 0) + goto out; + if (ktime_before(skb->tstamp, + ktime_add_us(call->cong_tstamp, +- call->peer->srtt_us >> 3))) ++ call->srtt_us >> 3))) + goto out_no_clear_ca; + change = rxrpc_cong_rtt_window_end; + call->cong_tstamp = skb->tstamp; +@@ -197,7 +197,7 @@ void rxrpc_congestion_degrade(struct rxr + if (__rxrpc_call_state(call) == RXRPC_CALL_CLIENT_AWAIT_REPLY) + return; + +- rtt = ns_to_ktime(call->peer->srtt_us * (1000 / 8)); ++ rtt = ns_to_ktime(call->srtt_us * (NSEC_PER_USEC / 8)); + now = ktime_get_real(); + if (!ktime_before(ktime_add(call->tx_last_sent, rtt), now)) + return; +@@ -664,7 +664,7 @@ static void rxrpc_complete_rtt_probe(str + clear_bit(i + RXRPC_CALL_RTT_PEND_SHIFT, &call->rtt_avail); + smp_mb(); /* Read data before setting avail bit */ + set_bit(i, &call->rtt_avail); +- rxrpc_peer_add_rtt(call, type, i, acked_serial, ack_serial, ++ rxrpc_call_add_rtt(call, type, i, acked_serial, ack_serial, + sent_at, resp_time); + matched = true; + } +--- a/net/rxrpc/output.c ++++ b/net/rxrpc/output.c +@@ -220,7 +220,7 @@ static void rxrpc_send_ack_packet(struct + if (ack->reason == RXRPC_ACK_PING) + rxrpc_begin_rtt_probe(call, txb->serial, now, rxrpc_rtt_tx_ping); + if (txb->flags & RXRPC_REQUEST_ACK) +- call->peer->rtt_last_req = now; ++ call->rtt_last_req = now; + rxrpc_set_keepalive(call, now); + } + rxrpc_tx_backoff(call, ret); +@@ -358,9 +358,9 @@ static void rxrpc_prepare_data_subpacket + why = rxrpc_reqack_slow_start; + else if (call->tx_winsize <= 2) + why = rxrpc_reqack_small_txwin; +- else if (call->peer->rtt_count < 3 && txb->seq & 1) ++ else if (call->rtt_count < 3) + why = rxrpc_reqack_more_rtt; +- else if (ktime_before(ktime_add_ms(call->peer->rtt_last_req, 1000), ktime_get_real())) ++ else if (ktime_before(ktime_add_ms(call->rtt_last_req, 1000), ktime_get_real())) + why = rxrpc_reqack_old_rtt; + else + goto dont_set_request_ack; +@@ -407,9 +407,9 @@ static void rxrpc_tstamp_data_packets(st + if (ack_requested) { + rxrpc_begin_rtt_probe(call, txb->serial, now, rxrpc_rtt_tx_data); + +- call->peer->rtt_last_req = now; +- if (call->peer->rtt_count > 1) { +- ktime_t delay = rxrpc_get_rto_backoff(call->peer, false); ++ call->rtt_last_req = now; ++ if (call->rtt_count > 1) { ++ ktime_t delay = rxrpc_get_rto_backoff(call, false); + + call->ack_lost_at = ktime_add(now, delay); + trace_rxrpc_timer_set(call, delay, rxrpc_timer_trace_lost_ack); +@@ -727,7 +727,7 @@ void rxrpc_transmit_one(struct rxrpc_cal + rxrpc_instant_resend(call, txb); + } + } else { +- ktime_t delay = ns_to_ktime(call->peer->rto_us * NSEC_PER_USEC); ++ ktime_t delay = ns_to_ktime(call->rto_us * NSEC_PER_USEC); + + call->resend_at = ktime_add(ktime_get_real(), delay); + trace_rxrpc_timer_set(call, delay, rxrpc_timer_trace_resend_tx); +--- a/net/rxrpc/peer_object.c ++++ b/net/rxrpc/peer_object.c +@@ -222,11 +222,8 @@ struct rxrpc_peer *rxrpc_alloc_peer(stru + peer->service_conns = RB_ROOT; + seqlock_init(&peer->service_conn_lock); + spin_lock_init(&peer->lock); +- spin_lock_init(&peer->rtt_input_lock); + peer->debug_id = atomic_inc_return(&rxrpc_debug_id); +- +- rxrpc_peer_init_rtt(peer); +- ++ peer->recent_srtt_us = UINT_MAX; + peer->cong_ssthresh = RXRPC_TX_MAX_WINDOW; + trace_rxrpc_peer(peer->debug_id, 1, why); + } +@@ -244,7 +241,6 @@ static void rxrpc_init_peer(struct rxrpc + peer->hash_key = hash_key; + rxrpc_assess_MTU_size(local, peer); + peer->mtu = peer->if_mtu; +- peer->rtt_last_req = ktime_get_real(); + + switch (peer->srx.transport.family) { + case AF_INET: +@@ -480,7 +476,7 @@ EXPORT_SYMBOL(rxrpc_kernel_get_call_peer + */ + unsigned int rxrpc_kernel_get_srtt(const struct rxrpc_peer *peer) + { +- return peer->rtt_count > 0 ? peer->srtt_us >> 3 : UINT_MAX; ++ return READ_ONCE(peer->recent_srtt_us); + } + EXPORT_SYMBOL(rxrpc_kernel_get_srtt); + +--- a/net/rxrpc/proc.c ++++ b/net/rxrpc/proc.c +@@ -299,15 +299,15 @@ static int rxrpc_peer_seq_show(struct se + now = ktime_get_seconds(); + seq_printf(seq, + "UDP %-47.47s %-47.47s %3u" +- " %3u %5u %6ds %8u %8u\n", ++ " %3u %5u %6ds %8d %8d\n", + lbuff, + rbuff, + refcount_read(&peer->ref), + peer->cong_ssthresh, + peer->mtu, + (s32)now - (s32)READ_ONCE(peer->last_tx_at), +- peer->srtt_us >> 3, +- peer->rto_us); ++ READ_ONCE(peer->recent_srtt_us), ++ READ_ONCE(peer->recent_rto_us)); + + return 0; + } +--- a/net/rxrpc/rtt.c ++++ b/net/rxrpc/rtt.c +@@ -12,17 +12,17 @@ + #include "ar-internal.h" + + #define RXRPC_RTO_MAX (120 * USEC_PER_SEC) +-#define RXRPC_TIMEOUT_INIT ((unsigned int)(1 * MSEC_PER_SEC)) /* RFC6298 2.1 initial RTO value */ ++#define RXRPC_TIMEOUT_INIT ((unsigned int)(1 * USEC_PER_SEC)) /* RFC6298 2.1 initial RTO value */ + #define rxrpc_jiffies32 ((u32)jiffies) /* As rxrpc_jiffies32 */ + +-static u32 rxrpc_rto_min_us(struct rxrpc_peer *peer) ++static u32 rxrpc_rto_min_us(struct rxrpc_call *call) + { + return 200; + } + +-static u32 __rxrpc_set_rto(const struct rxrpc_peer *peer) ++static u32 __rxrpc_set_rto(const struct rxrpc_call *call) + { +- return (peer->srtt_us >> 3) + peer->rttvar_us; ++ return (call->srtt_us >> 3) + call->rttvar_us; + } + + static u32 rxrpc_bound_rto(u32 rto) +@@ -40,10 +40,10 @@ static u32 rxrpc_bound_rto(u32 rto) + * To save cycles in the RFC 1323 implementation it was better to break + * it up into three procedures. -- erics + */ +-static void rxrpc_rtt_estimator(struct rxrpc_peer *peer, long sample_rtt_us) ++static void rxrpc_rtt_estimator(struct rxrpc_call *call, long sample_rtt_us) + { + long m = sample_rtt_us; /* RTT */ +- u32 srtt = peer->srtt_us; ++ u32 srtt = call->srtt_us; + + /* The following amusing code comes from Jacobson's + * article in SIGCOMM '88. Note that rtt and mdev +@@ -66,7 +66,7 @@ static void rxrpc_rtt_estimator(struct r + srtt += m; /* rtt = 7/8 rtt + 1/8 new */ + if (m < 0) { + m = -m; /* m is now abs(error) */ +- m -= (peer->mdev_us >> 2); /* similar update on mdev */ ++ m -= (call->mdev_us >> 2); /* similar update on mdev */ + /* This is similar to one of Eifel findings. + * Eifel blocks mdev updates when rtt decreases. + * This solution is a bit different: we use finer gain +@@ -78,31 +78,31 @@ static void rxrpc_rtt_estimator(struct r + if (m > 0) + m >>= 3; + } else { +- m -= (peer->mdev_us >> 2); /* similar update on mdev */ ++ m -= (call->mdev_us >> 2); /* similar update on mdev */ + } + +- peer->mdev_us += m; /* mdev = 3/4 mdev + 1/4 new */ +- if (peer->mdev_us > peer->mdev_max_us) { +- peer->mdev_max_us = peer->mdev_us; +- if (peer->mdev_max_us > peer->rttvar_us) +- peer->rttvar_us = peer->mdev_max_us; ++ call->mdev_us += m; /* mdev = 3/4 mdev + 1/4 new */ ++ if (call->mdev_us > call->mdev_max_us) { ++ call->mdev_max_us = call->mdev_us; ++ if (call->mdev_max_us > call->rttvar_us) ++ call->rttvar_us = call->mdev_max_us; + } + } else { + /* no previous measure. */ + srtt = m << 3; /* take the measured time to be rtt */ +- peer->mdev_us = m << 1; /* make sure rto = 3*rtt */ +- peer->rttvar_us = max(peer->mdev_us, rxrpc_rto_min_us(peer)); +- peer->mdev_max_us = peer->rttvar_us; ++ call->mdev_us = m << 1; /* make sure rto = 3*rtt */ ++ call->rttvar_us = umax(call->mdev_us, rxrpc_rto_min_us(call)); ++ call->mdev_max_us = call->rttvar_us; + } + +- peer->srtt_us = max(1U, srtt); ++ call->srtt_us = umax(srtt, 1); + } + + /* + * Calculate rto without backoff. This is the second half of Van Jacobson's + * routine referred to above. + */ +-static void rxrpc_set_rto(struct rxrpc_peer *peer) ++static void rxrpc_set_rto(struct rxrpc_call *call) + { + u32 rto; + +@@ -113,7 +113,7 @@ static void rxrpc_set_rto(struct rxrpc_p + * is invisible. Actually, Linux-2.4 also generates erratic + * ACKs in some circumstances. + */ +- rto = __rxrpc_set_rto(peer); ++ rto = __rxrpc_set_rto(call); + + /* 2. Fixups made earlier cannot be right. + * If we do not estimate RTO correctly without them, +@@ -124,73 +124,73 @@ static void rxrpc_set_rto(struct rxrpc_p + /* NOTE: clamping at RXRPC_RTO_MIN is not required, current algo + * guarantees that rto is higher. + */ +- peer->rto_us = rxrpc_bound_rto(rto); ++ call->rto_us = rxrpc_bound_rto(rto); + } + +-static void rxrpc_update_rtt_min(struct rxrpc_peer *peer, ktime_t resp_time, long rtt_us) ++static void rxrpc_update_rtt_min(struct rxrpc_call *call, ktime_t resp_time, long rtt_us) + { + /* Window size 5mins in approx usec (ipv4.sysctl_tcp_min_rtt_wlen) */ + u32 wlen_us = 5ULL * NSEC_PER_SEC / 1024; + +- minmax_running_min(&peer->min_rtt, wlen_us, resp_time / 1024, ++ minmax_running_min(&call->min_rtt, wlen_us, resp_time / 1024, + (u32)rtt_us ? : jiffies_to_usecs(1)); + } + +-static void rxrpc_ack_update_rtt(struct rxrpc_peer *peer, ktime_t resp_time, long rtt_us) ++static void rxrpc_ack_update_rtt(struct rxrpc_call *call, ktime_t resp_time, long rtt_us) + { + if (rtt_us < 0) + return; + + /* Update RACK min RTT [RFC8985 6.1 Step 1]. */ +- rxrpc_update_rtt_min(peer, resp_time, rtt_us); ++ rxrpc_update_rtt_min(call, resp_time, rtt_us); + +- rxrpc_rtt_estimator(peer, rtt_us); +- rxrpc_set_rto(peer); ++ rxrpc_rtt_estimator(call, rtt_us); ++ rxrpc_set_rto(call); + + /* Only reset backoff on valid RTT measurement [RFC6298]. */ +- peer->backoff = 0; ++ call->backoff = 0; + } + + /* + * Add RTT information to cache. This is called in softirq mode and has +- * exclusive access to the peer RTT data. ++ * exclusive access to the call RTT data. + */ +-void rxrpc_peer_add_rtt(struct rxrpc_call *call, enum rxrpc_rtt_rx_trace why, ++void rxrpc_call_add_rtt(struct rxrpc_call *call, enum rxrpc_rtt_rx_trace why, + int rtt_slot, + rxrpc_serial_t send_serial, rxrpc_serial_t resp_serial, + ktime_t send_time, ktime_t resp_time) + { +- struct rxrpc_peer *peer = call->peer; + s64 rtt_us; + + rtt_us = ktime_to_us(ktime_sub(resp_time, send_time)); + if (rtt_us < 0) + return; + +- spin_lock(&peer->rtt_input_lock); +- rxrpc_ack_update_rtt(peer, resp_time, rtt_us); +- if (peer->rtt_count < 3) +- peer->rtt_count++; +- peer->rtt_taken++; +- spin_unlock(&peer->rtt_input_lock); ++ rxrpc_ack_update_rtt(call, resp_time, rtt_us); ++ if (call->rtt_count < 3) ++ call->rtt_count++; ++ call->rtt_taken++; ++ ++ WRITE_ONCE(call->peer->recent_srtt_us, call->srtt_us / 8); ++ WRITE_ONCE(call->peer->recent_rto_us, call->rto_us); + + trace_rxrpc_rtt_rx(call, why, rtt_slot, send_serial, resp_serial, +- rtt_us, peer->srtt_us, peer->rto_us); ++ rtt_us, call->srtt_us, call->rto_us); + } + + /* + * Get the retransmission timeout to set in nanoseconds, backing it off each + * time we retransmit. + */ +-ktime_t rxrpc_get_rto_backoff(struct rxrpc_peer *peer, bool retrans) ++ktime_t rxrpc_get_rto_backoff(struct rxrpc_call *call, bool retrans) + { + u64 timo_us; +- u32 backoff = READ_ONCE(peer->backoff); ++ u32 backoff = READ_ONCE(call->backoff); + +- timo_us = peer->rto_us; ++ timo_us = call->rto_us; + timo_us <<= backoff; + if (retrans && timo_us * 2 <= RXRPC_RTO_MAX) +- WRITE_ONCE(peer->backoff, backoff + 1); ++ WRITE_ONCE(call->backoff, backoff + 1); + + if (timo_us < 1) + timo_us = 1; +@@ -198,10 +198,11 @@ ktime_t rxrpc_get_rto_backoff(struct rxr + return ns_to_ktime(timo_us * NSEC_PER_USEC); + } + +-void rxrpc_peer_init_rtt(struct rxrpc_peer *peer) ++void rxrpc_call_init_rtt(struct rxrpc_call *call) + { +- peer->rto_us = RXRPC_TIMEOUT_INIT; +- peer->mdev_us = RXRPC_TIMEOUT_INIT; +- peer->backoff = 0; +- //minmax_reset(&peer->rtt_min, rxrpc_jiffies32, ~0U); ++ call->rtt_last_req = KTIME_MIN; ++ call->rto_us = RXRPC_TIMEOUT_INIT; ++ call->mdev_us = RXRPC_TIMEOUT_INIT; ++ call->backoff = 0; ++ //minmax_reset(&call->rtt_min, rxrpc_jiffies32, ~0U); + } +--- a/net/rxrpc/sendmsg.c ++++ b/net/rxrpc/sendmsg.c +@@ -133,7 +133,7 @@ static int rxrpc_wait_for_tx_window_wait + rxrpc_seq_t tx_start, tx_win; + signed long rtt, timeout; + +- rtt = READ_ONCE(call->peer->srtt_us) >> 3; ++ rtt = READ_ONCE(call->srtt_us) >> 3; + rtt = usecs_to_jiffies(rtt) * 2; + if (rtt < 2) + rtt = 2; diff --git a/queue-6.12/series b/queue-6.12/series index 0b9f8c8b75..9f980e323f 100644 --- a/queue-6.12/series +++ b/queue-6.12/series @@ -326,3 +326,10 @@ drm-i915-hdcp-skip-inactive-mst-connectors-when-building-stream-list.patch drm-i915-hdcp-check-streams-bounds-before-overflow.patch drm-xe-stub-out-new-pagefault-layer.patch drm-xe-pt-reset-current_op-in-xe_pt_update_ops_init.patch +rxrpc-generate-rtt_min.patch +rxrpc-adjust-the-rxrpc_rtt_rx-tracepoint.patch +rxrpc-fix-the-calculation-and-use-of-rto.patch +rxrpc-manage-rtt-per-call-rather-than-per-peer.patch +rxrpc-fix-irq-disabled-in-local_bh_enable.patch +can-use-skb-hash-instead-of-private-variable-in-headroom.patch +can-isotp-fix-timer-drain-order-wakeup-handling-and-tx_gen-ordering.patch