From 665343e636315ff3aa4e508954304664d4b9188b Mon Sep 17 00:00:00 2001 From: Kevin Lange Date: Fri, 6 Jan 2017 18:30:17 +0900 Subject: [PATCH] First pass at a select-like fs wait function --- kernel/fs/pipe.c | 48 ++++ kernel/fs/vfs.c | 35 +++ kernel/include/fs.h | 10 + kernel/include/pipe.h | 1 + kernel/include/process.h | 5 + kernel/sys/process.c | 56 ++++ kernel/sys/syscall.c | 17 ++ modules/packetfs.c | 34 ++- .../patches/newlib/include/syscall_nums.h | 1 + userspace/gui/compositor/compositor.c | 246 ++++++++---------- 10 files changed, 312 insertions(+), 141 deletions(-) diff --git a/kernel/fs/pipe.c b/kernel/fs/pipe.c index 7a5441f1..5aa9cb97 100644 --- a/kernel/fs/pipe.c +++ b/kernel/fs/pipe.c @@ -71,6 +71,20 @@ static inline void pipe_increment_write_by(pipe_device_t * pipe, size_t amount) pipe->write_ptr = (pipe->write_ptr + amount) % pipe->size; } +static void pipe_alert_waiters(fs_node_t * fs_node) { + pipe_device_t * pipe = (pipe_device_t *)fs_node->device; + + if (pipe->alert_waiters) { + while (pipe->alert_waiters->head) { + node_t * node = list_dequeue(pipe->alert_waiters); + process_t * p = node->value; + process_alert_node(p, fs_node); + free(node); + } + + } +} + uint32_t read_pipe(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer) { assert(node->device != 0 && "Attempted to read from a fully-closed pipe."); @@ -166,6 +180,7 @@ uint32_t write_pipe(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *bu spin_unlock(pipe->lock_write); wakeup_queue(pipe->wait_queue_readers); + pipe_alert_waiters(node); if (written < size) { sleep_on(pipe->wait_queue_writers); } @@ -211,10 +226,39 @@ void close_pipe(fs_node_t * node) { return; } +static int pipe_check(fs_node_t * node) { + pipe_device_t * pipe = (pipe_device_t *)node->device; + + if (pipe_unread(pipe) > 0) { + return 0; + } + + return 1; +} + +static int pipe_wait(fs_node_t * node, void * process) { + pipe_device_t * pipe = (pipe_device_t *)node->device; + + if (!pipe->alert_waiters) { + pipe->alert_waiters = list_create(); + } + + list_insert(pipe->alert_waiters, process); + list_insert(((process_t *)process)->node_waits, pipe); + + return 0; +} + +static int pipe_match(fs_node_t * node, void * value) { + pipe_device_t * pipe = (pipe_device_t *)node->device; + return pipe == value; +} + fs_node_t * make_pipe(size_t size) { fs_node_t * fnode = malloc(sizeof(fs_node_t)); pipe_device_t * pipe = malloc(sizeof(pipe_device_t)); memset(fnode, 0, sizeof(fs_node_t)); + memset(pipe, 0, sizeof(pipe_device_t)); fnode->device = 0; fnode->name[0] = '\0'; @@ -232,6 +276,10 @@ fs_node_t * make_pipe(size_t size) { fnode->ioctl = NULL; /* TODO ioctls for pipes? maybe */ fnode->get_size = pipe_size; + fnode->selectcheck = pipe_check; + fnode->selectwait = pipe_wait; + fnode->match = pipe_match; + fnode->atime = now(); fnode->mtime = fnode->atime; fnode->ctime = fnode->atime; diff --git a/kernel/fs/vfs.c b/kernel/fs/vfs.c index d09a2049..b53bf2ed 100644 --- a/kernel/fs/vfs.c +++ b/kernel/fs/vfs.c @@ -89,6 +89,41 @@ static fs_node_t * vfs_mapper(void) { return fnode; } +/** + * selectcheck_fs: Check if a read from this file would block. + */ +int selectcheck_fs(fs_node_t * node) { + if (!node) return -1; + + if (node->selectcheck) { + return node->selectcheck(node); + } + + return -1; +} + +/** + * selectwait_fs: Inform a node that it should alert the current_process. + */ +int selectwait_fs(fs_node_t * node, void * process) { + if (!node) return -1; + + if (node->selectwait) { + return node->selectwait(node, process); + } + + return -1; +} + +/** + * fsnode_matches: Compare a node internally against a magic value to determine if the value belongs to that node. + */ +int fsnode_matches(fs_node_t * node, void * value) { + if (!node) return 0; + if (!node->match) return 0; + return node->match(node, value); +} + /** * read_fs: Read a file system node based on its underlying type. diff --git a/kernel/include/fs.h b/kernel/include/fs.h index 35dd9679..322d705a 100644 --- a/kernel/include/fs.h +++ b/kernel/include/fs.h @@ -51,6 +51,9 @@ typedef int (*get_size_type_t) (struct fs_node *); typedef int (*chmod_type_t) (struct fs_node *, int mode); typedef void (*symlink_type_t) (struct fs_node *, char * name, char * value); typedef int (*readlink_type_t) (struct fs_node *, char * buf, size_t size); +typedef int (*selectcheck_type_t) (struct fs_node *); +typedef int (*selectwait_type_t) (struct fs_node *, void * process); +typedef int (*match_type_t) (struct fs_node *, void * value); typedef struct fs_node { char name[256]; /* The filename. */ @@ -89,6 +92,10 @@ typedef struct fs_node { uint32_t offset; /* Offset for read operations XXX move this to new "file descriptor" entry */ int32_t refcount; uint32_t nlink; + + selectcheck_type_t selectcheck; + selectwait_type_t selectwait; + match_type_t match; } fs_node_t; struct dirent { @@ -140,6 +147,9 @@ int chmod_fs(fs_node_t *node, int mode); int unlink_fs(char * name); int symlink_fs(char * value, char * name); int readlink_fs(fs_node_t * node, char * buf, size_t size); +int selectcheck_fs(fs_node_t * node); +int selectwait_fs(fs_node_t * node, void * process); +int fsnode_matches(fs_node_t * node, void * value); void vfs_install(void); void * vfs_mount(char * path, fs_node_t * local_root); diff --git a/kernel/include/pipe.h b/kernel/include/pipe.h index 9cee78ce..bf3c0ab7 100644 --- a/kernel/include/pipe.h +++ b/kernel/include/pipe.h @@ -19,6 +19,7 @@ typedef struct _pipe_device { list_t * wait_queue_readers; list_t * wait_queue_writers; int dead; + list_t * alert_waiters; } pipe_device_t; fs_node_t * make_pipe(size_t size); diff --git a/kernel/include/process.h b/kernel/include/process.h index 5799d92b..13fbf529 100644 --- a/kernel/include/process.h +++ b/kernel/include/process.h @@ -103,6 +103,8 @@ typedef struct process { node_t * timed_sleep_node; uint8_t is_tasklet; volatile uint8_t sleep_interrupted; + list_t * node_waits; + int awoken_index; } process_t; typedef struct { @@ -134,6 +136,9 @@ extern volatile process_t * current_process; extern process_t * kernel_idle_task; extern list_t * process_list; +extern int process_wait_nodes(process_t * process,fs_node_t * nodes[]); +extern int process_alert_node(process_t * process, fs_node_t * fs_node); + typedef void (*tasklet_t) (void *, char *); extern int create_kernel_tasklet(tasklet_t tasklet, char * name, void * argp); diff --git a/kernel/sys/process.c b/kernel/sys/process.c index 631706a9..5b61ecf4 100644 --- a/kernel/sys/process.c +++ b/kernel/sys/process.c @@ -814,3 +814,59 @@ int waitpid(int pid, int * status, int options) { } while (1); } +int process_wait_nodes(process_t * process,fs_node_t * nodes[]) { + assert(!process->node_waits && "Tried to wait on nodes while already waiting on nodes."); + assert(nodes[0] && "Empty wait list."); + + fs_node_t ** n = nodes; + int index = 0; + do { + int result = selectcheck_fs(*n); + if (result < 0) { + debug_print(NOTICE, "An invalid descriptor was specified: %d (0x%x)", index, *n); + return -1; + } + if (result == 0) { + return index; + } + n++; + index++; + } while (*n); + + n = nodes; + process->node_waits = list_create(); + do { + if (selectwait_fs(*n, process) < 0) { + debug_print(NOTICE, "Bad selectwait? 0x%x", *n); + } + n++; + } while (*n); + + process->awoken_index = -1; + /* Wait. */ + switch_task(0); + + return process->awoken_index; +} + +int process_alert_node(process_t * process, fs_node_t * fs_node) { + if (!process->node_waits) { + return 0; /* Possibly already returned. Wait for another call. */ + } + + int index = 0; + foreach(node, process->node_waits) { + if (fsnode_matches(fs_node, node->value)) { + process->awoken_index = index; + list_free(process->node_waits); + free(process->node_waits); + process->node_waits = NULL; + make_process_ready(process); + return 0; + } + index++; + } + + return -1; +} + diff --git a/kernel/sys/syscall.c b/kernel/sys/syscall.c index 23add10d..5f058426 100644 --- a/kernel/sys/syscall.c +++ b/kernel/sys/syscall.c @@ -787,6 +787,22 @@ static int sys_lstat(char * file, uintptr_t st) { return result; } +static int sys_fswait(int c, int fds[]) { + PTR_VALIDATE(fds); + for (int i = 0; i < c; ++i) { + if (!FD_CHECK(fds[i])) return -1; + } + fs_node_t ** nodes = malloc(sizeof(fs_node_t *)*(c+1)); + for (int i = 0; i < c; ++i) { + nodes[i] = FD_ENTRY(fds[i]); + } + nodes[c] = NULL; + + int result = process_wait_nodes((process_t *)current_process, nodes); + free(nodes); + return result; +} + /* * System Call Internals */ @@ -839,6 +855,7 @@ static int (*syscalls[])() = { [SYS_SYMLINK] = sys_symlink, [SYS_READLINK] = sys_readlink, [SYS_LSTAT] = sys_lstat, + [SYS_FSWAIT] = sys_fswait, }; uint32_t num_syscalls = sizeof(syscalls) / sizeof(*syscalls); diff --git a/modules/packetfs.c b/modules/packetfs.c index c131dfbd..e7f4fed9 100644 --- a/modules/packetfs.c +++ b/modules/packetfs.c @@ -238,6 +238,32 @@ static void close_client(fs_node_t * node) { free(c); } +static int match_server(fs_node_t * node, void * value) { + pex_ex_t * p = (pex_ex_t *)node->device; + return fsnode_matches(p->server_pipe,value); +} +static int wait_server(fs_node_t * node, void * process) { + pex_ex_t * p = (pex_ex_t *)node->device; + return selectwait_fs(p->server_pipe, process); +} +static int check_server(fs_node_t * node) { + pex_ex_t * p = (pex_ex_t *)node->device; + return selectcheck_fs(p->server_pipe); +} + +static int match_client(fs_node_t * node, void * value) { + pex_client_t * c = (pex_client_t *)node->inode; + return fsnode_matches(c->pipe, value); +} +static int wait_client(fs_node_t * node, void * process) { + pex_client_t * c = (pex_client_t *)node->inode; + return selectwait_fs(c->pipe, process); +} +static int check_client(fs_node_t * node) { + pex_client_t * c = (pex_client_t *)node->inode; + return selectcheck_fs(c->pipe); +} + static void open_pex(fs_node_t * node, unsigned int flags) { pex_ex_t * t = (pex_ex_t *)(node->device); @@ -250,6 +276,9 @@ static void open_pex(fs_node_t * node, unsigned int flags) { node->read = read_server; node->write = write_server; node->ioctl = ioctl_server; + node->selectcheck = check_server; + node->selectwait = wait_server; + node->match = match_server; debug_print(INFO, "[pex] Server launched: %s", t->name); debug_print(INFO, "fs_node = 0x%x", node); } else if (!(flags & O_CREAT)) { @@ -261,6 +290,10 @@ static void open_pex(fs_node_t * node, unsigned int flags) { node->ioctl = ioctl_client; node->close = close_client; + node->selectcheck = check_client; + node->selectwait = wait_client; + node->match = match_client; + list_insert(t->clients, client); /* XXX: Send plumbing message to server for new client connection */ @@ -272,7 +305,6 @@ static void open_pex(fs_node_t * node, unsigned int flags) { return; } - static struct dirent * readdir_packetfs(fs_node_t *node, uint32_t index) { pex_t * p = (pex_t *)node->device; unsigned int i = 0; diff --git a/toolchain/patches/newlib/include/syscall_nums.h b/toolchain/patches/newlib/include/syscall_nums.h index 37b82960..42306fec 100644 --- a/toolchain/patches/newlib/include/syscall_nums.h +++ b/toolchain/patches/newlib/include/syscall_nums.h @@ -45,3 +45,4 @@ #define SYS_SYMLINK 56 #define SYS_READLINK 57 #define SYS_LSTAT 58 +#define SYS_FSWAIT 59 diff --git a/userspace/gui/compositor/compositor.c b/userspace/gui/compositor/compositor.c index 74511a5b..ab3a09c2 100644 --- a/userspace/gui/compositor/compositor.c +++ b/userspace/gui/compositor/compositor.c @@ -476,106 +476,10 @@ static void server_window_resize_finish(yutani_globals_t * yg, yutani_server_win mark_window(yg, win); } -/** - * Nested Yutani input thread - * - * Handles keyboard and mouse events, as well as - * other Yutani events from the nested window. - */ -void * nested_input(void * _yg) { - - yutani_globals_t * yg = _yg; - - yutani_t * y = yutani_init(); - - while (1) { - yutani_msg_t * m = yutani_poll(yg->host_context); - if (m) { - switch (m->type) { - case YUTANI_MSG_KEY_EVENT: - { - struct yutani_msg_key_event * ke = (void*)m->data; - yutani_msg_t * m_ = yutani_msg_build_key_event(0, &ke->event, &ke->state); - int result = yutani_msg_send(y, m_); - free(m_); - } - break; - case YUTANI_MSG_WINDOW_MOUSE_EVENT: - { - struct yutani_msg_window_mouse_event * me = (void*)m->data; - mouse_device_packet_t packet; - - packet.buttons = me->buttons; - packet.x_difference = me->new_x; - packet.y_difference = me->new_y; - - yutani_msg_t * m_ = yutani_msg_build_mouse_event(0, &packet, YUTANI_MOUSE_EVENT_TYPE_ABSOLUTE); - int result = yutani_msg_send(y, m_); - free(m_); - } - break; - case YUTANI_MSG_RESIZE_OFFER: - { - struct yutani_msg_window_resize * wr = (void*)m->data; - TRACE("Resize request from host compositor for size %dx%d", wr->width, wr->height); - yutani_window_resize_accept(yg->host_context, yg->host_window, wr->width, wr->height); - yg->resize_on_next = 1; - } - break; - case YUTANI_MSG_SESSION_END: - TRACE("Host session ended. Should exit."); - break; - default: - break; - } - } - free(m); - } -} - -/** - * Mouse input thread - * - * Reads the kernel mouse device and converts - * mouse clicks and movements into event objects - * to send to the core compositor. - */ -static uint32_t last_mouse_buttons = 0; -void * mouse_input(void * garbage) { - int mfd = open("/dev/mouse", O_RDONLY); - - yutani_t * y = yutani_init(); - mouse_device_packet_t packet; - - while (1) { - int r = read(mfd, (char *)&packet, sizeof(mouse_device_packet_t)); - if (r > 0) { - last_mouse_buttons = packet.buttons; - yutani_msg_t * m = yutani_msg_build_mouse_event(0, &packet, YUTANI_MOUSE_EVENT_TYPE_RELATIVE); - int result = yutani_msg_send(y, m); - free(m); - } - } -} - -void * mouse_input_abs(void * garbage) { - int mfd = open("/dev/absmouse", O_RDONLY); - - if (mfd == -1) return 0; - - yutani_t * y = yutani_init(); - mouse_device_packet_t packet; - - while (1) { - int r = read(mfd, (char *)&packet, sizeof(mouse_device_packet_t)); - if (r > 0) { - packet.buttons = last_mouse_buttons & 0xF; - yutani_msg_t * m = yutani_msg_build_mouse_event(0, &packet, YUTANI_MOUSE_EVENT_TYPE_ABSOLUTE); - int result = yutani_msg_send(y, m); - free(m); - } - } -} +#ifndef syscall_fswait +/* TODO: This isn't in our newlib syscall bindings yet. */ +DEFN_SYSCALL2(fswait,59,int,int*); +#endif void * timer_tick(void * _server) { (void)_server; @@ -589,32 +493,6 @@ void * timer_tick(void * _server) { } } -/** - * Keyboard input thread - * - * Reads the kernel keyboard device and converts - * key presses into event objects to send to the - * core compositor. - */ -void * keyboard_input(void * garbage) { - int kfd = open("/dev/kbd", O_RDONLY); - - yutani_t * y = yutani_init(); - key_event_t event; - key_event_state_t state = {0}; - - while (1) { - char buf[1]; - int r = read(kfd, buf, 1); - if (r > 0) { - kbd_scancode(&state, buf[0], &event); - yutani_msg_t * m = yutani_msg_build_key_event(0, &event, &state); - int result = yutani_msg_send(y, m); - free(m); - } - } -} - #define FONT_PATH "/usr/share/fonts/" #define FONT(a,b) {a, FONT_PATH b} @@ -2109,6 +1987,7 @@ int main(int argc, char * argv[]) { yg->host_context = yutani_init(); yg->host_window = yutani_window_create(yg->host_context, yutani_options.nest_width, yutani_options.nest_height); yutani_window_move(yg->host_context, yg->host_window, 50, 50); + yutani_window_advertise_icon(yg->host_context, yg->host_window, "Compositor", "compositor"); yg->backend_ctx = init_graphics_yutani_double_buffer(yg->host_window); } else { _static_yg = yg; @@ -2178,23 +2057,9 @@ int main(int argc, char * argv[]) { yutani_cairo_init(yg); - pthread_t mouse_thread; - pthread_t absmouse_thread; - pthread_t keyboard_thread; pthread_t render_thread; - pthread_t nested_thread; pthread_t timer_thread; - if (yutani_options.nested) { - /* Nested Yutani-Yutani mouse+keyboard */ - pthread_create(&nested_thread, NULL, nested_input, yg); - } else { - /* Toaru mouse+keyboard driver */ - pthread_create(&mouse_thread, NULL, mouse_input, NULL); - pthread_create(&absmouse_thread, NULL, mouse_input_abs, NULL); - pthread_create(&keyboard_thread, NULL, keyboard_input, NULL); - } - pthread_create(&render_thread, NULL, redraw, yg); pthread_create(&timer_thread, NULL, timer_tick, yg); @@ -2208,7 +2073,108 @@ int main(int argc, char * argv[]) { } } + int fds[4], mfd, kfd, amfd; + mouse_device_packet_t packet; + key_event_t event; + key_event_state_t state = {0}; + uint32_t last_mouse_buttons = 0; + + fds[0] = fileno(server); + + if (yutani_options.nested) { + fds[1] = fileno(yg->host_context->sock); + } else { + mfd = open("/dev/mouse", O_RDONLY); + kfd = open("/dev/kbd", O_RDONLY); + amfd = open("/dev/absmouse", O_RDONLY); + + fds[1] = mfd; + fds[2] = kfd; + fds[3] = amfd; + } + while (1) { + if (yutani_options.nested) { + int index = syscall_fswait(2, fds); + + if (index == 1) { + yutani_msg_t * m = yutani_poll(yg->host_context); + if (m) { + switch (m->type) { + case YUTANI_MSG_KEY_EVENT: + { + struct yutani_msg_key_event * ke = (void*)m->data; + yutani_msg_t * m_ = yutani_msg_build_key_event(0, &ke->event, &ke->state); + handle_key_event(yg, (struct yutani_msg_key_event *)m_->data); + free(m_); + } + break; + case YUTANI_MSG_WINDOW_MOUSE_EVENT: + { + struct yutani_msg_window_mouse_event * me = (void*)m->data; + mouse_device_packet_t packet; + + packet.buttons = me->buttons; + packet.x_difference = me->new_x; + packet.y_difference = me->new_y; + + yutani_msg_t * m_ = yutani_msg_build_mouse_event(0, &packet, YUTANI_MOUSE_EVENT_TYPE_ABSOLUTE); + handle_mouse_event(yg, (struct yutani_msg_mouse_event *)m_->data); + free(m_); + } + break; + case YUTANI_MSG_RESIZE_OFFER: + { + struct yutani_msg_window_resize * wr = (void*)m->data; + TRACE("Resize request from host compositor for size %dx%d", wr->width, wr->height); + yutani_window_resize_accept(yg->host_context, yg->host_window, wr->width, wr->height); + yg->resize_on_next = 1; + } + break; + case YUTANI_MSG_SESSION_END: + TRACE("Host session ended. Should exit."); + break; + default: + break; + } + } + free(m); + continue; + } + } else { + int index = syscall_fswait(amfd == -1 ? 3 : 4, fds); + + if (index == 2) { + char buf[1]; + int r = read(kfd, buf, 1); + if (r > 0) { + kbd_scancode(&state, buf[0], &event); + yutani_msg_t * m = yutani_msg_build_key_event(0, &event, &state); + handle_key_event(yg, (struct yutani_msg_key_event *)m->data); + free(m); + } + continue; + } else if (index == 1) { + int r = read(mfd, (char *)&packet, sizeof(mouse_device_packet_t)); + if (r > 0) { + last_mouse_buttons = packet.buttons; + yutani_msg_t * m = yutani_msg_build_mouse_event(0, &packet, YUTANI_MOUSE_EVENT_TYPE_RELATIVE); + handle_mouse_event(yg, (struct yutani_msg_mouse_event *)m->data); + free(m); + } + continue; + } else if (index == 3) { + int r = read(amfd, (char *)&packet, sizeof(mouse_device_packet_t)); + if (r > 0) { + packet.buttons = last_mouse_buttons & 0xF; + yutani_msg_t * m = yutani_msg_build_mouse_event(0, &packet, YUTANI_MOUSE_EVENT_TYPE_ABSOLUTE); + handle_mouse_event(yg, (struct yutani_msg_mouse_event *)m->data); + free(m); + } + continue; + } + } + pex_packet_t * p = calloc(PACKET_SIZE, 1); pex_listen(server, p);