Just to comment inside this patch and not on a second thread at
https://lore.kernel.org/linux-can/04fd32bc-b1c4-b9c3-3f8b-7987704a1f85@xxxxxxxxxxxx/T/#m25dc4514fb03cbe3f4b72b570a22d63a42ccbf5e
On 2023-08-18 13:43, Lukas Magel wrote:
With patch [1], isotp_poll was updated to also queue the poller in the
so->wait queue, which is used for send state changes. Since the queue
now also contains polling tasks that are not interested in sending, the
queue fill state can no longer be used as an indication of send
readiness. As a consequence, nonblocking writes can lead to a race and
lock-up of the socket if there is a second task polling the socket in
parallel.
With this patch, isotp_sendmsg does not consult wq_has_sleepers but
instead tries to atomically set so->tx.state and waits on so->wait if it
is unable to do so. This behavior is in alignment with isotp_poll, which
also checks so->tx.state to determine send readiness.
The patch also takes a direct exit if the wait is interrupted before
changing so->tx.state. Previously, the code jumped to err_event_drop,
resetting so->tx.state.
[1] https://lore.kernel.org/all/20230331125511.372783-1-michal.sojka@xxxxxxx
Reported-by: Maxime Jayat <maxime.jayat@xxxxxxxxxxxxxxxxx>
Closes: https://lore.kernel.org/linux-can/11328958-453f-447f-9af8-3b5824dfb041@xxxxxxxx/
Signed-off-by: Lukas Magel <lukas.magel@xxxxxxxxxx>
---
---
net/can/isotp.c | 19 ++++++++-----------
1 file changed, 8 insertions(+), 11 deletions(-)
diff --git a/net/can/isotp.c b/net/can/isotp.c
index 99770ed285..653cdb93f5 100644
--- a/net/can/isotp.c
+++ b/net/can/isotp.c
@@ -954,21 +954,18 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
if (!so->bound || so->tx.state == ISOTP_SHUTDOWN)
return -EADDRNOTAVAIL;
-wait_free_buffer:
- /* we do not support multiple buffers - for now */
- if (wq_has_sleeper(&so->wait) && (msg->msg_flags & MSG_DONTWAIT))
- return -EAGAIN;
+ while (cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SENDING) != ISOTP_IDLE) {
+ /* we do not support multiple buffers - for now */
+ if (msg->msg_flags & MSG_DONTWAIT)
+ return -EAGAIN;
- /* wait for complete transmission of current pdu */
- err = wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE);
- if (err)
- goto err_event_drop;
-
- if (cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SENDING) != ISOTP_IDLE) {
if (so->tx.state == ISOTP_SHUTDOWN)
return -EADDRNOTAVAIL;
- goto wait_free_buffer;
+ /* wait for complete transmission of current pdu */
+ err = wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE);
+ if (err)
+ return err;
NAK. Please use the former
if (err)
goto err_event_drop;
here.
Best regards,
Oliver
}
/* PDU size > default => try max_pdu_size */