diff --git a/base-linux/include/base/native_types.h b/base-linux/include/base/native_types.h index a39746e7e..fdb53a8e0 100644 --- a/base-linux/include/base/native_types.h +++ b/base-linux/include/base/native_types.h @@ -64,6 +64,7 @@ namespace Genode { /** * Native thread contains more thread-local data than just the ID * + * FIXME doc * A thread needs two sockets as it may be a server that depends on another * service during request processing. If the server socket would be used for * the client call, the server thread may be unblocked by further requests @@ -73,8 +74,7 @@ namespace Genode { */ struct Native_thread : Native_thread_id { - int client; /* socket used as IPC client */ - int server; /* socket used as IPC server */ + int socket; /* server-entrypoint socket */ /** * Opaque pointer to additional thread-specific meta data @@ -85,7 +85,7 @@ namespace Genode { */ Thread_meta_data *meta_data; - Native_thread() : client(-1), server(-1), meta_data(0) { } + Native_thread() : socket(-1), meta_data(0) { } }; inline bool operator == (Native_thread_id t1, Native_thread_id t2) { @@ -107,7 +107,46 @@ namespace Genode { typedef struct { } Native_utcb; typedef Native_capability_tpl Native_capability; - typedef int Native_connection_state; /* socket descriptor */ + + class Native_connection_state + { + public: + + struct Reply_socket_error { }; + + private: + + int _socket; /* server-entrypoint socket */ + int _reply_socket; /* reply socket */ + + public: + + Native_connection_state() : _socket(-1), _reply_socket(-1) { } + + void socket(int socket) { _socket = socket; } + int socket() const { return _socket; } + + /* + * FIXME Check for unsupported usage pattern: Reply sockets should + * only be set once and read once. + */ + + void reply_socket(int socket) + { + if (_reply_socket != -1) throw Reply_socket_error(); + + _reply_socket = socket; + } + + int reply_socket() + { + if (_reply_socket == -1) throw Reply_socket_error(); + + int s = _reply_socket; + _reply_socket = -1; + return s; + } + }; struct Native_config { @@ -125,7 +164,6 @@ namespace Genode { */ static addr_t context_virtual_size() { return 0x00100000UL; } }; - } #endif /* _INCLUDE__BASE__NATIVE_TYPES_H_ */ diff --git a/base-linux/src/base/ipc/ipc.cc b/base-linux/src/base/ipc/ipc.cc index b21c78843..61673358b 100644 --- a/base-linux/src/base/ipc/ipc.cc +++ b/base-linux/src/base/ipc/ipc.cc @@ -35,6 +35,7 @@ /* Linux includes */ #include +#include #include #include @@ -93,7 +94,7 @@ void Ipc_istream::_wait() Ipc_istream::Ipc_istream(Msgbuf_base *rcv_msg) : Ipc_unmarshaller(rcv_msg->buf, rcv_msg->size()), Native_capability(lx_gettid(), 0), - _rcv_msg(rcv_msg), _rcv_cs(-1) + _rcv_msg(rcv_msg) { } @@ -122,10 +123,9 @@ void Ipc_client::_prepare_next_call() void Ipc_client::_call() { if (Ipc_ostream::_dst.valid()) { - lx_send_to(_rcv_cs, Ipc_ostream::_dst.dst(), "server", - _snd_msg->buf, _write_offset); - - lx_recv_from(_rcv_cs, _rcv_msg->buf, _rcv_msg->size()); + lx_call(Ipc_ostream::_dst.dst(), + _snd_msg->buf, _write_offset, + _rcv_msg->buf, _rcv_msg->size()); } _prepare_next_call(); } @@ -135,8 +135,6 @@ Ipc_client::Ipc_client(Native_capability const &srv, Msgbuf_base *snd_msg, Msgbuf_base *rcv_msg) : Ipc_istream(rcv_msg), Ipc_ostream(srv, snd_msg), _result(0) { - _rcv_cs = lx_client_socket(Thread_base::myself()); - _prepare_next_call(); } @@ -171,7 +169,7 @@ void Ipc_server::_wait() { /* wait for new server request */ try { - lx_recv_from(_rcv_cs, _rcv_msg->buf, _rcv_msg->size()); + lx_wait(_rcv_cs, _rcv_msg->buf, _rcv_msg->size()); } catch (Blocking_canceled) { } /* now we have a request to reply, determine reply destination */ @@ -183,8 +181,7 @@ void Ipc_server::_wait() void Ipc_server::_reply() { try { - lx_send_to(_rcv_cs, Ipc_ostream::_dst.dst(), "client", - _snd_msg->buf, _write_offset); + lx_reply(_rcv_cs, _snd_msg->buf, _write_offset); } catch (Ipc_error) { } _prepare_next_reply_wait(); @@ -195,8 +192,7 @@ void Ipc_server::_reply_wait() { /* when first called, there was no request yet */ if (_reply_needed) - lx_send_to(_rcv_cs, Ipc_ostream::_dst.dst(), "client", - _snd_msg->buf, _write_offset); + lx_reply(_rcv_cs, _snd_msg->buf, _write_offset); _wait(); } @@ -206,7 +202,7 @@ Ipc_server::Ipc_server(Msgbuf_base *snd_msg, Msgbuf_base *rcv_msg) : Ipc_istream(rcv_msg), Ipc_ostream(Native_capability(), snd_msg), _reply_needed(false) { - _rcv_cs = lx_server_socket(Thread_base::myself()); + _rcv_cs.socket(lx_server_socket(Thread_base::myself())); _prepare_next_reply_wait(); } diff --git a/base-linux/src/platform/linux_socket.h b/base-linux/src/platform/linux_socket.h index c8d3d7940..ec50052be 100644 --- a/base-linux/src/platform/linux_socket.h +++ b/base-linux/src/platform/linux_socket.h @@ -45,63 +45,100 @@ extern "C" int raw_write_str(const char *str); /** - * Utility: Create socket address for thread ID and role (client/server) + * Utility: Create socket address for server entrypoint atthread ID */ -static void lx_create_sockaddr(sockaddr_un *addr, long thread_id, char const *role) +static void lx_create_server_addr(sockaddr_un *addr, long thread_id) { addr->sun_family = AF_UNIX; - Genode::snprintf(addr->sun_path, sizeof(addr->sun_path), "%s/ep-%ld-%s", - lx_rpath(), thread_id, role); + Genode::snprintf(addr->sun_path, sizeof(addr->sun_path), "%s/ep-%ld", + lx_rpath(), thread_id); } -/** - * Utility: Create a socket descriptor and file for given thread and role - */ -static int lx_create_socket(long thread_id, char const *role) -{ - int sd = lx_socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (sd < 0) return -1; +namespace { - sockaddr_un addr; - lx_create_sockaddr(&addr, thread_id, role); + /** + * Message object encapsulating data for sendmsg/recvmsg + */ + struct Message + { + private: - /* make sure bind succeeds */ - lx_unlink(addr.sun_path); + msghdr _msg; + sockaddr_un _addr; + iovec _iovec; + char _cmsg_buf[CMSG_SPACE(sizeof(int))]; - if (lx_bind(sd, (sockaddr *)&addr, sizeof(addr)) < 0) - return -2; + public: - return sd; -} + Message(long server_thread_id = -1) + { + memset(&_msg, 0, sizeof(_msg)); + if (server_thread_id != -1) { + /* initialize receiver */ + lx_create_server_addr(&_addr, server_thread_id); -/** - * Utility: Unlink socket file and close descriptor - * - * XXX Currently, socket destruction is missing. The client socket could be - * used from multiple Ipc_client objects. A safe destruction would need - * reference counting. - */ -//static void lx_destroy_socket(int sd, long thread_id, char const *role) -//{ -// sockaddr_un addr; -// lx_create_sockaddr(&addr, thread_id, role); -// -// lx_unlink(addr.sun_path); -// lx_close(sd); -//} + _msg.msg_name = &_addr; + _msg.msg_namelen = sizeof(_addr); + } + } + msghdr * msg() { return &_msg; } -/** - * Get client-socket descriptor for main thread - */ -static int lx_main_client_socket() -{ - static int sd = lx_create_socket(lx_gettid(), "client"); + void buffer(void *buffer, size_t buffer_len) + { + /* initialize iovec */ + _msg.msg_iov = &_iovec; + _msg.msg_iovlen = 1; - return sd; -} + _iovec.iov_base = buffer; + _iovec.iov_len = buffer_len; + } + + /** + * Prepare slot for socket sending/reception + * + * Note, if this function is not called sockets are not accepted on + * 'recvmsg' and, therefore, do not occupy local file descriptors. + */ + void prepare_reply_socket_slot() + { + /* initialize control message */ + struct cmsghdr *cmsg; + + _msg.msg_control = _cmsg_buf; + _msg.msg_controllen = sizeof(_cmsg_buf); /* buffer space available */ + _msg.msg_flags |= MSG_CMSG_CLOEXEC; + cmsg = CMSG_FIRSTHDR(&_msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + _msg.msg_controllen = cmsg->cmsg_len; /* actual cmsg length */ + } + + void reply_socket(int sd) + { + if (!_msg.msg_control) { + PRAW("reply-socket slot not prepared"); + throw Genode::Ipc_error(); + } + + *(int *)CMSG_DATA((cmsghdr *)_cmsg_buf) = sd; + } + + int reply_socket() const + { + if (!_msg.msg_control) { + PRAW("reply-socket slot not prepared"); + throw Genode::Ipc_error(); + } + + return *(int *)CMSG_DATA((cmsghdr *)_cmsg_buf); + } + }; + +} /* unnamed namespace */ /** @@ -115,59 +152,117 @@ static int lx_server_socket(Genode::Thread_base *thread) if (!thread) return lx_socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (thread->tid().server == -1) - thread->tid().server = lx_create_socket(thread->tid().tid, "server"); - return thread->tid().server; + if (thread->tid().socket == -1) { + int sd = lx_socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (sd < 0) return -1; + + sockaddr_un addr; + lx_create_server_addr(&addr, thread->tid().tid); + + /* make sure bind succeeds */ + lx_unlink(addr.sun_path); + + if (lx_bind(sd, (sockaddr *)&addr, sizeof(addr)) < 0) + return -2; + + thread->tid().socket = sd; + } + + return thread->tid().socket; } /** - * Utility: Get client socket for given thread + * Utility: Send request to server and wait for reply */ -static int lx_client_socket(Genode::Thread_base *thread) +static void lx_call(long thread_id, + void *send_buf, Genode::size_t send_buf_len, + void *recv_buf, Genode::size_t recv_buf_len) { - if (!thread) return lx_main_client_socket(); + int ret; - if (thread->tid().client == -1) - thread->tid().client = lx_create_socket(thread->tid().tid, "client"); - return thread->tid().client; -} + Message send_msg(thread_id); + /* create reply channel */ + enum { LOCAL_SOCKET = 0, REMOTE_SOCKET = 1 }; + int reply_channel[2]; -/** - * Utility: Send message to thread via given socket descriptor - */ -static void lx_send_to(int sd, long thread_id, char const *target_role, - void *msg, Genode::size_t msg_len) -{ - sockaddr_un addr; - lx_create_sockaddr(&addr, thread_id, target_role); - - int res = lx_sendto(sd, msg, msg_len, 0, (sockaddr *)&addr, sizeof(addr)); - if (res < 0) { - PRAW("Send error: %d with %s in %d", res, addr.sun_path, lx_gettid()); - wait_for_continue(); + ret = lx_socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0, reply_channel); + if (ret < 0) { + PRAW("lx_socketpair failed with %d", ret); throw Genode::Ipc_error(); } + + send_msg.prepare_reply_socket_slot(); + send_msg.reply_socket(reply_channel[REMOTE_SOCKET]); + send_msg.buffer(send_buf, send_buf_len); + + ret = lx_sendmsg(reply_channel[LOCAL_SOCKET], send_msg.msg(), 0); + if (ret < 0) { + PRAW("lx_sendmsg failed with %d in call()", ret); + throw Genode::Ipc_error(); + } + + Message recv_msg; + + recv_msg.buffer(recv_buf, recv_buf_len); + + ret = lx_recvmsg(reply_channel[LOCAL_SOCKET], recv_msg.msg(), 0); + if (ret < 0) { + PRAW("lx_recvmsg failed with %d in call()", ret); + throw Genode::Ipc_error(); + } + + /* destroy reply channel */ + lx_close(reply_channel[LOCAL_SOCKET]); + lx_close(reply_channel[REMOTE_SOCKET]); } /** - * Utility: Receive message via given socket descriptor + * Utility: Wait for request from client */ -static void lx_recv_from(int sd, void *buf, Genode::size_t buf_len) +static void lx_wait(Genode::Native_connection_state &cs, + void *buf, Genode::size_t buf_len) { - socklen_t fromlen; - int res = lx_recvfrom(sd, buf, buf_len, 0, 0, &fromlen); - if (res < 0) { - if ((-res) == EINTR) - throw Genode::Blocking_canceled(); - else { - PRAW("Recv error: %d in %d", res, lx_gettid()); - wait_for_continue(); - throw Genode::Ipc_error(); - } + int ret; + + Message msg; + + msg.prepare_reply_socket_slot(); + msg.buffer(buf, buf_len); + + ret = lx_recvmsg(cs.socket(), msg.msg(), 0); + if (ret < 0) { + PRAW("lx_recvmsg failed with %d in wait()", ret); + throw Genode::Ipc_error(); } + + /* remember socket descriptor for reply */ + cs.reply_socket(msg.reply_socket()); +} + + +/** + * Utility: Send reply to client + */ +static void lx_reply(Genode::Native_connection_state &cs, + void *buf, Genode::size_t buf_len) +{ + int ret; + int sd = cs.reply_socket(); + + Message msg; + + msg.buffer(buf, buf_len); + + ret = lx_sendmsg(sd, msg.msg(), 0); + if (ret < 0) { + PRAW("lx_sendmsg failed with %d in reply()", ret); + throw Genode::Ipc_error(); + } + + lx_close(sd); } #endif /* _PLATFORM__LINUX_SOCKET_H_ */ diff --git a/base-linux/src/platform/linux_syscalls.h b/base-linux/src/platform/linux_syscalls.h index 09c938083..65a3b006e 100644 --- a/base-linux/src/platform/linux_syscalls.h +++ b/base-linux/src/platform/linux_syscalls.h @@ -43,41 +43,38 @@ #include -/***************************************** - ** Functions used by the IPC framework ** - *****************************************/ - -#include - extern "C" long lx_syscall(int number, ...); extern "C" int lx_clone(int (*fn)(void *), void *child_stack, int flags, void *arg); -inline Genode::uint16_t lx_bswap16(Genode::uint16_t x) +/***************************************** + ** General syscalls used by base-linux ** + *****************************************/ + +inline int lx_write(int fd, const void *buf, Genode::size_t count) { - char v[2] = { - (char)((x & 0xff00) >> 8), - (char)((x & 0x00ff) >> 0), - }; - return *(Genode::uint16_t *)v; + return lx_syscall(SYS_write, fd, buf, count); } -inline Genode::uint32_t lx_bswap32(Genode::uint32_t x) +inline int lx_close(int fd) { - char v[4] = { - (char)((x & 0xff000000) >> 24), - (char)((x & 0x00ff0000) >> 16), - (char)((x & 0x0000ff00) >> 8), - (char)((x & 0x000000ff) >> 0), - }; - return *(Genode::uint32_t *)v; + return lx_syscall(SYS_close, fd); } -#define lx_htonl(x) lx_bswap32(x) -#define lx_htons(x) lx_bswap16(x) -#define lx_ntohs(x) lx_bswap16(x) + +inline int lx_unlink(const char *fname) +{ + return lx_syscall(SYS_unlink, fname); +} + + +/***************************************** + ** Functions used by the IPC framework ** + *****************************************/ + +#include #ifdef SYS_socketcall @@ -95,11 +92,10 @@ inline int lx_socket(int domain, int type, int protocol) } -inline int lx_connect(int sockfd, const struct sockaddr *serv_addr, - socklen_t addrlen) +inline int lx_socketpair(int domain, int type, int protocol, int sd[2]) { - unsigned long args[3] = { sockfd, (unsigned long)serv_addr, addrlen }; - return lx_socketcall(SYS_CONNECT, args); + unsigned long args[4] = { domain, type, protocol, (unsigned long)sd }; + return lx_socketcall(SYS_SOCKETPAIR, args); } @@ -111,28 +107,17 @@ inline int lx_bind(int sockfd, const struct sockaddr *addr, } -inline int lx_getsockname(int s, struct sockaddr *name, socklen_t *namelen) +inline int lx_sendmsg(int sockfd, const struct msghdr *msg, int flags) { - unsigned long args[3] = { s, (unsigned long)name, (unsigned long)namelen }; - return lx_socketcall(SYS_GETSOCKNAME, args); + unsigned long args[3] = { sockfd, (unsigned long)msg, flags }; + return lx_socketcall(SYS_SENDMSG, args); } -inline ssize_t lx_recvfrom(int s, void *buf, Genode::size_t len, int flags, - struct sockaddr *from, socklen_t *from_len) +inline int lx_recvmsg(int sockfd, struct msghdr *msg, int flags) { - unsigned long args[6] = { s, (unsigned long)buf, len, flags, - (unsigned long)from, (unsigned long)from_len }; - return lx_socketcall(SYS_RECVFROM, args); -} - - -inline ssize_t lx_sendto(int s, void *buf, Genode::size_t len, int flags, - struct sockaddr *to, socklen_t to_len) -{ - unsigned long args[6] = { s, (unsigned long)buf, len, flags, - (unsigned long)to, (unsigned long)to_len }; - return lx_socketcall(SYS_SENDTO, args); + unsigned long args[3] = { sockfd, (unsigned long)msg, flags }; + return lx_socketcall(SYS_RECVMSG, args); } #else @@ -142,55 +127,11 @@ inline int lx_socket(int domain, int type, int protocol) return lx_syscall(SYS_socket, domain, type, protocol); } - -inline int lx_connect(int sockfd, const struct sockaddr *serv_addr, - socklen_t addrlen) -{ - return lx_syscall(SYS_connect, sockfd, serv_addr, addrlen); -} - - -inline int lx_bind(int sockfd, const struct sockaddr *addr, - socklen_t addrlen) -{ - return lx_syscall(SYS_bind, sockfd, addr, addrlen); -} - - -inline int lx_getsockname(int s, struct sockaddr *name, socklen_t *namelen) -{ - return lx_syscall(SYS_getsockname, s, name, namelen); -} - - -inline ssize_t lx_recvfrom(int s, void *buf, Genode::size_t len, int flags, - struct sockaddr *from, socklen_t *from_len) -{ - return lx_syscall(SYS_recvfrom, s, buf, len, flags, from, from_len); -} - - -inline ssize_t lx_sendto(int s, void *buf, Genode::size_t len, int flags, - struct sockaddr *to, socklen_t to_len) -{ - return lx_syscall(SYS_sendto, s, buf, len, flags, to, to_len); -} +/* TODO add missing socket system calls */ #endif /* SYS_socketcall */ -inline int lx_write(int fd, const void *buf, Genode::size_t count) -{ - return lx_syscall(SYS_write, fd, buf, count); -} - - -inline int lx_close(int fd) -{ - return lx_syscall(SYS_close, fd); -} - - /******************************************* ** Functions used by the process library ** *******************************************/ @@ -290,11 +231,6 @@ inline int lx_ftruncate(int fd, unsigned long length) } -inline int lx_unlink(const char *fname) -{ - return lx_syscall(SYS_unlink, fname); -} - /******************************************************* ** Functions used by core's rom-session support code ** *******************************************************/ @@ -308,6 +244,7 @@ inline int lx_stat(const char *path, struct stat64 *buf) #endif } + /*********************************************************************** ** Functions used by thread lib and core's cancel-blocking mechanism ** ***********************************************************************/