Skip to content

Commit 8663378

Browse files
hartkoppmarckleinebudde
authored andcommitted
can: isotp: fix tx state handling for echo tx processing
In commit 4b7fe92 ("can: isotp: add local echo tx processing for consecutive frames") the data flow for consecutive frames (CF) has been reworked to improve the reliability of long data transfers. This rework did not touch the transmission and the tx state changes of single frame (SF) transfers which likely led to the WARN in the isotp_tx_timer_handler() catching a wrong tx state. This patch makes use of the improved frame processing for SF frames and sets the ISOTP_SENDING state in isotp_sendmsg() within the cmpxchg() condition handling. A review of the state machine and the timer handling additionally revealed a missing echo timeout handling in the case of the burst mode in isotp_rcv_echo() and removes a potential timer configuration uncertainty in isotp_rcv_fc() when the receiver requests consecutive frames. Fixes: 4b7fe92 ("can: isotp: add local echo tx processing for consecutive frames") Link: https://lore.kernel.org/linux-can/CAO4mrfe3dG7cMP1V5FLUkw7s+50c9vichigUMQwsxX4M=45QEw@mail.gmail.com/T/#u Reported-by: Wei Chen <harperchen1110@gmail.com> Cc: stable@vger.kernel.org # v6.0 Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net> Link: https://lore.kernel.org/all/20221104142551.16924-1-socketcan@hartkopp.net Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
1 parent 8aa59e3 commit 8663378

File tree

1 file changed

+38
-33
lines changed

1 file changed

+38
-33
lines changed

net/can/isotp.c

Lines changed: 38 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ MODULE_ALIAS("can-proto-6");
111111
#define ISOTP_FC_WT 1 /* wait */
112112
#define ISOTP_FC_OVFLW 2 /* overflow */
113113

114+
#define ISOTP_FC_TIMEOUT 1 /* 1 sec */
115+
#define ISOTP_ECHO_TIMEOUT 2 /* 2 secs */
116+
114117
enum {
115118
ISOTP_IDLE = 0,
116119
ISOTP_WAIT_FIRST_FC,
@@ -258,7 +261,8 @@ static int isotp_send_fc(struct sock *sk, int ae, u8 flowstatus)
258261
so->lastrxcf_tstamp = ktime_set(0, 0);
259262

260263
/* start rx timeout watchdog */
261-
hrtimer_start(&so->rxtimer, ktime_set(1, 0), HRTIMER_MODE_REL_SOFT);
264+
hrtimer_start(&so->rxtimer, ktime_set(ISOTP_FC_TIMEOUT, 0),
265+
HRTIMER_MODE_REL_SOFT);
262266
return 0;
263267
}
264268

@@ -344,6 +348,8 @@ static int check_pad(struct isotp_sock *so, struct canfd_frame *cf,
344348
return 0;
345349
}
346350

351+
static void isotp_send_cframe(struct isotp_sock *so);
352+
347353
static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae)
348354
{
349355
struct sock *sk = &so->sk;
@@ -398,14 +404,15 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae)
398404
case ISOTP_FC_CTS:
399405
so->tx.bs = 0;
400406
so->tx.state = ISOTP_SENDING;
401-
/* start cyclic timer for sending CF frame */
402-
hrtimer_start(&so->txtimer, so->tx_gap,
407+
/* send CF frame and enable echo timeout handling */
408+
hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
403409
HRTIMER_MODE_REL_SOFT);
410+
isotp_send_cframe(so);
404411
break;
405412

406413
case ISOTP_FC_WT:
407414
/* start timer to wait for next FC frame */
408-
hrtimer_start(&so->txtimer, ktime_set(1, 0),
415+
hrtimer_start(&so->txtimer, ktime_set(ISOTP_FC_TIMEOUT, 0),
409416
HRTIMER_MODE_REL_SOFT);
410417
break;
411418

@@ -600,7 +607,7 @@ static int isotp_rcv_cf(struct sock *sk, struct canfd_frame *cf, int ae,
600607
/* perform blocksize handling, if enabled */
601608
if (!so->rxfc.bs || ++so->rx.bs < so->rxfc.bs) {
602609
/* start rx timeout watchdog */
603-
hrtimer_start(&so->rxtimer, ktime_set(1, 0),
610+
hrtimer_start(&so->rxtimer, ktime_set(ISOTP_FC_TIMEOUT, 0),
604611
HRTIMER_MODE_REL_SOFT);
605612
return 0;
606613
}
@@ -829,7 +836,7 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data)
829836
struct isotp_sock *so = isotp_sk(sk);
830837
struct canfd_frame *cf = (struct canfd_frame *)skb->data;
831838

832-
/* only handle my own local echo skb's */
839+
/* only handle my own local echo CF/SF skb's (no FF!) */
833840
if (skb->sk != sk || so->cfecho != *(u32 *)cf->data)
834841
return;
835842

@@ -849,13 +856,16 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data)
849856
if (so->txfc.bs && so->tx.bs >= so->txfc.bs) {
850857
/* stop and wait for FC with timeout */
851858
so->tx.state = ISOTP_WAIT_FC;
852-
hrtimer_start(&so->txtimer, ktime_set(1, 0),
859+
hrtimer_start(&so->txtimer, ktime_set(ISOTP_FC_TIMEOUT, 0),
853860
HRTIMER_MODE_REL_SOFT);
854861
return;
855862
}
856863

857864
/* no gap between data frames needed => use burst mode */
858865
if (!so->tx_gap) {
866+
/* enable echo timeout handling */
867+
hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
868+
HRTIMER_MODE_REL_SOFT);
859869
isotp_send_cframe(so);
860870
return;
861871
}
@@ -879,7 +889,7 @@ static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer)
879889
/* start timeout for unlikely lost echo skb */
880890
hrtimer_set_expires(&so->txtimer,
881891
ktime_add(ktime_get(),
882-
ktime_set(2, 0)));
892+
ktime_set(ISOTP_ECHO_TIMEOUT, 0)));
883893
restart = HRTIMER_RESTART;
884894

