Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[LibOS, PAL] Support corking flags in socket related syscalls #936

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions common/include/linux_socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,13 @@ struct mmsghdr {
#define MSG_TRUNC 0x20
#define MSG_DONTWAIT 0x40
#define MSG_NOSIGNAL 0x4000
#define MSG_MORE 0x8000
#define MSG_CMSG_CLOEXEC 0x40000000

/* Option levels. */
#define SOL_SOCKET 1
#define SOL_TCP 6
#define SOL_UDP 17

/* Socket options. */
#define SO_REUSEADDR 2
Expand All @@ -86,6 +88,9 @@ struct mmsghdr {
#define TCP_NODELAY 1
#define TCP_CORK 3

/* UDP options. */
#define UDP_CORK 1

struct linger {
int l_onoff;
int l_linger;
Expand Down
4 changes: 3 additions & 1 deletion libos/include/libos_socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,11 @@ struct libos_sock_ops {
* \param addrlen The length of \p addr.
* \param force_nonblocking If `true` this request should not block. Otherwise just use
* whatever mode the handle is in.
* \param force_cork If `true` this request is corked. Otherwise just use
* whatever mode the handle is in.
*/
int (*send)(struct libos_handle* handle, struct iovec* iov, size_t iov_len, size_t* out_size,
void* addr, size_t addrlen, bool force_nonblocking);
void* addr, size_t addrlen, bool force_nonblocking, bool force_cork);

/*!
* \brief Receive continuous data into an array of buffers.
Expand Down
64 changes: 62 additions & 2 deletions libos/src/net/ip.c
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,31 @@ static int set_tcp_option(struct libos_handle* handle, int optname, void* optval
return pal_to_unix_errno(ret);
}

static int set_udp_option(struct libos_handle* handle, int optname, void* optval, size_t len) {
PAL_STREAM_ATTR attr;
int ret = PalStreamAttributesQueryByHandle(handle->info.sock.pal_handle, &attr);
if (ret < 0) {
return pal_to_unix_errno(ret);
}
assert(attr.handle_type == PAL_TYPE_SOCKET);

if (len < sizeof(int)) {
/* All currently supported options use `int`. */
return -EINVAL;
}

switch (optname) {
case UDP_CORK:
attr.socket.udp_cork = *(int*)optval;
break;
default:
return -ENOPROTOOPT;
}

ret = PalStreamAttributesSetByHandle(handle->info.sock.pal_handle, &attr);
return pal_to_unix_errno(ret);
}

static int set_ipv4_option(struct libos_handle* handle, int optname, void* optval, size_t len) {
__UNUSED(handle);
__UNUSED(optval);
Expand Down Expand Up @@ -483,6 +508,11 @@ static int setsockopt(struct libos_handle* handle, int level, int optname, void*
return -EOPNOTSUPP;
}
return set_tcp_option(handle, optname, optval, len);
case SOL_UDP:
if (sock->type != SOCK_DGRAM) {
return -EOPNOTSUPP;
}
return set_udp_option(handle, optname, optval, len);
default:
return -ENOPROTOOPT;
}
Expand Down Expand Up @@ -516,6 +546,31 @@ static int get_tcp_option(struct libos_handle* handle, int optname, void* optval
return 0;
}

static int get_udp_option(struct libos_handle* handle, int optname, void* optval, size_t* len) {
PAL_STREAM_ATTR attr;
int ret = PalStreamAttributesQueryByHandle(handle->info.sock.pal_handle, &attr);
if (ret < 0) {
return pal_to_unix_errno(ret);
}
assert(attr.handle_type == PAL_TYPE_SOCKET);

int val;
switch (optname) {
case UDP_CORK:
val = attr.socket.udp_cork;
break;
default:
return -ENOPROTOOPT;
}

if (*len > sizeof(val)) {
/* Cap the buffer size to the option size. */
*len = sizeof(val);
}
memcpy(optval, &val, *len);
return 0;
}

static int get_ipv6_option(struct libos_handle* handle, int optname, void* optval, size_t* len) {
PAL_STREAM_ATTR attr;
int ret = PalStreamAttributesQueryByHandle(handle->info.sock.pal_handle, &attr);
Expand Down Expand Up @@ -606,13 +661,18 @@ static int getsockopt(struct libos_handle* handle, int level, int optname, void*
return -EOPNOTSUPP;
}
return get_tcp_option(handle, optname, optval, len);
case SOL_UDP:
if (sock->type != SOCK_DGRAM) {
return -EOPNOTSUPP;
}
return get_udp_option(handle, optname, optval, len);
default:
return -EOPNOTSUPP;
}
}

