summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2024-09-24 22:22:32 -0600
committerLuke T. Shumaker <lukeshu@lukeshu.com>2024-09-24 22:22:32 -0600
commit729663cb9d20b0d36b3ef6b689447390b94006df (patch)
treee91f6b82b5bf4f0265518820dbd7ffe4fcd2bb4c
parent0b7bd5db8b2539d2e03b3a13b91ee6b21101460d (diff)
wip config, 9p
-rw-r--r--9p/9P2000.u.txt2
-rw-r--r--9p/defs.h9
-rw-r--r--9p/internal.h47
-rw-r--r--9p/srv.c205
-rw-r--r--9p/srv.h25
-rw-r--r--Makefile3
-rw-r--r--README.md14
-rw-r--r--config.h58
-rw-r--r--coroutine.c63
-rw-r--r--coroutine.h8
-rw-r--r--netio.h13
-rw-r--r--netio_posix.c84
12 files changed, 388 insertions, 143 deletions
diff --git a/9p/9P2000.u.txt b/9p/9P2000.u.txt
index 20aea48..60d2b11 100644
--- a/9p/9P2000.u.txt
+++ b/9p/9P2000.u.txt
@@ -15,6 +15,6 @@ stat += "file_extension[s]"
"file_owner_n_gid[4]"
"file_last_modified_n_uid[4]"
-Tauth += "nxs_uname[4]"
+Tauth += "n_uname[4]"
Rerror += "errno[4]"
diff --git a/9p/defs.h b/9p/defs.h
index 20a6411..907cdde 100644
--- a/9p/defs.h
+++ b/9p/defs.h
@@ -13,6 +13,7 @@
#define P9_NOFID ((uint32_t)~0U)
enum p9_version {
+ P9_VER_UNINITIALIZED,
/* P9_VER_9P1, */
P9_VER_9P2000,
/*P9_VER_9P2000_u,*/
@@ -21,13 +22,9 @@ enum p9_version {
_P9_VER_CNT,
};
-struct p9_ctx {
- enum p9_version version;
- uint32_t max_msg_size;
+struct p9_ctx;
- uint32_t err_num;
- char err_msg[256]; /* I chose 256 arbitrarily. */
-};
+enum p9_version p9_ctx_version(p9_ctx *);
/** Write an static error into ctx, return -1. */
int p9_error(struct p9_ctx *ctx, uint32_t linux_errno, char const *msg);
diff --git a/9p/internal.h b/9p/internal.h
index 1bc0e92..61977d4 100644
--- a/9p/internal.h
+++ b/9p/internal.h
@@ -7,15 +7,36 @@
#ifndef _9P_INTERNAL_H_
#define _9P_INTERNAL_H_
+#include <assert.h>
#include <stdint.h>
#include <stdbool.h>
#include "9p/defs.h"
+#define USE_CONFIG_9P
+#include "config.h"
+static_assert(CONFIG_9P_MAX_ERR_SIZE <= UINT16_MAX);
+
/* C language *****************************************************************/
#define UNUSED(name) /* name __attribute__ ((unused)) */
+/* types **********************************************************************/
+
+struct p9_ctx {
+ enum p9_version version;
+ uint32_t max_msg_size;
+ uint32_t Rerror_overhead;
+
+ uint32_t err_num;
+ char err_msg[CONFIG_9P_MAX_ERR_SIZE];
+};
+
+static inline enum p9_version p9_ctx_version(p9_ctx *ctx) {
+ assert(ctx);
+ return ctx->version;
+}
+
/* vtables ********************************************************************/
typedef bool (*_checksize_fn_t)(uint32_t net_len, uint8_t *net_bytes, uint32_t *mut_net_offset, size_t *mut_host_extra);
@@ -65,26 +86,30 @@ static inline uint64_t decode_u64le(uint8_t *in) {
;
}
-static inline bool is_valid_utf8_without_nul(uint8_t *str, size_t len) {
- uint8_t mask;
+static inline bool _is_valid_utf8(uint8_t *str, size_t len, bool forbid_nul) {
+ uint32_t ch;
uint8_t chlen;
+ assert(str);
for (size_t pos = 0; pos < len;) {
- if ((str[pos] & 0b10000000) == 0b00000000) { mask = 0b01111111; chlen = 1; }
- else if ((str[pos] & 0b11100000) == 0b11000000) { mask = 0b00011111; chlen = 2; }
- else if ((str[pos] & 0b11110000) == 0b11100000) { mask = 0b00001111; chlen = 3; }
- else if ((str[pos] & 0b11111000) == 0b11110000) { mask = 0b00000111; chlen = 4; }
+ if ((str[pos] & 0b10000000) == 0b00000000) { ch = str[pos] & 0b01111111; chlen = 1; }
+ else if ((str[pos] & 0b11100000) == 0b11000000) { ch = str[pos] & 0b00011111; chlen = 2; }
+ else if ((str[pos] & 0b11110000) == 0b11100000) { ch = str[pos] & 0b00001111; chlen = 3; }
+ else if ((str[pos] & 0b11111000) == 0b11110000) { ch = str[pos] & 0b00000111; chlen = 4; }
else return false;
- if (pos + chlen > len || (str[pos] & mask) == 0) return false;
- switch (chlen) {
- case 4: if ((str[pos+3] & 0b11000000) != 0b10000000) return false; /* fallthrough */
- case 3: if ((str[pos+2] & 0b11000000) != 0b10000000) return false; /* fallthrough */
- case 2: if ((str[pos+1] & 0b11000000) != 0b10000000) return false; /* fallthrough */
+ if ((ch == 0 && (chlen != 1 || forbid_nul)) || pos + chlen > len) return false;
+ for (uint8_t i = 1; i < chlen; i++) {
+ if ((str[pos+i] & 0b11000000) != 0b10000000) return false;
+ ch = (ch << 6) | (str[pos+i] & 0b00111111);
}
+ if (ch > 0x10FFFF) return false;
pos += chlen;
}
return true;
}
+#define is_valid_utf8(str, len) _is_valid_utf8(str, len, false)
+#define is_valid_utf8_without_nul(str, len) _is_valid_utf8(str, len, true)
+
/* marshal utilities **********************************************************/
static inline void encode_u8le(uint8_t in, uint8_t *out) {
diff --git a/9p/srv.c b/9p/srv.c
index f7c8195..652a76b 100644
--- a/9p/srv.c
+++ b/9p/srv.c
@@ -1,44 +1,191 @@
-#include "net9p.h"
-#include "net9p_defs.h"
+#include <assert.h>
+
+#include "coroutine.h"
#include "netio.h"
+#include "9p/9p.h"
-#define MAX_MSG_SIZE 1024
+#include "9p/internal.h"
-int read_msg(int conn, uint16_t *out_tag, void **out_body) {
- uint8_t buf[MAX_MSG_SIZE];
- size_t todo = 7, done = 0;
+struct p9_srvconn {
+ /* immutable */
+ p9_srv *srv;
+ cid_t reader;
+ int fd;
+ /* mutable */
+ uint32_t max_msg_size;
+ enum p9_version version;
+ unsigned int refcount;
+};
- while (done < todo) {
- ssize_t r = netio_read(conn, buf, 7);
- if (r < 0)
- return r;
- done += r;
- }
- todo = docode_u32le(buf);
- if (todo < 7)
- return -EINVAL;
- while (done < todo) {
- ssize_t r = netio_read(conn, buf, 7);
- if (r < 0)
- return r;
- done += r;
- }
- return v9fs_unmarshal_msg(buf, out_tag, out_body);
-}
+struct p9_srvreq {
+ p9_srvconn *conn;
+ uint8_t *msg;
+};
+
+COROUTINE p9_srv_read_cr(void *_srv) {
+ uint8_t buf[CONFIG_9P_MAX_MSG_SIZE];
-void net9p_cr(void *_arg) {
- int sock = *((int *)_arg);
+ p9_srv *srv = _srv;
+ assert(srv);
cr_begin();
for (;;) {
- int conn = netio_accept(sock);
- if (conn < 0) {
- error(0, -conn, "netio_accept");
+ struct p9_srvconn conn = {
+ .srv = srv,
+ .reader = cr_getcid();
+
+ .max_msg_size = CONFIG_9P_MAX_MSG_SIZE;
+ .version = P9_VER_UNINITIALIZED;
+ .refcount = 1,
+ };
+ conn.fd = netio_accept(srv->sockfd);
+ if (conn.fd < 0) {
+ fprintf(stderr, "error: accept: %m", -conn.fd);
continue;
}
-
+ for (;;) {
+ /* Read the message size. */
+ size_t goal = 4, done = 0;
+ while (done < goal) {
+ ssize_t r = netio_read(conn.fd, &buf[done], sizeof(buf)-done);
+ if (r < 0) {
+ fprintf(stderr, "error: read: %m", -r);
+ goto close;
+ } else if (r == 0) {
+ if (done != 0)
+ fprintf(stderr, "error: read: unexpected EOF");
+ goto close;
+ }
+ done += r;
+ }
+ goal = decode_u32le(buf);
+ if (goal < 7) {
+ /* We can't respond with an Rerror becuase we wouldn't know what tag to use! */
+ fprintf(stderr, "error: T-message is impossibly small");
+ goto close;
+ }
+ if (goal > conn.max_msg_size) {
+ struct p9_ctx ctx = {
+ .version = conn.version,
+ .max_msg_size = conn.max_msg_size,
+ };
+ if (initialized)
+ p9_errorf(&ctx, LINUX_EMSGSIZE, "T-message larger than negotiated limit (%zu > %zu)", goal, conn.max_msg_size);
+ else
+ p9_errorf(&ctx, LINUX_EMSGSIZE, "T-message larger than server limit (%zu > %zu)", goal, conn.max_msg_size);
+ marshal_error(&ctx, buf);
+ netio_write(conn.fd, buf, decode_u32le(buf));
+ continue;
+ }
+ /* Read the rest of the message. */
+ while (done < goal) {
+ ssize_t r = netio_read(conn.fd, &buf[done], sizeof(buf)-done);
+ if (r < 0) {
+ fprintf(stderr, "error: read: %m", -r);
+ goto close;
+ } else if (r == 0) {
+ fprintf(stderr, "error: read: unexpected EOF");
+ goto close;
+ }
+ done += r;
+ }
+
+ /* Handle the message... */
+ if (conn.version == P9_VER_UNINITIALIZED) {
+ /* ...synchronously if we haven't negotiated the protocol yet, ... */
+ handle_message(&conn, buf);
+ } else {
+ /* ...asynchronously if we have. */
+ cr_chan_send(&srv->reqch, buf);
+ cr_pause_and_yield();
+ }
+ }
+ close:
+ netio_close(conn.fd, true, (--conn.refcount) == 0);
+ if (conn.refcount) {
+ cr_pause_and_yield();
+ assert(conn.refcount == 0);
+ netio_close(conn.fd, false, true);
+ }
}
cr_end();
}
+
+COROUTINE p9_srv_write_cr(void *_srv) {
+ uint8_t net[CONFIG_9P_MAX_MSG_SIZE];
+
+ p9_srv *srv = _srv;
+ assert(srv);
+ cr_begin();
+
+ for (;;) {
+ struct p9_srvreq req;
+ cr_chan_recv(&srv->reqch, &req);
+ memcpy(net, req.msg, decode_u32le(req.msg));
+ req.conn->refcount++;
+ cr_unpause(req.conn->reader);
+
+ handle_message(&req.conn, net);
+
+ if ((--req.conn->refcount) == 0)
+ cr_unpause(req.conn->reader);
+ }
+
+ cr_end();
+}
+
+void handle_message(p9_srvconn *conn, uint8_t *net) {
+ uint8_t host[CONFIG_9P_MAX_MSG_SIZE];
+
+ struct p9_ctx ctx = {
+ .version = req.conn->version,
+ .max_msg_size = req.conn->max_msg_size,
+ };
+
+ size_t host_size = p9_unmarshal_size(&ctx, net);
+ if (host_size == (size_t)-1)
+ goto write;
+ if (host_size > sizeof(host)) {
+ p9_errorf(&ctx, LINUX_EMSGSIZE, "unmarshalled payload larger than server limit (%zu > %zu)", host_size, sizeof(host));
+ goto write;
+ }
+
+ uint16_t tag;
+ uint8_t typ = p9_unmarshal(&ctx, net, &tag, host);
+ if (typ == (uint8_t)-1)
+ goto write;
+ if (typ % 2 != 0) {
+ p9_errorf(&ctx, LINUX_EOPNOTSUPP, "expected a T-message but got an R-message");
+ goto write;
+ }
+
+ TODO;
+
+ write:
+ if (ctx.err_num || ctx.err_msg[0])
+ marshal_error(&ctx, net);
+ else
+ TODO;
+ netio_write(req.conn->fd, net, decode_u32le(net));
+}
+
+static inline uint16_t min_u16(uint16_t a, b) {
+ return (a < b) ? a : b;
+}
+
+/* We have special code for marshaling Rerror because we don't ever
+ * want to produce an error because the err_msg is too long for the
+ * `ctx->max_msg_size`! */
+void marshal_error(struct p9_ctx *ctx, uint16_t tag, uint8_t *net) {
+ struct p9_msg_Rerror host = {
+ .ename = {
+ .len = strnlen(ctx->err_msg, CONFIG_9P_MAX_ERR_SIZE),
+ .utf8 = ctx->err_msg,
+ },
+ };
+ if (host.ename.len + ctx->Rerror_overhead > ctx->max_msg_size)
+ host.ename.len = ctx->max_msg_size - overhead;
+ p9_marshal(ctx, tag, host, net);
+}
diff --git a/9p/srv.h b/9p/srv.h
index 32a2e30..e3623ed 100644
--- a/9p/srv.h
+++ b/9p/srv.h
@@ -3,25 +3,14 @@
#include "coroutine.h"
-#define 9P_DEFAULT_PORT 564
+struct p9_srvreq;
-/**
- * The default here is the same as in Plan 9 4e's lib9p. It's sized
- * so that a Twrite message can return 8KiB of data; it uses the
- * default (1024*8)+24 with the comment that "24" is "ample room for
- * Twrite/Rread header (iounit)". In fact, the Twrite header is only
- * 23 bytes ("size[4] Twrite[1] tag[2] fid[4] offset[8] count[4]") and
- * the Rread header is even shorter at 11 bytes ("size[4] Rread[1]
- * tag[2] count[4]"), so "24" appears to be the size of the Twrite
- * header rounded up to a nice round number.
- *
- * In older versions of 9P ("9P1"), the max message size was defined
- * as part of the protocol specification rather than negotiated. In
- * Plan 9 1e it was (1024*8)+128, and was bumped to (1024*8)+160 in 2e
- * and 3e.
- */
-#define 9P_DEFAULT_MAX_MSG_SIZE ((1024*8)+24)
+struct p9_srv {
+ int sockfd;
+ cr_chan_t(p9_srvreq *) reqch;
+};
-COROUTINE net9p_cr(void *);
+COROUTINE p9_srv_read_cr(void *_srv);
+COROUTINE p9_srv_write_cr(void *_srv);
#endif /* _NET9P_H_ */
diff --git a/Makefile b/Makefile
index 74aa20b..c364472 100644
--- a/Makefile
+++ b/Makefile
@@ -14,7 +14,8 @@ linux.git = $(HOME)/src/github.com/torvalds/linux
9p/defs-%.c 9p/defs-%.h: 9p/defs.gen 9p/%.txt
$^
-srv9p: srv9p.o coroutine.o net9p.o 9p/9P2000.o
+lib9p = 9p/defs.o 9p/defs-9P2000.o 9p/srv.o
+srv9p: srv9p.o coroutine.o netio_posix.o $(lib9p)
sources_py = 9p/defs.gen
sources_py += 9p/linux-errno.h.gen
diff --git a/README.md b/README.md
index a1441f4..2982e55 100644
--- a/README.md
+++ b/README.md
@@ -39,9 +39,9 @@ protocol over TCP:
- `9P2000` (base protocol): Yes
- `9P2000.u` (Unix extension): Yes, with Linux kernel
architecture-"generic" errnos. This will match the Linux kernel
- errnos on most architectures (but notably not on Alpha, MIPS,
- PA-RISC, PowerPC, or SPARC; I am unsure whether on these
- platforms the kernel's v9fs filesystem driver will map the
+ errnos on most architectures (but, as of Linux v6.7, not on
+ Alpha, MIPS, PA-RISC, PowerPC, or SPARC; I am unsure whether on
+ these platforms the kernel's v9fs filesystem driver will map the
"generic" errnos to the architecture-specific errnos for you).
- `9P2000.L` (Linux extension): No, it's an abomination and
unlikely to ever be supported
@@ -72,6 +72,10 @@ Some notes on choosing a client:
# Bugs/Limitations
- Only supports 8 concurrent TCP connectsions to the 9P server (due
- to limitations in the W5500 TCP-offload chip)
+ to limitations in the W5500 TCP-offload chip; TODO: investigate
+ using a software TCP/IP stack with the W5500 in MAC-raw mode)
+ - Only supports 16 concurrent 9P requests (I wanted a static limit,
+ and "connections*2" seemed reasonable)
- Only supports IPv4, not IPv6 (due to limitations in the W5500
- TCP-offload chip)
+ TCP-offload chip; TODO: investigate upgrading to the W6100 or using
+ a software TCP/IP stack with the W5500 in MAC-raw mode)
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..37bfcbe
--- /dev/null
+++ b/config.h
@@ -0,0 +1,58 @@
+/* config.h - Compile-time configuration for sbc-harness
+ *
+ * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-Licence-Identifier: AGPL-3.0-or-later
+ */
+#ifndef _CONFIG_H_
+#define _CONFIG_H_
+
+/*#include <assert.h>*/
+
+#if defined(USE_CONFIG_NETIO_POSIX) || defined(USE_CONFIG_COROUTINE)
+# define CONFIG_NETIO_NUM_CONNS 8
+#endif
+#ifdef USE_CONFIG_NETIO_POSIX
+# define CONFIG_NETIO_ISLINUX 1 /* can we use Linux-kernel-specific fcntls? */
+# define CONFIG_NETIO_NUM_PORTS 1
+#endif
+
+#ifdef USE_CONFIG_COROUTINE
+# define CONFIG_COROUTINE_DEFAULT_STACK_SIZE (8*1024)
+# define CONFIG_COROUTINE_MEASURE_STACK 1 /* bool */
+# define CONFIG_COROUTINE_PROTECT_STACK 1 /* bool */
+# define CONFIG_COROUTINE_DEBUG 0 /* bool */
+# define CONFIG_COROUTINE_NUM (1 /* usb_common */ +\
+ 1 /* usb_keyboard */ +\
+ CONFIG_NETIO_NUM_CONNS /* accept+read */ +\
+ (2*CONFIG_NETIO_NUM_CONNS) /* work+write */ )
+ /*static_assert((CONFIG_COROUTINE_NUM * CONFIG_COROUTINE_DEFAULT_STACK_SIZE) < (264 * 1024)); */
+#endif
+
+#ifdef USE_CONFIG_TUSB
+# include "tusb_config.h"
+#endif
+
+#ifdef USE_CONFIG_9P
+# define CONFIG_9P_PORT 564
+ /**
+ * This max-msg-size is sized so that a Twrite message can return
+ * 8KiB of data.
+ *
+ * This is the same as the default in Plan 9 4e's lib9p; it has the
+ * comment that "24" is "ample room for Twrite/Rread header
+ * (iounit)". In fact, the Twrite header is only 23 bytes
+ * ("size[4] Twrite[1] tag[2] fid[4] offset[8] count[4]") and the
+ * Rread header is even shorter at 11 bytes ("size[4] Rread[1]
+ * tag[2] count[4]"), so "24" appears to be the size of the Twrite
+ * header rounded up to a nice round number.
+ *
+ * In older versions of 9P ("9P1"), the max message size was
+ * defined as part of the protocol specification rather than
+ * negotiated. In Plan 9 1e it was (8*1024)+128, and was bumped to
+ * (8*1024)+160 in 2e and 3e.
+ */
+# define CONFIG_9P_MAX_MSG_SIZE ((8*1024)+24)
+# define CONFIG_9P_MAX_ERR_SIZE 128 /* 128 is what Plan 9 4e uses */
+#endif
+
+#endif /* _CONFIG_H */
diff --git a/coroutine.c b/coroutine.c
index 2c265a3..f138fcf 100644
--- a/coroutine.c
+++ b/coroutine.c
@@ -14,10 +14,24 @@
/* Configuration **************************************************************/
-#define COROUTINE_NUM 5
-#define COROUTINE_MEASURE_STACK 1
-#define COROUTINE_PROTECT_STACK 1
-#define COROUTINE_DEBUG 0
+#define USE_CONFIG_COROUTINE
+#include "config.h"
+
+#ifndef CONFIG_COROUTINE_DEFAULT_STACK_SIZE
+# error config.h must define CONFIG_COROUTINE_DEFAULT_STACK_SIZE
+#endif
+#ifndef CONFIG_COROUTINE_NUM
+# error config.h must define CONFIG_COROUTINE_NUM
+#endif
+#ifndef CONFIG_COROUTINE_MEASURE_STACK
+# error config.h must define CONFIG_COROUTINE_MEASURE_STACK
+#endif
+#ifndef CONFIG_COROUTINE_PROTECT_STACK
+# error config.h must define CONFIG_COROUTINE_PROTECT_STACK
+#endif
+#ifndef CONFIG_COROUTINE_DEBUG
+# error config.h must define CONFIG_COROUTINE_DEBUG
+#endif
/* Implementation *************************************************************/
@@ -77,7 +91,7 @@
* sizes. (2) Leaving it uninitialized just gives me the willies.
*
* - Because embedded programs should be adverse to using the heap,
- * COROUTINE_NUM is fixed, instead of having coroutine_add()
+ * CONFIG_COROUTINE_NUM is fixed, instead of having coroutine_add()
* dynamically grow the coroutine_table as-needed.
*
* - On the flip-side, coroutine stacks are allocated on the heap
@@ -105,7 +119,7 @@
* and a few bytes.
*/
-#if COROUTINE_DEBUG
+#if CONFIG_COROUTINE_DEBUG
# define debugf(...) printf("dbg: " __VA_ARGS__)
#else
# define debugf(...)
@@ -146,8 +160,8 @@ struct coroutine {
void *stack;
};
-static struct coroutine coroutine_table[COROUTINE_NUM] = {0};
-static cid_t coroutine_running = 0;
+static struct coroutine coroutine_table[CONFIG_COROUTINE_NUM] = {0};
+static cid_t coroutine_running = 0;
static void call_with_stack(void *stack, cr_fn_t fn, void *args) {
static void *saved_sp = NULL;
@@ -193,13 +207,13 @@ static void call_with_stack(void *stack, cr_fn_t fn, void *args) {
#endif
}
-#if COROUTINE_MEASURE_STACK || COROUTINE_PROTECT_STACK
+#if CONFIG_COROUTINE_MEASURE_STACK || CONFIG_COROUTINE_PROTECT_STACK
/* We just need a pattern that is unlikely to occur naturaly; this is
* just a few bytes that I read from /dev/random. */
-static const uint8_t const stack_pattern[] = {0x1e, 0x15, 0x16, 0x0a, 0xcc, 0x52, 0x7e, 0xb7};
+static const uint8_t stack_pattern[] = {0x1e, 0x15, 0x16, 0x0a, 0xcc, 0x52, 0x7e, 0xb7};
#endif
-#if COROUTINE_PROTECT_STACK
+#if CONFIG_COROUTINE_PROTECT_STACK
void assert_stack_protection(cid_t cid) {
assert(coroutine_table[cid-1].stack_size);
assert(coroutine_table[cid-1].stack);
@@ -215,7 +229,7 @@ void assert_stack_protection(cid_t cid) {
#define assert_cid_state(cid, opstate) do { \
assert((cid) > 0); \
- assert((cid) <= COROUTINE_NUM); \
+ assert((cid) <= CONFIG_COROUTINE_NUM); \
assert(coroutine_table[(cid)-1].state opstate); \
assert_stack_protection(cid); \
} while (0)
@@ -224,6 +238,9 @@ cid_t coroutine_add_with_stack_size(size_t stack_size, cr_fn_t fn, void *args) {
static cid_t last_created = 0;
cid_t parent = coroutine_running;
+ if (!stack_size)
+ stack_size = CONFIG_COROUTINE_DEFAULT_STACK_SIZE;
+
if (parent)
assert_cid_state(parent, == CR_RUNNING);
assert(stack_size);
@@ -233,8 +250,8 @@ cid_t coroutine_add_with_stack_size(size_t stack_size, cr_fn_t fn, void *args) {
cid_t child;
{
size_t idx_base = last_created;
- for (size_t idx_shift = 0; idx_shift < COROUTINE_NUM; idx_shift++) {
- child = ((idx_base + idx_shift) % COROUTINE_NUM) + 1;
+ for (size_t idx_shift = 0; idx_shift < CONFIG_COROUTINE_NUM; idx_shift++) {
+ child = ((idx_base + idx_shift) % CONFIG_COROUTINE_NUM) + 1;
if (coroutine_table[child-1].state == CR_NONE)
goto found;
}
@@ -247,7 +264,7 @@ cid_t coroutine_add_with_stack_size(size_t stack_size, cr_fn_t fn, void *args) {
coroutine_table[child-1].stack_size = stack_size;
coroutine_table[child-1].stack = malloc(stack_size);
-#if COROUTINE_MEASURE_STACK || COROUTINE_PROTECT_STACK
+#if CONFIG_COROUTINE_MEASURE_STACK || CONFIG_COROUTINE_PROTECT_STACK
for (size_t i = 0; i < stack_size; i++)
((uint8_t*)coroutine_table[child-1].stack)[i] = stack_pattern[i%sizeof(stack_pattern)];
#endif
@@ -256,7 +273,7 @@ cid_t coroutine_add_with_stack_size(size_t stack_size, cr_fn_t fn, void *args) {
coroutine_table[child-1].state = CR_INITIALIZING;
if (!setjmp(coroutine_add_env)) { /* point=a */
void *stack_base = coroutine_table[child-1].stack + (STACK_GROWS_DOWNWARD ? stack_size : 0);
-#if COROUTINE_PROTECT_STACK
+#if CONFIG_COROUTINE_PROTECT_STACK
# if STACK_GROWS_DOWNWARD
stack_base -= sizeof(stack_pattern);
# else
@@ -280,7 +297,7 @@ cid_t coroutine_add_with_stack_size(size_t stack_size, cr_fn_t fn, void *args) {
void coroutine_main(void) {
debugf("coroutine_main()\n");
bool ran;
- for (coroutine_running = 1;; coroutine_running = (coroutine_running%COROUTINE_NUM)+1) {
+ for (coroutine_running = 1;; coroutine_running = (coroutine_running%CONFIG_COROUTINE_NUM)+1) {
if (coroutine_running == 1)
ran = false;
struct coroutine *cr = &coroutine_table[coroutine_running-1];
@@ -294,13 +311,13 @@ void coroutine_main(void) {
}
assert_cid_state(coroutine_running, != CR_RUNNING);
if (cr->state == CR_NONE) {
-#if COROUTINE_MEASURE_STACK
- size_t stack_size = cr->stack_size - (COROUTINE_PROTECT_STACK ? 2*sizeof(stack_pattern) : 0);
+#if CONFIG_COROUTINE_MEASURE_STACK
+ size_t stack_size = cr->stack_size - (CONFIG_COROUTINE_PROTECT_STACK ? 2*sizeof(stack_pattern) : 0);
size_t stack_used = stack_size;
for (;;) {
size_t i = STACK_GROWS_DOWNWARD
- ? (COROUTINE_PROTECT_STACK ? sizeof(stack_pattern) : 0) + stack_size - stack_used
- : stack_used - 1 - (COROUTINE_PROTECT_STACK ? sizeof(stack_pattern) : 0);
+ ? (CONFIG_COROUTINE_PROTECT_STACK ? sizeof(stack_pattern) : 0) + stack_size - stack_used
+ : stack_used - 1 - (CONFIG_COROUTINE_PROTECT_STACK ? sizeof(stack_pattern) : 0);
if (stack_used == 0 || ((uint8_t*)cr->stack)[i] != stack_pattern[i%sizeof(stack_pattern)])
break;
stack_used--;
@@ -311,14 +328,14 @@ void coroutine_main(void) {
coroutine_table[coroutine_running-1] = (struct coroutine){0};
}
}
- if (coroutine_running == COROUTINE_NUM && !ran) {
+ if (coroutine_running == CONFIG_COROUTINE_NUM && !ran) {
fprintf(stderr, "error: no runnable coroutines\n");
return;
}
}
}
-bool cr_begin(void) {
+void cr_begin(void) {
assert_cid_state(coroutine_running, == CR_INITIALIZING);
coroutine_table[coroutine_running-1].state = CR_RUNNABLE;
diff --git a/coroutine.h b/coroutine.h
index d02d0f9..4bdc4f8 100644
--- a/coroutine.h
+++ b/coroutine.h
@@ -29,10 +29,6 @@
#include <stddef.h> /* for size_t */
#include <stdbool.h> /* for bool */
-/* configuration **************************************************************/
-
-#define COROUTINE_DEFAULT_STACK_SIZE (8*1024)
-
/* typedefs *******************************************************************/
/**
@@ -94,7 +90,7 @@ cid_t coroutine_add_with_stack_size(size_t stack_size, cr_fn_t fn, void *args);
* Like coroutine_add_with_stack_size(), but uses a default stack size so
* you don't need to think about it.
*/
-#define coroutine_add(fn, args) coroutine_add_with_stack_size(COROUTINE_DEFAULT_STACK_SIZE, fn, args)
+#define coroutine_add(fn, args) coroutine_add_with_stack_size(0, fn, args)
/**
* The main scheduler loop.
@@ -109,7 +105,7 @@ void coroutine_main(void);
/* inside of coroutines *******************************************************/
/** cr_begin() goes at the beginning of a coroutine, after it has initialized its stack. */
-bool cr_begin( void);
+void cr_begin( void);
/** cr_exit() terminates the currently-running coroutine. */
__attribute__ ((noreturn)) void cr_exit(void);
/** cr_yield() switches to another coroutine (if there is another runnable coroutine to switch to). */
diff --git a/netio.h b/netio.h
index 8d9fa50..8497330 100644
--- a/netio.h
+++ b/netio.h
@@ -1,13 +1,18 @@
#ifndef _NETIO_H_
#define _NETIO_H_
-#include <stdint.h>
-#include <stdbool.h>
+#include <stdbool.h> /* for bool */
+#include <stdint.h> /* for size_t, ssize_t, uint16_t */
+/** Return socket-fd on success, -errno on error. */
int netio_listen(uint16_t port);
+/** Return connection-fd on success, -errno on error. */
int netio_accept(int sock);
-size_t netio_read(int conn, void *buf, size_t count);
-size_t netio_write(int conn, void *buf, size_t count);
+/** Return bytes-read on success, 0 on EOF, -errno on error; a short read is *not* an error. */
+ssize_t netio_read(int conn, void *buf, size_t count);
+/** Return `count` on success, -errno on error; a short write *is* an error. */
+ssize_t netio_write(int conn, void *buf, size_t count);
+/** Return 0 on success, -errno on error. */
int netio_close(int conn, bool rd, bool wr);
#endif /* _NETIO_H_ */
diff --git a/netio_posix.c b/netio_posix.c
index 3730f98..3cc00bb 100644
--- a/netio_posix.c
+++ b/netio_posix.c
@@ -1,7 +1,3 @@
-#define LINUX 1
-#define NUM_SOCKETS 1
-#define NUM_WORKERS 8
-
#define _GNU_SOURCE
#include <aio.h> /* for struct aiocb, aio_read(), aio_write(), aio_error(), aio_return(), SIGEV_SIGNAL */
#include <arpa/inet.h> /* for htons() */
@@ -12,7 +8,11 @@
#include <stdlib.h> /* for shutdown(), SHUT_RD, SHUT_WR, SHUT_RDWR */
#include <string.h> /* for memset() */
#include <sys/socket.h> /* for struct sockaddr, socket(), SOCK_* flags, setsockopt(), SOL_SOCKET, SO_REUSEADDR, bind(), listen(), accept() */
-#if LINUX
+
+#define USE_CONFIG_NETIO_POSIX
+#include "config.h"
+
+#if CONFIG_NETIO_ISLINUX
# include <fcntl.h> /* for fcntl(), F_SETFL, O_ASYNC, F_SETSIG */
#endif
@@ -24,32 +24,32 @@
static int sigs_allocated = 0;
static int sig_io = 0;
-#if LINUX
+#if CONFIG_NETIO_ISLINUX
static int sig_accept = 0;
#endif
struct netio_socket {
int fd;
-#if LINUX
- cid_t accept_waiters[NUM_WORKERS];
+#if CONFIG_NETIO_ISLINUX
+ cid_t accept_waiters[CONFIG_NETIO_NUM_CONNS];
#endif
};
-static struct netio_socket socket_table[NUM_SOCKETS] = {0};
+static struct netio_socket socket_table[CONFIG_NETIO_NUM_PORTS] = {0};
static void handle_sig_io(int sig __attribute__ ((unused)), siginfo_t *info, void *ucontext __attribute__ ((unused))) {
cr_unpause((cid_t)info->si_value.sival_int);
}
-#if LINUX
+#if CONFIG_NETIO_ISLINUX
static void handle_sig_accept(int sig __attribute__ ((unused)), siginfo_t *info, void *ucontext __attribute__ ((unused))) {
struct netio_socket *sock = NULL;
- for (int i = 0; sock == NULL && i < NUM_SOCKETS; i++)
+ for (int i = 0; sock == NULL && i < CONFIG_NETIO_NUM_PORTS; i++)
if (info->si_fd == socket_table[i].fd)
sock = &socket_table[i];
if (!sock)
return;
- for (int i = 0; i < NUM_WORKERS; i++)
+ for (int i = 0; i < CONFIG_NETIO_NUM_CONNS; i++)
if (sock->accept_waiters[i] > 0) {
cr_unpause(sock->accept_waiters[i]);
sock->accept_waiters[i] = 0;
@@ -73,7 +73,7 @@ static void _netio_init(void) {
if (sigaction(sig_io, &action, NULL) < 0)
error(1, errno, "sigaction");
-#if LINUX
+#if CONFIG_NETIO_ISLINUX
sig_accept = SIGRTMIN + (sigs_allocated++);
if (sig_accept > SIGRTMAX)
error(1, 0, "SIGRTMAX exceeded");
@@ -97,23 +97,23 @@ int netio_listen(uint16_t port) {
/* Allocate a handle out of socket_table. */
handle = -1;
- for (int i = 0; handle < 0 && i < NUM_SOCKETS; i++)
+ for (int i = 0; handle < 0 && i < CONFIG_NETIO_NUM_PORTS; i++)
if (socket_table[i].fd == 0)
handle = i;
if (handle < 0)
- error(1, 0, "NUM_SOCKETS exceeded");
+ error(1, 0, "CONFIG_NETIO_NUM_PORTS exceeded");
sock = &socket_table[handle];
/* Bind the socket. */
memset(&addr, 0, sizeof addr);
addr.in.sin_family = AF_INET;
addr.in.sin_port = htons(port);
- sock->fd = socket(AF_INET, SOCK_STREAM | (LINUX ? 0 : SOCK_NONBLOCK), 0);
+ sock->fd = socket(AF_INET, SOCK_STREAM | (CONFIG_NETIO_ISLINUX ? 0 : SOCK_NONBLOCK), 0);
if (sock->fd < 0)
error(1, errno, "socket");
if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) < 0)
error(1, errno, "setsockopt");
-#if LINUX
+#if CONFIG_NETIO_ISLINUX
if (fcntl(sock->fd, F_SETFL, O_ASYNC) < 0)
error(1, errno, "fcntl(F_SETFL)");
if (fcntl(sock->fd, F_SETSIG, sig_accept) < 0)
@@ -121,7 +121,7 @@ int netio_listen(uint16_t port) {
#endif
if (bind(sock->fd, &addr.gen, sizeof addr) < 0)
error(1, errno, "bind");
- if (listen(sock->fd, NUM_WORKERS) < 0)
+ if (listen(sock->fd, CONFIG_NETIO_NUM_CONNS) < 0)
error(1, errno, "listen");
/* Return. */
@@ -129,9 +129,9 @@ int netio_listen(uint16_t port) {
}
int netio_accept(int sock) {
-#if LINUX
+#if CONFIG_NETIO_ISLINUX
int conn;
- for (int i = 0; i < NUM_WORKERS; i++)
+ for (int i = 0; i < CONFIG_NETIO_NUM_CONNS; i++)
if (socket_table[sock].accept_waiters[i] == 0) {
socket_table[sock].accept_waiters[i] = cr_getcid();
break;
@@ -156,7 +156,7 @@ int netio_accept(int sock) {
#endif
}
-size_t netio_read(int conn, void *buf, size_t count) {
+ssize_t netio_read(int conn, void *buf, size_t count) {
int r;
struct aiocb ctl_block = {
.aio_fildes = conn,
@@ -179,27 +179,33 @@ size_t netio_read(int conn, void *buf, size_t count) {
return r ? -abs(r) : aio_return(&ctl_block);
}
-size_t netio_write(int conn, void *buf, size_t count) {
- int r;
- struct aiocb ctl_block = {
- .aio_fildes = conn,
- .aio_buf = buf,
- .aio_nbytes = count,
- .aio_sigevent = {
- .sigev_notify = SIGEV_SIGNAL,
- .sigev_signo = sig_io,
- .sigev_value = {
- .sival_int = (int)cr_getcid(),
+ssize_t netio_write(int conn, void *buf, size_t goal) {
+ size_t done = 0;
+ while (done < goal) {
+ int r;
+ struct aiocb ctl_block = {
+ .aio_fildes = conn,
+ .aio_buf = &buf[done],
+ .aio_nbytes = goal-done,
+ .aio_sigevent = {
+ .sigev_notify = SIGEV_SIGNAL,
+ .sigev_signo = sig_io,
+ .sigev_value = {
+ .sival_int = (int)cr_getcid(),
+ },
},
- },
- };
+ };
- if (aio_write(&ctl_block) < 0)
- return -errno;
+ if (aio_write(&ctl_block) < 0)
+ return -errno;
- while ((r = aio_error(&ctl_block)) == EINPROGRESS)
- cr_pause_and_yield();
- return r ? -abs(r) : aio_return(&ctl_block);
+ while ((r = aio_error(&ctl_block)) == EINPROGRESS)
+ cr_pause_and_yield();
+ if ((r) < 0)
+ return -abs(r);
+ done += aio_return(&ctl_block);
+ }
+ return done;
}
int netio_close(int conn, bool rd, bool wr) {