From nobody Mon Sep 28 08:46:34 2026 Received: from mail-pj1-f47.google.com (mail-pj1-f47.google.com [209.85.216.47]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 59EF941D20F for ; Mon, 24 Aug 2026 12:54:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.47 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787576042; cv=none; b=qwZIxPJ4S1d09dwwJPbL9zKIfwZ00XGCSc9lPYGXWTiWFOHdr2j4GYw355CHAbPMyI5Nrd/4ASBiM0w45DcpKdFvuLDgBqcIKk5qMpE7whoceM3BzWclXryvh/dfugpDoYpSMbabOmF2pje2Mnx6F00Gut+f+auPLRZOIFZkcvg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787576042; c=relaxed/simple; bh=g5Gjw3IzAe7/knSsX/zuM7s6pCoTujnIsNbK8G68sFo=; h=From:To:Cc:Subject:Date:Message-Id:MIME-Version; b=Yrq9hvapqsesFXsu2riJ01rgTIoqDgJZyIK7qbySHCapW/iqHr16U6WN3ugvbvpGbJkuQxikyysbuReQVDdu6cCMXNuZl7r59+NqT8kCZwIffBVPjj14dKtFRM1npxW9R3garfvKvj6ctB5HfPLbsALtvhL+HvbjdD9s/Lwz38g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=EgagS3YX; arc=none smtp.client-ip=209.85.216.47 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="EgagS3YX" Received: by mail-pj1-f47.google.com with SMTP id 98e67ed59e1d1-38dfe7eb825so2916553a91.0 for ; Mon, 24 Aug 2026 05:54:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1787576040; x=1788180840; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=V8/sUQGbhm0rzxqY2kDdfzdZpT2sKv66wG53evR61DI=; b=EgagS3YX2esp64X56Rf7Pg7U5Z/m0Z3qQFiGAzWd+d78Eu5p1neqFuqPm4tZU1Nsh8 MDZPKVmoIY9YLTofbe4pFDVRSI7NsujplQE5lQjqdE9tKLcgQ23tZ9Ck5KWkAvU7zgRD TEf1cmL7/wy1G5jZhdDJb2+Ak7VhpFSaKc7WBCwHTggYHvhSXpKCcZ0a3oS1gLRvdEX8 hYy6VkgAUh5lr1m8afcnmSBQNVzvaqtokZ/gqsl27SIFA8fZ18TRS8XxTOkzMUuLtI0w /p18cI4cV2GPJmrFUVg7yeLUR1xSRFmMAa3Dx/ebwSvfOF4QDL+dHnH5YPStypYIuYEq VCCg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1787576040; x=1788180840; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=V8/sUQGbhm0rzxqY2kDdfzdZpT2sKv66wG53evR61DI=; b=JT6hNi6sB8i2RPEfD9tipSSIsCsgLflKIhkQsyPdEA+NAnT/kXtiTW31kiURGkh4t2 5ZFZJ71zqMMKnuJ08jp223/TOo9wjAbxVzNpuE/cUk1WAI2XnI/BXF/CixRVfUf7bqQI +39qb+SDx+k772TBDPt1j0dTEU0FErfhnOafqqJUpVwn8TZ81u/FJnAwOkGezs7xgZ7S ZOUY6JVUGj4/1NsjhLak4RbUPTfVTBYR1t/j427fmqeHjBMWf9VoDpbdaxlrpKLvLh6F qt96m9EzMslYxgb/IkmJi/YKjfOXZTbedruDdc0l8I9dsVLEJpc8C1iu5Nf+RKXWiYVw aJjg== X-Forwarded-Encrypted: i=1; AHgh+Rp8cdBP/rgA5YBdAmzkl/CgI7d4BpJKSbmb4Kx7/ABc+mH45zFqfhGNp/th3c5M1KimWVaGrvTnT12F1sA=@vger.kernel.org X-Gm-Message-State: AFuF++ndIIaDN4jB5cUYY2Kne8xkfwI3ni6a9s/SwMhkRIYiFhw0ct0C luk/Zg5I0m8IMTH8fm6EJxpergrlgLYTnY0aJvl5jD0w8vNFSWy4l7D/ X-Gm-Gg: AR+sD120/CA/MG1nsUiz3d6TEPgtz8lOF81DIO/mywiq3xdCzmyGY7YF3nw9kNQbrZB L+fVy5m0Wy1T0uiaUCI/8ia5UtGhywvTeMhE7DHy1PUNI2vK/qurJfkR5UCpWAXxx85QzdVzSV9 tu4rtq8Yc4itHhBVHNIBwHMOz93nUlACFZLZd52j0S2n2M9vYkeb2gpNras01iO00KAfKEdsCiL CFkFJpBf+a+s5mX8KIVxwQpUSUD4IKQ4yjBW8zSDNTXhlIotJrAsoc4vy2HgcwDl34AUVUYjI6s TTe8179Vs6ijpgOCw3P1FT/nzXhFLBnX79UZtWmHuwkdlDDxo5d6jH+BdtoeA0Q4mdYISQXzY5f usdqnhgYWHvk2j/vjGB/0s8sHaJu9cwlP/RrTUzK9jo83HbtM0d9vxoVhL72jaPNCOQQbTopeB+ Lom8RBZdFiwTHomO6Ubk9pfVQIXCjQX0NgmMxxspK5jEfh9RYnpegaDUebKdzzYY+MEDK9oMXjd ylS6kTNV/8T5PAEJgO6ffYUBg== X-Received: by 2002:a17:90a:c88b:b0:38e:6a44:671b with SMTP id 98e67ed59e1d1-395c2f4b17emr51268255a91.0.1787576040330; Mon, 24 Aug 2026 05:54:00 -0700 (PDT) Received: from mi-ThinkStation-K.mioffice.cn ([43.224.245.235]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-395c8e0ebbfsm4179719a91.0.2026.08.24.05.53.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 24 Aug 2026 05:53:59 -0700 (PDT) From: yewentian395 To: Oliver Hartkopp , Marc Kleine-Budde Cc: linux-can@vger.kernel.org, linux-kernel@vger.kernel.org, yewentian395 Subject: [PATCH] qcan: isotp: implement N_Ar timeout handling for FC transmission Date: Mon, 24 Aug 2026 20:53:43 +0800 Message-Id: <20260824125343.2700828-1-yewentian395@gmail.com> X-Mailer: git-send-email 2.34.1 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add N_Ar (ISO 15765-2) timeout logic to detect FC frame transmission failures on the receiver side. Previously, isotp_send_fc() fired the FC and immediately started the N_Cr timer (rxtimer) without confirming that the FC was actually transmitted onto the CAN bus. Introduce ISOTP_WAIT_FC_TX_CONFIRM state and fc_artimer to implement a two-phase approach: 1. N_Ar phase: after can_send(FC), wait for local echo confirmation 2. N_Cr phase: after echo arrives, start rxtimer to wait for next CF Key design points: - fc_artimer and rxtimer are mutually exclusive (never both active) - Timer handler uses guard condition (no spinlock) to avoid deadlock with hrtimer_cancel() callers, following isotp_rx_timer_handler pattern - CF arrival during WAIT_FC_TX_CONFIRM acts as implicit FC confirmation (CF from remote proves FC was transmitted successfully) - New FF/SF arrival during WAIT_FC_TX_CONFIRM cancels fc_artimer - ISOTP_FC_OVFLW does not enter N_Ar phase (fire-and-forget) - can_send() failure triggers immediate rollback State machine extension (RX path only): WAIT_DATA -> isotp_send_fc(CTS) -> WAIT_FC_TX_CONFIRM WAIT_FC_TX_CONFIRM -> FC echo / CF arrival -> WAIT_DATA WAIT_FC_TX_CONFIRM -> fc_artimer timeout -> IDLE (ECOMM) WAIT_FC_TX_CONFIRM -> new FF/SF -> IDLE (reset) Signed-off-by: yewentian395 --- net/can/isotp.c | 93 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 80 insertions(+), 13 deletions(-) diff --git a/net/can/isotp.c b/net/can/isotp.c index 1efa377f002e..2bdedfc1d1f8 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -125,12 +125,14 @@ MODULE_PARM_DESC(max_pdu_size, "maximum isotp pdu siz= e (default " =20 #define ISOTP_FC_TIMEOUT 1 /* 1 sec */ #define ISOTP_ECHO_TIMEOUT 2 /* 2 secs */ +#define ISOTP_FC_AR_TIMEOUT 1 /* 1 sec - N_Ar timeout for FC tx confirm */ =20 enum { ISOTP_IDLE =3D 0, ISOTP_WAIT_FIRST_FC, ISOTP_WAIT_FC, ISOTP_WAIT_DATA, + ISOTP_WAIT_FC_TX_CONFIRM, /* RX: waiting for local FC echo */ ISOTP_SENDING, ISOTP_SHUTDOWN, }; @@ -155,7 +157,7 @@ struct isotp_sock { canid_t rxid; ktime_t tx_gap; ktime_t lastrxcf_tstamp; - struct hrtimer rxtimer, txtimer, txfrtimer; + struct hrtimer rxtimer, txtimer, txfrtimer, fc_artimer; struct can_isotp_options opt; struct can_isotp_fc_options rxfc, txfc; struct can_isotp_ll_options ll; @@ -210,6 +212,25 @@ static enum hrtimer_restart isotp_rx_timer_handler(str= uct hrtimer *hrtimer) return HRTIMER_NORESTART; } =20 +static enum hrtimer_restart isotp_fc_ar_timer_handler(struct hrtimer *hrti= mer) +{ + struct isotp_sock *so =3D container_of(hrtimer, struct isotp_sock, + fc_artimer); + struct sock *sk =3D &so->sk; + + if (so->rx.state !=3D ISOTP_WAIT_FC_TX_CONFIRM) + return HRTIMER_NORESTART; + + so->rx.state =3D ISOTP_IDLE; + so->rx.len =3D 0; + + sk->sk_err =3D ECOMM; + if (!sock_flag(sk, SOCK_DEAD)) + sk_error_report(sk); + + return HRTIMER_NORESTART; +} + static int isotp_send_fc(struct sock *sk, int ae, u8 flowstatus) { struct net_device *dev; @@ -256,22 +277,37 @@ static int isotp_send_fc(struct sock *sk, int ae, u8 = flowstatus) =20 ncf->flags =3D so->ll.tx_flags; =20 - can_send_ret =3D can_send(nskb, 1); - if (can_send_ret) - pr_notice_once("can-isotp: %s: can_send_ret %pe\n", - __func__, ERR_PTR(can_send_ret)); - - dev_put(dev); - /* reset blocksize counter */ so->rx.bs =3D 0; =20 /* reset last CF frame rx timestamp for rx stmin enforcement */ so->lastrxcf_tstamp =3D ktime_set(0, 0); =20 - /* start rx timeout watchdog */ - hrtimer_start(&so->rxtimer, ktime_set(ISOTP_FC_TIMEOUT, 0), - HRTIMER_MODE_REL_SOFT); + if (flowstatus =3D=3D ISOTP_FC_CTS) { + /* cancel rxtimer before entering N_Ar phase */ + hrtimer_cancel(&so->rxtimer); + + /* enter N_Ar confirmation phase */ + so->rx.state =3D ISOTP_WAIT_FC_TX_CONFIRM; + hrtimer_start(&so->fc_artimer, + ktime_set(ISOTP_FC_AR_TIMEOUT, 0), + HRTIMER_MODE_REL_SOFT); + } + + can_send_ret =3D can_send(nskb, 1); + if (can_send_ret) { + pr_notice_once("can-isotp: %s: can_send_ret %pe\n", + __func__, ERR_PTR(can_send_ret)); + if (flowstatus =3D=3D ISOTP_FC_CTS) { + hrtimer_cancel(&so->fc_artimer); + so->rx.state =3D ISOTP_IDLE; + so->rx.len =3D 0; + } + dev_put(dev); + return 1; + } + + dev_put(dev); return 0; } =20 @@ -447,6 +483,9 @@ static int isotp_rcv_sf(struct sock *sk, struct canfd_f= rame *cf, int pcilen, struct isotp_sock *so =3D isotp_sk(sk); struct sk_buff *nskb; =20 + if (so->rx.state =3D=3D ISOTP_WAIT_FC_TX_CONFIRM) + hrtimer_cancel(&so->fc_artimer); + hrtimer_cancel(&so->rxtimer); so->rx.state =3D ISOTP_IDLE; =20 @@ -481,6 +520,9 @@ static int isotp_rcv_ff(struct sock *sk, struct canfd_f= rame *cf, int ae) int off; int ff_pci_sz; =20 + if (so->rx.state =3D=3D ISOTP_WAIT_FC_TX_CONFIRM) + hrtimer_cancel(&so->fc_artimer); + hrtimer_cancel(&so->rxtimer); so->rx.state =3D ISOTP_IDLE; =20 @@ -554,6 +596,13 @@ static int isotp_rcv_cf(struct sock *sk, struct canfd_= frame *cf, int ae, struct sk_buff *nskb; int i; =20 + if (so->rx.state =3D=3D ISOTP_WAIT_FC_TX_CONFIRM) { + hrtimer_cancel(&so->fc_artimer); + so->rx.state =3D ISOTP_WAIT_DATA; + hrtimer_start(&so->rxtimer, ktime_set(ISOTP_FC_TIMEOUT, 0), + HRTIMER_MODE_REL_SOFT); + } + if (so->rx.state !=3D ISOTP_WAIT_DATA) return 0; =20 @@ -855,9 +904,22 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *= data) struct sock *sk =3D (struct sock *)data; struct isotp_sock *so =3D isotp_sk(sk); struct canfd_frame *cf =3D (struct canfd_frame *)skb->data; + int ae =3D (so->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0; =20 - /* only handle my own local echo CF/SF skb's (no FF!) */ - if (skb->sk !=3D sk || so->cfecho !=3D *(u32 *)cf->data) + if (skb->sk !=3D sk) + return; + + /* FC echo handling: confirm FC was transmitted (N_Ar) */ + if (so->rx.state =3D=3D ISOTP_WAIT_FC_TX_CONFIRM && + (cf->data[ae] & 0xF0) =3D=3D N_PCI_FC) { + hrtimer_cancel(&so->fc_artimer); + so->rx.state =3D ISOTP_WAIT_DATA; + hrtimer_start(&so->rxtimer, ktime_set(ISOTP_FC_TIMEOUT, 0), + HRTIMER_MODE_REL_SOFT); + return; + } + + if (so->cfecho !=3D *(u32 *)cf->data) return; =20 /* cancel local echo timeout */ @@ -1225,6 +1287,7 @@ static int isotp_release(struct socket *sock) hrtimer_cancel(&so->txfrtimer); hrtimer_cancel(&so->txtimer); hrtimer_cancel(&so->rxtimer); + hrtimer_cancel(&so->fc_artimer); =20 so->ifindex =3D 0; so->bound =3D 0; @@ -1563,6 +1626,8 @@ static void isotp_notify(struct isotp_sock *so, unsig= ned long msg, isotp_rcv_echo, sk); } =20 + hrtimer_cancel(&so->fc_artimer); + so->rx.state =3D ISOTP_IDLE; so->ifindex =3D 0; so->bound =3D 0; release_sock(sk); @@ -1639,6 +1704,8 @@ static int isotp_init(struct sock *sk) hrtimer_setup(&so->txtimer, isotp_tx_timer_handler, CLOCK_MONOTONIC, HRTI= MER_MODE_REL_SOFT); hrtimer_setup(&so->txfrtimer, isotp_txfr_timer_handler, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); + hrtimer_setup(&so->fc_artimer, isotp_fc_ar_timer_handler, CLOCK_MONOTONIC, + HRTIMER_MODE_REL_SOFT); =20 init_waitqueue_head(&so->wait); spin_lock_init(&so->rx_lock); --=20 2.43.0