static int send(struct libos_handle* handle, struct iovec* iov, size_t iov_len, size_t* out_size,
void* addr, size_t addrlen, bool force_nonblocking) {
void* addr, size_t addrlen, bool force_nonblocking, bool force_cork) {
assert(handle->type == TYPE_SOCK);

struct libos_sock_handle* sock = &handle->info.sock;
Expand Down Expand Up @@ -662,7 +722,7 @@ static int send(struct libos_handle* handle, struct iovec* iov, size_t iov_len,
}

int ret = PalSocketSend(sock->pal_handle, pal_iov, iov_len, out_size,
addr ? &pal_ip_addr : NULL, force_nonblocking);
addr ? &pal_ip_addr : NULL, force_nonblocking, force_cork);
ret = (ret == -PAL_ERROR_TOOLONG) ? -EMSGSIZE : pal_to_unix_errno(ret);
free(pal_iov);
return ret;
Expand Down
7 changes: 6 additions & 1 deletion libos/src/net/unix.c
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ static int maybe_force_nonblocking_wrapper(bool force_nonblocking, struct libos_
}

static int send(struct libos_handle* handle, struct iovec* iov, size_t iov_len, size_t* out_size,
void* addr, size_t addrlen, bool force_nonblocking) {
void* addr, size_t addrlen, bool force_nonblocking, bool force_cork) {
__UNUSED(addr);
__UNUSED(addrlen);

Expand All @@ -413,6 +413,11 @@ static int send(struct libos_handle* handle, struct iovec* iov, size_t iov_len,
BUG();
}

if (force_cork == true) {
/* MSG_MORE flag is not supported by UNIX domain sockets. */
BUG();
}

PAL_HANDLE pal_handle = __atomic_load_n(&handle->info.sock.pal_handle, __ATOMIC_ACQUIRE);
if (!pal_handle) {
return -ENOTCONN;
Expand Down
12 changes: 10 additions & 2 deletions libos/src/sys/libos_socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -613,13 +613,14 @@ ssize_t do_sendmsg(struct libos_handle* handle, struct iovec* iov, size_t iov_le
if (handle->type != TYPE_SOCK) {
return -ENOTSOCK;
}
if (!WITHIN_MASK(flags, MSG_NOSIGNAL | MSG_DONTWAIT)) {
if (!WITHIN_MASK(flags, MSG_NOSIGNAL | MSG_DONTWAIT | MSG_MORE)) {
return -EOPNOTSUPP;
}

/* Note this only indicates whether this operation was requested to be nonblocking. If it's
* `false`, but the handle is in nonblocking mode, this send won't block. */
bool force_nonblocking = flags & MSG_DONTWAIT;
bool force_cork = flags & MSG_MORE;
struct libos_sock_handle* sock = &handle->info.sock;

lock(&sock->lock);
Expand All @@ -632,6 +633,13 @@ ssize_t do_sendmsg(struct libos_handle* handle, struct iovec* iov, size_t iov_le
ret = -EPIPE;
}

if (!ret && force_cork) {
if (sock->domain != AF_INET && sock->domain != AF_INET6) {
log_warning("%s: MSG_MORE on non IPv4 or IPv6 sockets is not supported", __func__);
ret = -EOPNOTSUPP;
}
}

unlock(&sock->lock);

if (ret < 0) {
Expand All @@ -644,7 +652,7 @@ ssize_t do_sendmsg(struct libos_handle* handle, struct iovec* iov, size_t iov_le
}

size_t size = 0;
ret = sock->ops->send(handle, iov, iov_len, &size, addr, addrlen, force_nonblocking);
ret = sock->ops->send(handle, iov, iov_len, &size, addr, addrlen, force_nonblocking, force_cork);
maybe_epoll_et_trigger(handle, ret, /*in=*/false, !ret ? size < total_size : false);
if (!ret) {
ret = size;
Expand Down
20 changes: 20 additions & 0 deletions libos/test/regression/getsockopt.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <assert.h>
#include <errno.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
Expand Down Expand Up @@ -47,5 +48,24 @@ int main(int argc, char** argv) {
}

printf("getsockopt: Got TCP_NODELAY flag OK\n");

int fd2 = socket(PF_INET, SOCK_DGRAM, 0);
if (fd2 < 0) {
perror("socket failed");
return 1;
}
ret = getsockopt(fd2, SOL_UDP, UDP_CORK, (void*)&so_flags, &optlen);
if (ret < 0) {
perror("getsockopt(SOL_UDP, UDP_CORK) failed");
return 1;
}

if (optlen != sizeof(so_flags) || (so_flags != 0 && so_flags != 1)) {
fprintf(stderr, "getsockopt(SOL_UDP, UDP_CORK) failed\n");
return 1;
}

printf("getsockopt: Got UDP_CORK flag OK\n");

return 0;
}
1 change: 1 addition & 0 deletions libos/test/regression/test_libos.py
Original file line number Diff line number Diff line change
Expand Up @@ -1215,6 +1215,7 @@ def test_000_getsockopt(self):
stdout, _ = self.run_binary(['getsockopt'])
self.assertIn('getsockopt: Got socket type OK', stdout)
self.assertIn('getsockopt: Got TCP_NODELAY flag OK', stdout)
self.assertIn('getsockopt: Got UDP_CORK flag OK', stdout)

def test_010_epoll(self):
stdout, _ = self.run_binary(['epoll_test'])
Expand Down
5 changes: 4 additions & 1 deletion pal/include/pal/pal.h
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,7 @@ typedef struct _PAL_STREAM_ATTR {
bool tcp_cork;
bool tcp_nodelay;
bool ipv6_v6only;
bool udp_cork;
} socket;
};
} PAL_STREAM_ATTR;
Expand Down Expand Up @@ -596,13 +597,15 @@ int PalSocketConnect(PAL_HANDLE handle, struct pal_socket_addr* addr,
* \param addr Destination address. Can be NULL if the socket was connected.
* \param force_nonblocking If `true` this request should not block. Otherwise just use
* whatever mode the handle is in.
* \param force_cork If `true` this request is corked. Otherwise just use
* whatever mode the handle is in.
*
* \returns 0 on success, negative error code on failure.
*
* Data is sent atomically, i.e. data from two `PalSocketSend` calls will not be interleaved.
*/
int PalSocketSend(PAL_HANDLE handle, struct pal_iovec* iov, size_t iov_len, size_t* out_size,
struct pal_socket_addr* addr, bool force_nonblocking);
struct pal_socket_addr* addr, bool force_nonblocking, bool force_cork);

/*!
* \brief Receive data.
Expand Down
4 changes: 2 additions & 2 deletions pal/include/pal_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ struct socket_ops {
int (*connect)(PAL_HANDLE handle, struct pal_socket_addr* addr,
struct pal_socket_addr* out_local_addr);
int (*send)(PAL_HANDLE handle, struct pal_iovec* iov, size_t iov_len, size_t* out_size,
struct pal_socket_addr* addr, bool force_nonblocking);
struct pal_socket_addr* addr, bool force_nonblocking, bool force_cork);
int (*recv)(PAL_HANDLE handle, struct pal_iovec* iov, size_t iov_len, size_t* out_size,
struct pal_socket_addr* addr, bool force_nonblocking);
};
Expand Down Expand Up @@ -192,7 +192,7 @@ int _PalSocketAccept(PAL_HANDLE handle, pal_stream_options_t options, PAL_HANDLE
int _PalSocketConnect(PAL_HANDLE handle, struct pal_socket_addr* addr,
struct pal_socket_addr* out_local_addr);
int _PalSocketSend(PAL_HANDLE handle, struct pal_iovec* iov, size_t iov_len, size_t* out_size,
struct pal_socket_addr* addr, bool force_nonblocking);
struct pal_socket_addr* addr, bool force_nonblocking, bool force_cork);
int _PalSocketRecv(PAL_HANDLE handle, struct pal_iovec* iov, size_t iov_len, size_t* out_total_size,
struct pal_socket_addr* addr, bool force_nonblocking);

Expand Down
2 changes: 1 addition & 1 deletion pal/regression/send_handle.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ static void write_all(PAL_HANDLE handle, int type, char* buf, size_t size) {
.iov_len = this_size,
};
CHECK(PalSocketSend(handle, &iov, 1, &this_size, /*addr=*/NULL,
/*force_nonblocking=*/false));
/*force_nonblocking=*/false, /*force_cork=*/false));
break;
default:
BUG();
Expand Down
1 change: 1 addition & 0 deletions pal/src/host/linux-sgx/pal_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ typedef struct {
bool tcp_cork;
bool tcp_nodelay;
bool ipv6_v6only;
bool udp_cork;
} sock;

struct {
Expand Down
29 changes: 23 additions & 6 deletions pal/src/host/linux-sgx/pal_sockets.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ static PAL_HANDLE create_sock_handle(int fd, enum pal_socket_domain domain,
handle->sock.tcp_cork = false;
handle->sock.tcp_nodelay = false;
handle->sock.ipv6_v6only = false;
handle->sock.udp_cork = false;

return handle;
}
Expand Down Expand Up @@ -295,6 +296,7 @@ static int attrquerybyhdl(PAL_HANDLE handle, PAL_STREAM_ATTR* attr) {
attr->socket.tcp_cork = handle->sock.tcp_cork;
attr->socket.tcp_nodelay = handle->sock.tcp_nodelay;
attr->socket.ipv6_v6only = handle->sock.ipv6_v6only;
attr->socket.udp_cork = handle->sock.udp_cork;

return 0;
};
Expand Down Expand Up @@ -451,11 +453,25 @@ static int attrsetbyhdl_tcp(PAL_HANDLE handle, PAL_STREAM_ATTR* attr) {
static int attrsetbyhdl_udp(PAL_HANDLE handle, PAL_STREAM_ATTR* attr) {
assert(handle->sock.type == PAL_SOCKET_UDP);

return attrsetbyhdl_common(handle, attr);
int ret = attrsetbyhdl_common(handle, attr);
if (ret < 0) {
return ret;
}

if (attr->socket.udp_cork != handle->sock.udp_cork) {
int val = attr->socket.udp_cork;
int ret = ocall_setsockopt(handle->sock.fd, SOL_UDP, UDP_CORK, &val, sizeof(val));
if (ret < 0) {
return unix_to_pal_error(ret);
}
handle->sock.udp_cork = attr->socket.udp_cork;
}

return 0;
}

static int send(PAL_HANDLE handle, struct pal_iovec* pal_iov, size_t iov_len, size_t* out_size,
struct pal_socket_addr* addr, bool force_nonblocking) {
struct pal_socket_addr* addr, bool force_nonblocking, bool force_cork) {
assert(handle->hdr.type == PAL_TYPE_SOCKET);

struct sockaddr_storage sa_storage;
Expand All @@ -476,8 +492,8 @@ static int send(PAL_HANDLE handle, struct pal_iovec* pal_iov, size_t iov_len, si
iov[i].iov_base = pal_iov[i].iov_base;
iov[i].iov_len = pal_iov[i].iov_len;
}

unsigned int flags = force_nonblocking ? MSG_DONTWAIT : 0;
unsigned int flags = (force_nonblocking ? MSG_DONTWAIT : 0) |
(force_cork ? MSG_MORE : 0);
ssize_t ret = ocall_send(handle->sock.fd, iov, iov_len, addr ? &sa_storage : NULL,
linux_addrlen, /*control=*/NULL, /*controllen=*/0, flags);
free(iov);
Expand Down Expand Up @@ -634,11 +650,12 @@ int _PalSocketConnect(PAL_HANDLE handle, struct pal_socket_addr* addr,
}

int _PalSocketSend(PAL_HANDLE handle, struct pal_iovec* iov, size_t iov_len, size_t* out_size,
struct pal_socket_addr* addr, bool force_nonblocking) {
struct pal_socket_addr* addr, bool force_nonblocking, bool force_cork) {
if (!handle->sock.ops->send) {
return -PAL_ERROR_NOTSUPPORT;
}
return handle->sock.ops->send(handle, iov, iov_len, out_size, addr, force_nonblocking);
return handle->sock.ops->send(handle, iov, iov_len, out_size, addr, force_nonblocking,
force_cork);
}

int _PalSocketRecv(PAL_HANDLE handle, struct pal_iovec* iov, size_t iov_len, size_t* out_total_size,
Expand Down
1 change: 1 addition & 0 deletions pal/src/host/linux/pal_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ typedef struct {
bool tcp_cork;
bool tcp_nodelay;
bool ipv6_v6only;
bool udp_cork;
} sock;

struct {
Expand Down
Loading