diff --git a/net/tcp/tcp_send_buffered.c b/net/tcp/tcp_send_buffered.c index 2743dcab27..239107eaf7 100644 --- a/net/tcp/tcp_send_buffered.c +++ b/net/tcp/tcp_send_buffered.c @@ -1129,69 +1129,73 @@ ssize_t psock_tcp_send(FAR struct socket *psock, FAR const void *buf, } #endif /* CONFIG_NET_SEND_BUFSIZE */ - /* Allocate a write buffer. Careful, the network will be momentarily - * unlocked here. - */ - - /* Try to coalesce into the last wrb. - * - * But only when it might yield larger segments. - * (REVISIT: It might make sense to lift this condition. - * IOB boundaries and segment boundaries usually do not match. - * It makes sense to save the number of IOBs.) - * - * Also, for simplicity, do it only when we haven't sent anything - * from the the wrb yet. - */ - - max_wrb_size = tcp_max_wrb_size(conn); - wrb = (FAR struct tcp_wrbuffer_s *)sq_tail(&conn->write_q); - if (wrb != NULL && TCP_WBSENT(wrb) == 0 && TCP_WBNRTX(wrb) == 0 && - TCP_WBPKTLEN(wrb) < max_wrb_size && - (TCP_WBPKTLEN(wrb) % conn->mss) != 0) + while (true) { - wrb = (FAR struct tcp_wrbuffer_s *)sq_remlast(&conn->write_q); - ninfo("coalesce %zu bytes to wrb %p (%" PRIu16 ")\n", len, wrb, - TCP_WBPKTLEN(wrb)); - DEBUGASSERT(TCP_WBPKTLEN(wrb) > 0); - } - else if (nonblock) - { - wrb = tcp_wrbuffer_tryalloc(); - ninfo("new wrb %p (non blocking)\n", wrb); - } - else - { - wrb = tcp_wrbuffer_alloc(); - ninfo("new wrb %p\n", wrb); - } + struct iob_s *iob; - if (wrb == NULL) - { - /* A buffer allocation error occurred */ + /* Allocate a write buffer. Careful, the network will be + * momentarily unlocked here. + */ - nerr("ERROR: Failed to allocate write buffer\n"); - ret = nonblock ? -EAGAIN : -ENOMEM; - goto errout_with_lock; - } + /* Try to coalesce into the last wrb. + * + * But only when it might yield larger segments. + * (REVISIT: It might make sense to lift this condition. + * IOB boundaries and segment boundaries usually do not match. + * It makes sense to save the number of IOBs.) + * + * Also, for simplicity, do it only when we haven't sent anything + * from the the wrb yet. + */ - /* Initialize the write buffer */ + max_wrb_size = tcp_max_wrb_size(conn); + wrb = (FAR struct tcp_wrbuffer_s *)sq_tail(&conn->write_q); + if (wrb != NULL && TCP_WBSENT(wrb) == 0 && TCP_WBNRTX(wrb) == 0 && + TCP_WBPKTLEN(wrb) < max_wrb_size && + (TCP_WBPKTLEN(wrb) % conn->mss) != 0) + { + wrb = (FAR struct tcp_wrbuffer_s *)sq_remlast(&conn->write_q); + ninfo("coalesce %zu bytes to wrb %p (%" PRIu16 ")\n", len, wrb, + TCP_WBPKTLEN(wrb)); + DEBUGASSERT(TCP_WBPKTLEN(wrb) > 0); + } + else if (nonblock) + { + wrb = tcp_wrbuffer_tryalloc(); + ninfo("new wrb %p (non blocking)\n", wrb); + DEBUGASSERT(TCP_WBPKTLEN(wrb) == 0); + } + else + { + wrb = tcp_wrbuffer_alloc(); + ninfo("new wrb %p\n", wrb); + DEBUGASSERT(TCP_WBPKTLEN(wrb) == 0); + } - TCP_WBSEQNO(wrb) = (unsigned)-1; - TCP_WBNRTX(wrb) = 0; + if (wrb == NULL) + { + /* A buffer allocation error occurred */ - off = TCP_WBPKTLEN(wrb); - if (off + chunk_len > max_wrb_size) - { - chunk_len = max_wrb_size - off; - } + nerr("ERROR: Failed to allocate write buffer\n"); + ret = nonblock ? -EAGAIN : -ENOMEM; + goto errout_with_lock; + } - /* Copy the user data into the write buffer. We cannot wait for - * buffer space if the socket was opened non-blocking. - */ + /* Initialize the write buffer */ + + TCP_WBSEQNO(wrb) = (unsigned)-1; + TCP_WBNRTX(wrb) = 0; + + off = TCP_WBPKTLEN(wrb); + if (off + chunk_len > max_wrb_size) + { + chunk_len = max_wrb_size - off; + } + + /* Copy the user data into the write buffer. We cannot wait for + * buffer space. + */ - if (nonblock) - { /* The return value from TCP_WBTRYCOPYIN is either OK or * -ENOMEM if less than the entire data chunk could be allocated. * If -ENOMEM is returned, check if at least a part of the data @@ -1220,34 +1224,48 @@ ssize_t psock_tcp_send(FAR struct socket *psock, FAR const void *buf, } else { - nerr("ERROR: Failed to add data to the I/O chain\n"); - ret = -EWOULDBLOCK; - goto errout_with_wrb; + chunk_result = 0; } } else { DEBUGASSERT(chunk_result == chunk_len); } - } - else - { - unsigned int count; - int blresult; - /* iob_copyin might wait for buffers to be freed, but if network is - * locked this might never happen, since network driver is also - * locked, therefore we need to break the lock + if (chunk_result > 0) + { + break; + } + + /* release wrb */ + + if (TCP_WBPKTLEN(wrb) > 0) + { + DEBUGASSERT(TCP_WBSENT(wrb) == 0); + DEBUGASSERT(TCP_WBPKTLEN(wrb) > 0); + sq_addlast(&wrb->wb_node, &conn->write_q); + } + else + { + tcp_wrbuffer_release(wrb); + } + + if (nonblock) + { + nerr("ERROR: Failed to add data to the I/O chain\n"); + ret = -EAGAIN; + goto errout_with_lock; + } + + /* Wait for at least one IOB getting available. + * + * Note: net_ioballoc releases the network lock when blocking. + * It allows our write_q being drained in the meantime. Otherwise, + * we risk a deadlock with other threads competing on IOBs. */ - blresult = net_breaklock(&count); - ninfo("starting copyin to wrb %p\n", wrb); - chunk_result = TCP_WBCOPYIN(wrb, cp, chunk_len, off); - ninfo("finished copyin to wrb %p\n", wrb); - if (blresult >= 0) - { - net_restorelock(count); - } + iob = net_ioballoc(true, IOBUSER_NET_TCP_WRITEBUFFER); + iob_free_chain(iob, IOBUSER_NET_TCP_WRITEBUFFER); } /* Dump I/O buffer chain */ @@ -1311,9 +1329,6 @@ ssize_t psock_tcp_send(FAR struct socket *psock, FAR const void *buf, return result; -errout_with_wrb: - tcp_wrbuffer_release(wrb); - errout_with_lock: net_unlock();