Commit 3793301c authored by Oliver Hartkopp's avatar Oliver Hartkopp Committed by Marc Kleine-Budde

can: raw: fix CAN FD frame transmissions over CAN XL devices

A CAN XL device is always capable to process CAN FD frames. The former
check when sending CAN FD frames relied on the existence of a CAN FD
device and did not check for a CAN XL device that would be correct
too.

With this patch the CAN FD feature is enabled automatically when CAN
XL is switched on - and CAN FD cannot be switch off while CAN XL is
enabled.

This precondition also leads to a clean up and reduction of checks in
the hot path in raw_rcv() and raw_sendmsg(). Some conditions are
reordered to handle simple checks first.

changes since v1: https://lore.kernel.org/all/20230131091012.50553-1-socketcan@hartkopp.net
- fixed typo: devive -> device
changes since v2: https://lore.kernel.org/all/20230131091824.51026-1-socketcan@hartkopp.net/
- reorder checks in if statements to handle simple checks first

Fixes: 62633269 ("can: raw: add CAN XL support")
Signed-off-by: default avatarOliver Hartkopp <socketcan@hartkopp.net>
Link: https://lore.kernel.org/all/20230131105613.55228-1-socketcan@hartkopp.netSigned-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parent d0553680
...@@ -132,8 +132,8 @@ static void raw_rcv(struct sk_buff *oskb, void *data) ...@@ -132,8 +132,8 @@ static void raw_rcv(struct sk_buff *oskb, void *data)
return; return;
/* make sure to not pass oversized frames to the socket */ /* make sure to not pass oversized frames to the socket */
if ((can_is_canfd_skb(oskb) && !ro->fd_frames && !ro->xl_frames) || if ((!ro->fd_frames && can_is_canfd_skb(oskb)) ||
(can_is_canxl_skb(oskb) && !ro->xl_frames)) (!ro->xl_frames && can_is_canxl_skb(oskb)))
return; return;
/* eliminate multiple filter matches for the same skb */ /* eliminate multiple filter matches for the same skb */
...@@ -670,6 +670,11 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, ...@@ -670,6 +670,11 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
if (copy_from_sockptr(&ro->fd_frames, optval, optlen)) if (copy_from_sockptr(&ro->fd_frames, optval, optlen))
return -EFAULT; return -EFAULT;
/* Enabling CAN XL includes CAN FD */
if (ro->xl_frames && !ro->fd_frames) {
ro->fd_frames = ro->xl_frames;
return -EINVAL;
}
break; break;
case CAN_RAW_XL_FRAMES: case CAN_RAW_XL_FRAMES:
...@@ -679,6 +684,9 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, ...@@ -679,6 +684,9 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
if (copy_from_sockptr(&ro->xl_frames, optval, optlen)) if (copy_from_sockptr(&ro->xl_frames, optval, optlen))
return -EFAULT; return -EFAULT;
/* Enabling CAN XL includes CAN FD */
if (ro->xl_frames)
ro->fd_frames = ro->xl_frames;
break; break;
case CAN_RAW_JOIN_FILTERS: case CAN_RAW_JOIN_FILTERS:
...@@ -786,6 +794,25 @@ static int raw_getsockopt(struct socket *sock, int level, int optname, ...@@ -786,6 +794,25 @@ static int raw_getsockopt(struct socket *sock, int level, int optname,
return 0; return 0;
} }
static bool raw_bad_txframe(struct raw_sock *ro, struct sk_buff *skb, int mtu)
{
/* Classical CAN -> no checks for flags and device capabilities */
if (can_is_can_skb(skb))
return false;
/* CAN FD -> needs to be enabled and a CAN FD or CAN XL device */
if (ro->fd_frames && can_is_canfd_skb(skb) &&
(mtu == CANFD_MTU || can_is_canxl_dev_mtu(mtu)))
return false;
/* CAN XL -> needs to be enabled and a CAN XL device */
if (ro->xl_frames && can_is_canxl_skb(skb) &&
can_is_canxl_dev_mtu(mtu))
return false;
return true;
}
static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
{ {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
...@@ -833,20 +860,8 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) ...@@ -833,20 +860,8 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
goto free_skb; goto free_skb;
err = -EINVAL; err = -EINVAL;
if (ro->xl_frames && can_is_canxl_dev_mtu(dev->mtu)) { if (raw_bad_txframe(ro, skb, dev->mtu))
/* CAN XL, CAN FD and Classical CAN */ goto free_skb;
if (!can_is_canxl_skb(skb) && !can_is_canfd_skb(skb) &&
!can_is_can_skb(skb))
goto free_skb;
} else if (ro->fd_frames && dev->mtu == CANFD_MTU) {
/* CAN FD and Classical CAN */
if (!can_is_canfd_skb(skb) && !can_is_can_skb(skb))
goto free_skb;
} else {
/* Classical CAN */
if (!can_is_can_skb(skb))
goto free_skb;
}
sockcm_init(&sockc, sk); sockcm_init(&sockc, sk);
if (msg->msg_controllen) { if (msg->msg_controllen) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment