#!python3 # # Cforall Version 1.0.0 Copyright (C) 2020 University of Waterloo # # The contents of this file are covered under the licence agreement in the # file "LICENCE" distributed with Cforall. # # call.cfa.in -- Python script to generate io/call.cfa # # Author : Thierry Delisle # Created On : Fri Sep 11 12:41:16 2020 # Last Modified By : # Last Modified On : # Update Count : # Header = """// // Cforall Version 1.0.0 Copyright (C) 2020 University of Waterloo // // The contents of this file are covered under the licence agreement in the // file "LICENCE" distributed with Cforall. // // call.cfa -- Api for cforall // // Author : Generated from call.cfa.in // Created On : {} // """ Prelude = """#define __cforall_thread__ #include "bits/defs.hfa" #include "kernel.hfa" #include "io/types.hfa" //============================================================================================= // I/O uring backend //============================================================================================= #if defined(CFA_HAVE_LINUX_IO_URING_H) #include #include #include #include #include "kernel/fwd.hfa" static const __u8 REGULAR_FLAGS = 0 #if defined(CFA_HAVE_IOSQE_FIXED_FILE) | IOSQE_FIXED_FILE #endif #if defined(CFA_HAVE_IOSQE_IO_DRAIN) | IOSQE_IO_DRAIN #endif #if defined(CFA_HAVE_IOSQE_ASYNC) | IOSQE_ASYNC #endif ; static const __u32 LINK_FLAGS = 0 #if defined(CFA_HAVE_IOSQE_IO_LINK) | IOSQE_IO_LINK #endif #if defined(CFA_HAVE_IOSQE_IO_HARDLINK) | IOSQE_IO_HARDLINK #endif ; static const __u32 SPLICE_FLAGS = 0 #if defined(CFA_HAVE_SPLICE_F_FD_IN_FIXED) | SPLICE_F_FD_IN_FIXED #endif ; extern [* struct io_uring_sqe, __u32] __submit_alloc( struct __io_data & ring, __u64 data ); extern void __submit( struct io_context * ctx, __u32 idx ) __attribute__((nonnull (1))); static inline io_context * __get_io_context( void ) { cluster * cltr = active_cluster(); /* paranoid */ verifyf( cltr, "No active cluster for io operation\\n"); assertf( cltr->io.cnt > 0, "Cluster %p has no default io contexts and no context was specified\\n", cltr ); /* paranoid */ verifyf( cltr->io.ctxs, "default io contexts for cluster %p are missing\\n", cltr); return &cltr->io.ctxs[ thread_rand() % cltr->io.cnt ]; } #endif //============================================================================================= // I/O Forwards //============================================================================================= #include // Some forward declarations #include #include extern "C" { #include #include #include #if defined(CFA_HAVE_PREADV2) struct iovec; extern ssize_t preadv2 (int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags); #endif #if defined(CFA_HAVE_PWRITEV2) struct iovec; extern ssize_t pwritev2(int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags); #endif extern int fsync(int fd); #if __OFF_T_MATCHES_OFF64_T typedef __off64_t off_t; #else typedef __off_t off_t; #endif typedef __off64_t off64_t; extern int sync_file_range(int fd, off64_t offset, off64_t nbytes, unsigned int flags); struct msghdr; struct sockaddr; extern ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); extern ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags); extern ssize_t send(int sockfd, const void *buf, size_t len, int flags); extern ssize_t recv(int sockfd, void *buf, size_t len, int flags); extern int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags); extern int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); extern int fallocate(int fd, int mode, off_t offset, off_t len); extern int posix_fadvise(int fd, off_t offset, off_t len, int advice); extern int madvise(void *addr, size_t length, int advice); extern int openat(int dirfd, const char *pathname, int flags, mode_t mode); extern int close(int fd); extern ssize_t read (int fd, void *buf, size_t count); struct epoll_event; extern int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); extern ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags); extern ssize_t tee(int fd_in, int fd_out, size_t len, unsigned int flags); } //============================================================================================= // I/O Interface //============================================================================================= """ print(Header.format("A Date")) print(Prelude) import re import sys class Call: def __init__(self, op, signature, body, define=None): sig = re.search("(.*) (.*)\((.*)\)", signature) if not sig: print("OP '{}' has invalid signature {}".format(op, signature), file=sys.stderr) sys.exit(1) self.op = op self.ret = sig.group(1) self.name = sig.group(2) self.params = sig.group(3) self.define = define self.body = "" accepted_keys = [ 'ioprio', 'fd', 'off', 'addr2','addr', 'splice_off_in','len', 'rw_flags', 'fsync_flags', 'poll_events', 'poll32_events', 'sync_range_flags', 'msg_flags', 'timeout_flags', 'accept_flags', 'cancel_flags', 'open_flags', 'statx_flags', 'fadvise_advice', 'splice_flags', 'buf_index' ,'buf_group' 'personality', 'splice_fd_in' ] for k, v in body.items(): if not k in accepted_keys: print("OP '{}' has invalid body kew {}".format(op, k), file=sys.stderr) sys.exit(1) self.body += "\n sqe->{key} = {value};".format(key=k, value=v) def args(self): param_a = self.params.split(',') args_a = [p.replace('*', ' ').split()[-1] for p in param_a] for a in args_a: if '*' in a: print("OP '{}' has invalid * in argument {}".format(self.op, a), file=sys.stderr) sys.exit(1) return ', '.join(args_a) AsyncTemplate = """inline void async_{name}(io_future_t & future, {params}, int submit_flags, io_cancellation * cancellation, io_context * context) {{ #if !defined(CFA_HAVE_LINUX_IO_URING_H) || !defined(CFA_HAVE_IORING_OP_{op}) ssize_t res = {name}({args}); if (res >= 0) {{ fulfil(future, res); }} else {{ fulfil(future, -errno); }} #else // we don't support LINK yet if( 0 != (submit_flags & LINK_FLAGS) ) {{ errno = ENOTSUP; return -1; }} if( !context ) {{ context = __get_io_context(); }} if(cancellation) {{ cancellation->target = (__u64)(uintptr_t)&future; }} __u8 sflags = REGULAR_FLAGS & submit_flags; struct __io_data & ring = *context->thrd.ring; __u32 idx; struct io_uring_sqe * sqe; [sqe, idx] = __submit_alloc( ring, (__u64)(uintptr_t)&future ); sqe->__pad2[0] = sqe->__pad2[1] = sqe->__pad2[2] = 0; sqe->opcode = IORING_OP_{op}; sqe->flags = sflags;{body} verify( sqe->user_data == (__u64)(uintptr_t)&future ); __submit( context, idx ); #endif }}""" SyncTemplate = """{ret} cfa_{name}({params}, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context) {{ if( timeout >= 0 ) {{ errno = ENOTSUP; return -1; }} io_future_t future; async_{name}( future, {args}, submit_flags, cancellation, context ); wait( future ); if( future.result < 0 ) {{ errno = -future.result; return -1; }} return future.result; }}""" calls = [ # CFA_HAVE_IORING_OP_READV Call('READV', 'ssize_t preadv2(int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags)', { 'fd' : 'fd', 'off' : 'offset', 'addr': '(__u64)iov', 'len' : 'iovcnt', }, define = 'CFA_HAVE_PREADV2'), # CFA_HAVE_IORING_OP_WRITEV Call('WRITEV', 'ssize_t pwritev2(int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags)', { 'fd' : 'fd', 'off' : 'offset', 'addr': '(__u64)iov', 'len' : 'iovcnt' }, define = 'CFA_HAVE_PWRITEV2'), # CFA_HAVE_IORING_OP_FSYNC Call('FSYNC', 'int fsync(int fd)', { 'fd': 'fd' }), # CFA_HAVE_IORING_OP_EPOLL_CTL Call('EPOLL_CTL', 'int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)', { 'fd': 'epfd', 'addr': 'fd', 'len': 'op', 'off': '(__u64)event' }), # CFA_HAVE_IORING_OP_SYNC_FILE_RANGE Call('SYNC_FILE_RANGE', 'int sync_file_range(int fd, off64_t offset, off64_t nbytes, unsigned int flags)', { 'fd': 'fd', 'off': 'offset', 'len': 'nbytes', 'sync_range_flags': 'flags' }), # CFA_HAVE_IORING_OP_SENDMSG Call('SENDMSG', 'ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags)', { 'fd': 'sockfd', 'addr': '(__u64)(struct msghdr *)msg', 'len': '1', 'msg_flags': 'flags' }), # CFA_HAVE_IORING_OP_RECVMSG Call('RECVMSG', 'ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags)', { 'fd': 'sockfd', 'addr': '(__u64)(struct msghdr *)msg', 'len': '1', 'msg_flags': 'flags' }), # CFA_HAVE_IORING_OP_SEND Call('SEND', 'ssize_t send(int sockfd, const void *buf, size_t len, int flags)', { 'fd': 'sockfd', 'addr': '(__u64)buf', 'len': 'len', 'msg_flags': 'flags' }), # CFA_HAVE_IORING_OP_RECV Call('RECV', 'ssize_t recv(int sockfd, void *buf, size_t len, int flags)', { 'fd': 'sockfd', 'addr': '(__u64)buf', 'len': 'len', 'msg_flags': 'flags' }), # CFA_HAVE_IORING_OP_ACCEPT Call('ACCEPT4', 'int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags)', { 'fd': 'sockfd', 'addr': 'addr', 'addr2': 'addrlen', 'accept_flags': 'flags' }), # CFA_HAVE_IORING_OP_CONNECT Call('CONNECT', 'int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)', { 'fd': 'sockfd', 'addr': '(__u64)addr', 'off': 'addrlen' }), # CFA_HAVE_IORING_OP_FALLOCATE Call('FALLOCATE', 'int fallocate(int fd, int mode, off_t offset, off_t len)', { 'fd': 'fd', 'addr': '(__u64)len', 'len': 'mode', 'off': 'offset' }), # CFA_HAVE_IORING_OP_FADVISE Call('FADVISE', 'int posix_fadvise(int fd, off_t offset, off_t len, int advice)', { 'fd': 'fd', 'off': 'offset', 'len': 'len', 'fadvise_advice': 'advice' }), # CFA_HAVE_IORING_OP_MADVISE Call('MADVISE', 'int madvise(void *addr, size_t length, int advice)', { 'addr': '(__u64)addr', 'len': 'length', 'fadvise_advice': 'advice' }), # CFA_HAVE_IORING_OP_OPENAT Call('OPENAT', 'int openat(int dirfd, const char *pathname, int flags, mode_t mode)', { 'fd': 'dirfd', 'addr': '(__u64)pathname', 'len': 'mode', 'open_flags': 'flags;' }), # CFA_HAVE_IORING_OP_OPENAT2 Call('OPENAT2', 'int openat2(int dirfd, const char *pathname, struct open_how * how, size_t size)', { 'fd': 'dirfd', 'addr': 'pathname', 'len': 'sizeof(*how)', 'off': '(__u64)how', }, define = 'CFA_HAVE_OPENAT2'), # CFA_HAVE_IORING_OP_CLOSE Call('CLOSE', 'int close(int fd)', { 'fd': 'fd' }), # CFA_HAVE_IORING_OP_STATX Call('STATX', 'int statx(int dirfd, const char *pathname, int flags, unsigned int mask, struct statx *statxbuf)', { 'fd': 'dirfd', 'off': '(__u64)statxbuf', 'addr': 'pathname', 'len': 'mask', 'statx_flags': 'flags' }, define = 'CFA_HAVE_STATX'), # CFA_HAVE_IORING_OP_READ Call('READ', 'ssize_t read(int fd, void * buf, size_t count)', { 'fd': 'fd', 'addr': '(__u64)buf', 'len': 'count' }), # CFA_HAVE_IORING_OP_WRITE Call('WRITE', 'ssize_t write(int fd, void * buf, size_t count)', { 'fd': 'fd', 'addr': '(__u64)buf', 'len': 'count' }), # CFA_HAVE_IORING_OP_SPLICE Call('SPLICE', 'ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags)', { 'splice_fd_in': 'fd_in', 'splice_off_in': 'off_in ? (__u64)*off_in : (__u64)-1', 'fd': 'fd_out', 'off': 'off_out ? (__u64)*off_out : (__u64)-1', 'len': 'len', 'splice_flags': 'flags' }), # CFA_HAVE_IORING_OP_TEE Call('TEE', 'ssize_t tee(int fd_in, int fd_out, size_t len, unsigned int flags)', { 'splice_fd_in': 'fd_in', 'fd': 'fd_out', 'len': 'len', 'splice_flags': 'flags' }) ] print("//----------") print("// synchronous calls") for c in calls: if c.define: print("""#if defined({define}) {ret} cfa_{name}({params}, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context); #endif""".format(define=c.define,ret=c.ret, name=c.name, params=c.params)) else: print("{ret} cfa_{name}({params}, int submit_flags, Duration timeout, io_cancellation * cancellation, io_context * context);" .format(ret=c.ret, name=c.name, params=c.params)) print("\n//----------") print("// asynchronous calls") for c in calls: if c.define: print("""#if defined({define}) void async_{name}(io_future_t & future, {params}, int submit_flags, io_cancellation * cancellation, io_context * context); #endif""".format(define=c.define,name=c.name, params=c.params)) else: print("void async_{name}(io_future_t & future, {params}, int submit_flags, io_cancellation * cancellation, io_context * context);" .format(name=c.name, params=c.params)) print("\n") for c in calls: print("//-----------------------------------------------------------------------------") print("// {}".format(c.name)) Async = AsyncTemplate.format( name = c.name, ret = c.ret, params = c.params, args = c.args(), op = c.op, body = c.body ) Sync = SyncTemplate.format( name = c.name, ret = c.ret, params = c.params, args = c.args() ) if c.define: print("""#if defined({}) //---------- // asynchronous call {} //---------- // synchronous call {} #endif """.format(c.define, "\n\t".join( Async.splitlines() ), "\n\t".join( Sync.splitlines() ))) else : print("""//---------- // asynchronous call {} //---------- // synchronous call {} """.format(Async, Sync)) print(""" //----------------------------------------------------------------------------- // Check if a function is has asynchronous bool has_user_level_blocking( fptr_t func ) { #if defined(CFA_HAVE_LINUX_IO_URING_H)""") for c in calls: if c.define: print(""" #if defined({define}) if( /*func == (fptr_t)preadv2 || */ func == (fptr_t)cfa_{name} || func == (fptr_t)async_{name} ) {{ #if defined(CFA_HAVE_IORING_OP_{op}) return true; #else return false; #endif }} #endif""".format(define=c.define, name=c.name, op=c.op)) else: print(""" if( /*func == (fptr_t)preadv2 || */ func == (fptr_t)cfa_{name} || func == (fptr_t)async_{name} ) {{ #if defined(CFA_HAVE_IORING_OP_{op}) return true; #else return false; #endif }}""".format(name=c.name, op=c.op)) print(""" #endif return false; }""")