Introduce pollers
Handlers may way to poll on multiple file descriptors. This change
introduces pollers, which are registered with:
console_register_poller(), which provides a function to call when a
specified fd has pollable events.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
diff --git a/console-server.c b/console-server.c
index e68eab1..1ddb36c 100644
--- a/console-server.c
+++ b/console-server.c
@@ -6,6 +6,7 @@
#define _GNU_SOURCE
+#include <assert.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
@@ -29,10 +30,27 @@
int tty_sirq;
int tty_lpc_addr;
int tty_fd;
+
struct handler **handlers;
int n_handlers;
+
+ struct poller **pollers;
+ int n_pollers;
+
+ struct pollfd *pollfds;
};
+struct poller {
+ struct handler *handler;
+ void *data;
+ poller_fn_t fn;
+ bool remove;
+};
+
+/* we have one extra entry in the pollfds array for the VUART tty */
+static const int n_internal_pollfds = 1;
+
+
static void usage(const char *progname)
{
fprintf(stderr,
@@ -148,6 +166,9 @@
*/
fcntl(console->tty_fd, F_SETFL, FNDELAY);
+ console->pollfds[console->n_pollers].fd = console->tty_fd;
+ console->pollfds[console->n_pollers].events = POLLIN;
+
return 0;
}
@@ -210,25 +231,125 @@
}
return rc;
+
}
-static int handlers_poll_event(struct console *console,
- struct pollfd *pollfds)
+struct poller *console_register_poller(struct console *console,
+ struct handler *handler, poller_fn_t poller_fn,
+ int fd, int events, void *data)
{
- struct handler *handler;
- int i, rc, tmp;
+ struct poller *poller;
+ int n;
+
+ poller = malloc(sizeof(*poller));
+ poller->remove = false;
+ poller->handler = handler;
+ poller->fn = poller_fn;
+ poller->data = data;
+
+ /* add one to our pollers array */
+ n = console->n_pollers++;
+ console->pollers = realloc(console->pollers,
+ sizeof(*console->pollers) * console->n_pollers);
+
+ console->pollers[n] = poller;
+
+ /* increase pollfds array too */
+ console->pollfds = realloc(console->pollfds,
+ sizeof(*console->pollfds) *
+ (n_internal_pollfds + console->n_pollers));
+
+ /* shift the end pollfds up by one */
+ memcpy(&console->pollfds[n+n_internal_pollfds],
+ &console->pollfds[n],
+ sizeof(*console->pollfds) * n_internal_pollfds);
+
+ console->pollfds[n].fd = fd;
+ console->pollfds[n].events = events;
+
+ return poller;
+}
+
+void console_unregister_poller(struct console *console,
+ struct poller *poller)
+{
+ int i;
+
+ /* find the entry in our pollers array */
+ for (i = 0; i < console->n_pollers; i++)
+ if (console->pollers[i] == poller)
+ break;
+
+ assert(i < console->n_pollers);
+
+ console->n_pollers--;
+
+ /* remove the item from the pollers array... */
+ memmove(&console->pollers[i], &console->pollers[i+1],
+ sizeof(*console->pollers)
+ * (console->n_pollers - i));
+
+ console->pollers = realloc(console->pollers,
+ sizeof(*console->pollers) * console->n_pollers);
+
+ /* ... and the pollfds array */
+ memmove(&console->pollfds[i], &console->pollfds[i+1],
+ sizeof(*console->pollfds) *
+ (n_internal_pollfds + console->n_pollers - i));
+
+ console->pollfds = realloc(console->pollfds,
+ sizeof(*console->pollfds) *
+ (n_internal_pollfds + console->n_pollers));
+
+
+ free(poller);
+}
+
+static int call_pollers(struct console *console)
+{
+ struct poller *poller;
+ struct pollfd *pollfd;
+ enum poller_ret prc;
+ int i, rc;
rc = 0;
- for (i = 0; i < console->n_handlers; i++) {
- handler = console->handlers[i];
+ /*
+ * Process poll events by iterating through the pollers and pollfds
+ * in-step, calling any pollers that we've found revents for.
+ */
+ for (i = 0; i < console->n_pollers; i++) {
+ poller = console->pollers[i];
+ pollfd = &console->pollfds[i];
- if (!handler->poll_event)
+ if (!pollfd->revents)
continue;
- tmp = handler->poll_event(handler, pollfds[i].revents);
- if (tmp == HANDLER_EXIT)
- rc = 1;
+ prc = poller->fn(poller->handler, pollfd->revents,
+ poller->data);
+ if (prc == POLLER_EXIT)
+ rc = -1;
+ else if (prc == POLLER_REMOVE)
+ poller->remove = true;
+ }
+
+ /**
+ * Process deferred removals; restarting each time we unregister, as
+ * the array will have changed
+ */
+ for (;;) {
+ bool removed = false;
+
+ for (i = 0; i < console->n_pollers; i++) {
+ poller = console->pollers[i];
+ if (poller->remove) {
+ console_unregister_poller(console, poller);
+ removed = true;
+ break;
+ }
+ }
+ if (!removed)
+ break;
}
return rc;
@@ -236,31 +357,22 @@
int run_console(struct console *console)
{
- struct handler *handler;
- struct pollfd *pollfds;
- int i, rc;
-
- pollfds = calloc(console->n_handlers + 1, sizeof(*pollfds));
-
- pollfds[0].fd = console->tty_fd;
- pollfds[0].events = POLLIN;
+ int rc;
for (;;) {
uint8_t buf[4096];
- /* init pollers */
- for (i = 0; i < console->n_handlers; i++) {
- handler = console->handlers[i];
- handler->init_poll(handler, &pollfds[i+1]);
- }
-
- rc = poll(pollfds, console->n_handlers + 1, -1);
+ rc = poll(console->pollfds,
+ console->n_pollers + n_internal_pollfds, -1);
if (rc < 0) {
warn("poll error");
return -1;
}
- if (pollfds[0].revents) {
+ /* process internal fd first */
+ BUILD_ASSERT(n_internal_pollfds == 1);
+
+ if (console->pollfds[console->n_pollers].revents) {
rc = read(console->tty_fd, buf, sizeof(buf));
if (rc <= 0) {
warn("Error reading from tty device");
@@ -271,7 +383,8 @@
return 0;
}
- rc = handlers_poll_event(console, pollfds + 1);
+ /* ... and then the pollers */
+ rc = call_pollers(console);
if (rc)
return 0;
}
@@ -328,6 +441,9 @@
}
}
+ console->pollfds = calloc(n_internal_pollfds,
+ sizeof(*console->pollfds));
+
if (!console->tty_kname) {
fprintf(stderr,
"Error: No TTY device specified (use --device)\n");
diff --git a/console-server.h b/console-server.h
index ec77102..38cddef 100644
--- a/console-server.h
+++ b/console-server.h
@@ -1,9 +1,10 @@
-struct console;
-
#include <poll.h>
#include <stdint.h>
+struct console;
+
+/* handler API */
enum {
HANDLER_OK = 0,
HANDLER_EXIT,
@@ -13,10 +14,6 @@
const char *name;
int (*init)(struct handler *handler,
struct console *console);
- int (*init_poll)(struct handler *hander,
- struct pollfd *pollfd);
- int (*poll_event)(struct handler *handler,
- int events);
int (*data_in)(struct handler *handler,
uint8_t *buf, size_t len);
void (*fini)(struct handler *handler);
@@ -33,6 +30,24 @@
int console_data_out(struct console *console, const uint8_t *data, size_t len);
+/* poller API */
+struct poller;
+
+enum poller_ret {
+ POLLER_OK = 0,
+ POLLER_REMOVE,
+ POLLER_EXIT,
+};
+
+typedef enum poller_ret (*poller_fn_t)(struct handler *handler,
+ int revents, void *data);
+
+struct poller *console_register_poller(struct console *console,
+ struct handler *handler, poller_fn_t poller_fn,
+ int fd, int events, void *data);
+
+void console_unregister_poller(struct console *console, struct poller *poller);
+
/* utils */
int write_buf_to_fd(int fd, const uint8_t *buf, size_t len);
diff --git a/log-handler.c b/log-handler.c
index 0eb9553..8396890 100644
--- a/log-handler.c
+++ b/log-handler.c
@@ -57,14 +57,6 @@
return 0;
}
-static int log_init_poll(struct handler *handler, struct pollfd *pollfd)
-{
- (void)handler;
- pollfd->fd = -1;
- pollfd->events = 0;
- return 0;
-}
-
static int log_trim(struct log_handler *lh, size_t space)
{
int rc, n_shift_pages, shift_len, shift_start;
@@ -130,7 +122,6 @@
.handler = {
.name = "log",
.init = log_init,
- .init_poll = log_init_poll,
.data_in = log_data,
.fini = log_fini,
},
diff --git a/stdio-handler.c b/stdio-handler.c
index e74aaf5..d8a5a8a 100644
--- a/stdio-handler.c
+++ b/stdio-handler.c
@@ -12,6 +12,7 @@
struct stdio_handler {
struct handler handler;
struct console *console;
+ struct poller *poller;
int fd_in;
int fd_out;
bool is_tty;
@@ -28,6 +29,59 @@
static const uint8_t esc_str[] = { '\r', '~', '.' };
+static int process_input(struct stdio_handler *sh,
+ uint8_t *buf, size_t len)
+{
+ unsigned long i;
+ uint8_t e;
+
+ e = esc_str[sh->esc_str_pos];
+
+ for (i = 0; i < len; i++) {
+ if (buf[i] == e) {
+ sh->esc_str_pos++;
+ if (sh->esc_str_pos == ARRAY_SIZE(esc_str))
+ return 1;
+ e = esc_str[sh->esc_str_pos];
+ } else {
+ console_data_out(sh->console,
+ esc_str, sh->esc_str_pos);
+ sh->esc_str_pos = 0;
+ }
+ }
+ return 0;
+}
+
+static enum poller_ret stdio_poll(struct handler *handler,
+ int events, void __attribute__((unused)) *data)
+{
+ struct stdio_handler *sh = to_stdio_handler(handler);
+ uint8_t buf[4096];
+ ssize_t len;
+ int rc;
+
+ if (!(events & POLLIN))
+ return POLLER_OK;
+
+ len = read(sh->fd_in, buf, sizeof(buf));
+ if (len <= 0)
+ goto err;
+
+ rc = process_input(sh, buf, len);
+ if (rc)
+ return POLLER_EXIT;
+
+ rc = console_data_out(sh->console, buf, len);
+ if (rc < 0)
+ goto err;
+
+ return POLLER_OK;
+
+err:
+ sh->poller = NULL;
+ return POLLER_REMOVE;
+}
+
/*
@@ -62,14 +116,9 @@
return -1;
}
- return 0;
-}
+ sh->poller = console_register_poller(console, handler, stdio_poll,
+ sh->fd_in, POLLIN, NULL);
-static int stdio_init_poll(struct handler *handler, struct pollfd *pollfd)
-{
- struct stdio_handler *sh = to_stdio_handler(handler);
- pollfd->fd = sh->fd_in;
- pollfd->events = POLLIN;
return 0;
}
@@ -79,67 +128,19 @@
return write_buf_to_fd(sh->fd_out, buf, len);
}
-static int process_input(struct stdio_handler *sh,
- uint8_t *buf, size_t len)
-{
- unsigned long i;
- uint8_t e;
-
- e = esc_str[sh->esc_str_pos];
-
- for (i = 0; i < len; i++) {
- if (buf[i] == e) {
- sh->esc_str_pos++;
- if (sh->esc_str_pos == ARRAY_SIZE(esc_str))
- return 1;
- e = esc_str[sh->esc_str_pos];
- } else {
- console_data_out(sh->console,
- esc_str, sh->esc_str_pos);
- sh->esc_str_pos = 0;
- }
- }
- return 0;
-}
-
-static int stdio_poll_event(struct handler *handler, int events)
-{
- struct stdio_handler *sh = to_stdio_handler(handler);
- uint8_t buf[4096];
- ssize_t len;
- int rc;
-
- if (!(events & POLLIN))
- return 0;
-
- len = read(sh->fd_in, buf, sizeof(buf));
- if (len <= 0)
- return -1;
-
- rc = process_input(sh, buf, len);
- if (rc)
- return HANDLER_EXIT;
-
- rc = console_data_out(sh->console, buf, len);
- if (rc < 0)
- return -1;
-
- return 0;
-}
-
static void stdio_fini(struct handler *handler)
{
struct stdio_handler *sh = to_stdio_handler(handler);
if (sh->is_tty)
tcsetattr(sh->fd_in, TCSANOW, &sh->orig_termios);
+ if (sh->poller)
+ console_unregister_poller(sh->console, sh->poller);
}
static struct stdio_handler stdio_handler = {
.handler = {
.name = "stdio",
.init = stdio_init,
- .init_poll = stdio_init_poll,
- .poll_event = stdio_poll_event,
.data_in = stdio_data,
.fini = stdio_fini,
},