net: sockets: Implement MSG_WAITALL recv flag

Implement MSG_WAITALL flag for stream sockets. Setting this flag on
`recv()` call will make it wait until the requested amount of data is
received.

In case both, MSG_WAITALL all is set and SO_RCVTIMEO option configured
on a socket, follow the Linux behavior, i. e. when the requested amount
of data is not received until the timeout expires, return the data
received so far w/o an error.

Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
This commit is contained in:
Robert Lubos 2021-03-04 15:28:30 +01:00 committed by Anas Nashif
parent a81765bbe4
commit d47e803976
3 changed files with 35 additions and 8 deletions

View file

@ -57,6 +57,8 @@ struct zsock_pollfd {
#define ZSOCK_MSG_PEEK 0x02
/** zsock_recv/zsock_send: Override operation to non-blocking */
#define ZSOCK_MSG_DONTWAIT 0x40
/** zsock_recv: block until the full amount of data can be returned */
#define ZSOCK_MSG_WAITALL 0x100
/* Well-known values, e.g. from Linux man 2 shutdown:
* "The constants SHUT_RD, SHUT_WR, SHUT_RDWR have the value 0, 1, 2,
@ -772,6 +774,7 @@ static inline char *inet_ntop(sa_family_t family, const void *src, char *dst,
#define MSG_PEEK ZSOCK_MSG_PEEK
#define MSG_DONTWAIT ZSOCK_MSG_DONTWAIT
#define MSG_WAITALL ZSOCK_MSG_WAITALL
#define SHUT_RD ZSOCK_SHUT_RD
#define SHUT_WR ZSOCK_SHUT_WR

View file

@ -29,6 +29,7 @@ static inline int socketpair(int family, int type, int proto, int sv[2])
#define MSG_PEEK ZSOCK_MSG_PEEK
#define MSG_DONTWAIT ZSOCK_MSG_DONTWAIT
#define MSG_WAITALL ZSOCK_MSG_WAITALL
static inline int shutdown(int sock, int how)
{

View file

@ -1028,6 +1028,8 @@ static inline ssize_t zsock_recv_stream(struct net_context *ctx,
size_t recv_len = 0;
struct net_pkt_cursor backup;
int res;
uint64_t end;
const bool waitall = flags & ZSOCK_MSG_WAITALL;
if (!net_context_is_used(ctx)) {
errno = EBADF;
@ -1045,9 +1047,12 @@ static inline ssize_t zsock_recv_stream(struct net_context *ctx,
net_context_get_option(ctx, NET_OPT_RCVTIMEO, &timeout, NULL);
}
end = z_timeout_end_calc(timeout);
do {
struct net_pkt *pkt;
size_t data_len;
size_t data_len, read_len;
bool release_pkt = true;
if (sock_is_eof(ctx)) {
return 0;
@ -1066,7 +1071,10 @@ static inline ssize_t zsock_recv_stream(struct net_context *ctx,
* due to connection closure by peer.
*/
NET_DBG("NULL return from fifo");
if (sock_is_eof(ctx)) {
if (waitall && (recv_len > 0)) {
return recv_len;
} else if (sock_is_eof(ctx)) {
return 0;
} else {
errno = EAGAIN;
@ -1077,19 +1085,22 @@ static inline ssize_t zsock_recv_stream(struct net_context *ctx,
net_pkt_cursor_backup(pkt, &backup);
data_len = net_pkt_remaining_data(pkt);
recv_len = data_len;
if (recv_len > max_len) {
recv_len = max_len;
read_len = data_len;
if (recv_len + read_len > max_len) {
read_len = max_len - recv_len;
release_pkt = false;
}
/* Actually copy data to application buffer */
if (net_pkt_read(pkt, buf, recv_len)) {
if (net_pkt_read(pkt, (uint8_t *)buf + recv_len, read_len)) {
errno = ENOBUFS;
return -1;
}
recv_len += read_len;
if (!(flags & ZSOCK_MSG_PEEK)) {
if (recv_len == data_len) {
if (release_pkt) {
/* Finished processing head pkt in
* the fifo. Drop it from there.
*/
@ -1108,7 +1119,19 @@ static inline ssize_t zsock_recv_stream(struct net_context *ctx,
} else {
net_pkt_cursor_restore(pkt, &backup);
}
} while (recv_len == 0);
/* Update the timeout value in case loop is repeated. */
if (!K_TIMEOUT_EQ(timeout, K_NO_WAIT) &&
!K_TIMEOUT_EQ(timeout, K_FOREVER)) {
int64_t remaining = end - z_tick_get();
if (remaining <= 0) {
timeout = K_NO_WAIT;
} else {
timeout = Z_TIMEOUT_TICKS(remaining);
}
}
} while ((recv_len == 0) || (waitall && (recv_len < max_len)));
if (!(flags & ZSOCK_MSG_PEEK)) {
net_context_update_recv_wnd(ctx, recv_len);