--- /dev/null
+From stable+bounces-297170-greg=kroah.com@vger.kernel.org Fri Aug 7 09:50:59 2026
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+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 <socketcan@hartkopp.net>, Marc Kleine-Budde <mkl@pengutronix.de>
+Message-ID: <20260807075025.104370-2-socketcan@hartkopp.net>
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+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 <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260724181525.43556-1-socketcan@hartkopp.net
+Cc: stable@kernel.org
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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;
--- /dev/null
+From stable+bounces-297169-greg=kroah.com@vger.kernel.org Fri Aug 7 09:55:48 2026
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+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 <socketcan@hartkopp.net>, Marc Kleine-Budde <mkl@pengutronix.de>
+Message-ID: <20260807075025.104370-1-socketcan@hartkopp.net>
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+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 <mkl@pengutronix.de>
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260201-can_skb_ext-v8-1-3635d790fe8b@hartkopp.net
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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);
--- /dev/null
+From stable+bounces-296835-greg=kroah.com@vger.kernel.org Thu Aug 6 15:55:57 2026
+From: Sasha Levin <sashal@kernel.org>
+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 <dhowells@redhat.com>, Marc Dionne <marc.dionne@auristor.com>, linux-afs@lists.infradead.org, Jakub Kicinski <kuba@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260806135003.602726-2-sashal@kernel.org>
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+cc: Marc Dionne <marc.dionne@auristor.com>
+cc: linux-afs@lists.infradead.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: e4d2878369d5 ("rxrpc: Fix irq-disabled in local_bh_enable()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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);
+ }
+
+ /*
--- /dev/null
+From stable+bounces-296838-greg=kroah.com@vger.kernel.org Thu Aug 6 15:52:26 2026
+From: Sasha Levin <sashal@kernel.org>
+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 <dhowells@redhat.com>, Jeffrey Altman <jaltman@auristor.com>, Marc Dionne <marc.dionne@auristor.com>, "Junvyyang, Tencent Zhuque Lab" <zhuque@tencent.com>, LePremierHomme <kwqcheii@proton.me>, Simon Horman <horms@kernel.org>, linux-afs@lists.infradead.org, Jakub Kicinski <kuba@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260806135003.602726-5-sashal@kernel.org>
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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:
+ <TASK>
+ 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 <dhowells@redhat.com>
+Reviewed-by: Jeffrey Altman <jaltman@auristor.com>
+cc: Marc Dionne <marc.dionne@auristor.com>
+cc: Junvyyang, Tencent Zhuque Lab <zhuque@tencent.com>
+cc: LePremierHomme <kwqcheii@proton.me>
+cc: Simon Horman <horms@kernel.org>
+cc: linux-afs@lists.infradead.org
+Link: https://patch.msgid.link/20250717074350.3767366-2-dhowells@redhat.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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);
--- /dev/null
+From stable+bounces-296836-greg=kroah.com@vger.kernel.org Thu Aug 6 15:51:40 2026
+From: Sasha Levin <sashal@kernel.org>
+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 <dhowells@redhat.com>, Simon Wilkinson <sxw@auristor.com>, Marc Dionne <marc.dionne@auristor.com>, linux-afs@lists.infradead.org, Jakub Kicinski <kuba@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260806135003.602726-3-sashal@kernel.org>
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Suggested-by: Simon Wilkinson <sxw@auristor.com>
+cc: Marc Dionne <marc.dionne@auristor.com>
+cc: linux-afs@lists.infradead.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: e4d2878369d5 ("rxrpc: Fix irq-disabled in local_bh_enable()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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);
+ }
+
+ /*
--- /dev/null
+From stable+bounces-296834-greg=kroah.com@vger.kernel.org Thu Aug 6 15:52:10 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 6 Aug 2026 09:49:59 -0400
+Subject: rxrpc: Generate rtt_min
+To: stable@vger.kernel.org
+Cc: David Howells <dhowells@redhat.com>, Marc Dionne <marc.dionne@auristor.com>, linux-afs@lists.infradead.org, Jakub Kicinski <kuba@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260806135003.602726-1-sashal@kernel.org>
+
+From: David Howells <dhowells@redhat.com>
+
+[ Upstream commit c637bd066841de6d0a204898a62f1d9bb8fa1b7f ]
+
+Generate rtt_min as this is required by RACK-TLP.
+
+Signed-off-by: David Howells <dhowells@redhat.com>
+cc: Marc Dionne <marc.dionne@auristor.com>
+cc: linux-afs@lists.infradead.org
+Link: https://patch.msgid.link/20241204074710.990092-27-dhowells@redhat.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: e4d2878369d5 ("rxrpc: Fix irq-disabled in local_bh_enable()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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,
--- /dev/null
+From stable+bounces-296837-greg=kroah.com@vger.kernel.org Thu Aug 6 15:51:40 2026
+From: Sasha Levin <sashal@kernel.org>
+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 <dhowells@redhat.com>, Marc Dionne <marc.dionne@auristor.com>, linux-afs@lists.infradead.org, Jakub Kicinski <kuba@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260806135003.602726-4-sashal@kernel.org>
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+cc: Marc Dionne <marc.dionne@auristor.com>
+cc: linux-afs@lists.infradead.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: e4d2878369d5 ("rxrpc: Fix irq-disabled in local_bh_enable()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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;
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