885895
/* push out the next consecutive frame */
@@ -907,7 +917,8 @@ static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer)
907917
break;
908918

909919
default:
910-
WARN_ON_ONCE(1);
920+
WARN_ONCE(1, "can-isotp: tx timer state %08X cfecho %08X\n",
921+
so->tx.state, so->cfecho);
911922
}
912923

913924
return restart;
@@ -923,7 +934,7 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
923934
struct canfd_frame *cf;
924935
int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0;
925936
int wait_tx_done = (so->opt.flags & CAN_ISOTP_WAIT_TX_DONE) ? 1 : 0;
926-
s64 hrtimer_sec = 0;
937+
s64 hrtimer_sec = ISOTP_ECHO_TIMEOUT;
927938
int off;
928939
int err;
929940

@@ -942,6 +953,8 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
942953
err = wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE);
943954
if (err)
944955
goto err_out;
956+
957+
so->tx.state = ISOTP_SENDING;
945958
}
946959

947960
if (!size || size > MAX_MSG_LENGTH) {
@@ -986,6 +999,10 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
986999
cf = (struct canfd_frame *)skb->data;
9871000
skb_put_zero(skb, so->ll.mtu);
9881001

1002+
/* cfecho should have been zero'ed by init / former isotp_rcv_echo() */
1003+
if (so->cfecho)
1004+
pr_notice_once("can-isotp: uninit cfecho %08X\n", so->cfecho);
1005+
9891006
/* check for single frame transmission depending on TX_DL */
9901007
if (size <= so->tx.ll_dl - SF_PCI_SZ4 - ae - off) {
9911008
/* The message size generally fits into a SingleFrame - good.
@@ -1011,11 +1028,8 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
10111028
else
10121029
cf->data[ae] |= size;
10131030

1014-
so->tx.state = ISOTP_IDLE;
1015-
wake_up_interruptible(&so->wait);
1016-
1017-
/* don't enable wait queue for a single frame transmission */
1018-
wait_tx_done = 0;
1031+
/* set CF echo tag for isotp_rcv_echo() (SF-mode) */
1032+
so->cfecho = *(u32 *)cf->data;
10191033
} else {
10201034
/* send first frame */
10211035

@@ -1031,31 +1045,23 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
10311045
/* disable wait for FCs due to activated block size */
10321046
so->txfc.bs = 0;
10331047

1034-
/* cfecho should have been zero'ed by init */
1035-
if (so->cfecho)
1036-
pr_notice_once("can-isotp: no fc cfecho %08X\n",
1037-
so->cfecho);
1038-
1039-
/* set consecutive frame echo tag */
1048+
/* set CF echo tag for isotp_rcv_echo() (CF-mode) */
10401049
so->cfecho = *(u32 *)cf->data;
1041-
1042-
/* switch directly to ISOTP_SENDING state */
1043-
so->tx.state = ISOTP_SENDING;
1044-
1045-
/* start timeout for unlikely lost echo skb */
1046-
hrtimer_sec = 2;
10471050
} else {
10481051
/* standard flow control check */
10491052
so->tx.state = ISOTP_WAIT_FIRST_FC;
10501053

10511054
/* start timeout for FC */
1052-
hrtimer_sec = 1;
1053-
}
1055+
hrtimer_sec = ISOTP_FC_TIMEOUT;
10541056

1055-
hrtimer_start(&so->txtimer, ktime_set(hrtimer_sec, 0),
1056-
HRTIMER_MODE_REL_SOFT);
1057+
/* no CF echo tag for isotp_rcv_echo() (FF-mode) */
1058+
so->cfecho = 0;
1059+
}
10571060
}
10581061

1062+
hrtimer_start(&so->txtimer, ktime_set(hrtimer_sec, 0),
1063+
HRTIMER_MODE_REL_SOFT);
1064+
10591065
/* send the first or only CAN frame */
10601066
cf->flags = so->ll.tx_flags;
10611067

@@ -1068,8 +1074,7 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
10681074
__func__, ERR_PTR(err));
10691075

10701076
/* no transmission -> no timeout monitoring */
1071-
if (hrtimer_sec)
1072-
hrtimer_cancel(&so->txtimer);
1077+
hrtimer_cancel(&so->txtimer);
10731078

10741079
/* reset consecutive frame echo tag */
10751080
so->cfecho = 0;

0 commit comments

Comments
 (0)