From 9889b47b5216bdcd301e2b63baf71683bcbb22af Mon Sep 17 00:00:00 2001
From: dzwdz
Date: Sat, 3 Sep 2022 14:56:05 +0200
Subject: driver/ps2,serial: handle all pending reads at the same time

---
 src/kernel/arch/amd64/driver/ps2.c    | 10 ++++++----
 src/kernel/arch/amd64/driver/serial.c | 19 ++++++-------------
 src/kernel/arch/amd64/driver/util.c   | 22 ++++++++++++++++++++++
 src/kernel/arch/amd64/driver/util.h   |  5 +++++
 4 files changed, 39 insertions(+), 17 deletions(-)

(limited to 'src')

diff --git a/src/kernel/arch/amd64/driver/ps2.c b/src/kernel/arch/amd64/driver/ps2.c
index 196b890..51aabea 100644
--- a/src/kernel/arch/amd64/driver/ps2.c
+++ b/src/kernel/arch/amd64/driver/ps2.c
@@ -63,10 +63,10 @@ void ps2_irq(void) {
 		if (!(status & 1)) break; /* read while data available */
 		if (status & (1 << 5)) {
 			ring_put1b((void*)&mouse_backlog, port_in8(PS2));
-			postqueue_pop(&mouse_queue, accept);
+			postqueue_ringreadall(&mouse_queue, (void*)&mouse_backlog);
 		} else {
 			ring_put1b((void*)&kb_backlog, port_in8(PS2));
-			postqueue_pop(&kb_queue, accept);
+			postqueue_ringreadall(&kb_queue, (void*)&kb_backlog);
 		}
 	}
 }
@@ -107,9 +107,11 @@ static void accept(struct vfs_request *req) {
 				ret = req_readcopy(req, data, sizeof data);
 				vfsreq_finish_short(req, ret);
 			} else if ((long __force)req->id == H_KB) {
-				read_backlog(req, (void*)&kb_backlog, &kb_queue);
+				postqueue_join(&kb_queue, req);
+				postqueue_ringreadall(&kb_queue, (void*)&kb_backlog);
 			} else if ((long __force)req->id == H_MOUSE) {
-				read_backlog(req, (void*)&mouse_backlog, &mouse_queue);
+				postqueue_join(&mouse_queue, req);
+				postqueue_ringreadall(&mouse_queue, (void*)&mouse_backlog);
 			} else panic_invalid_state();
 			break;
 		default:
diff --git a/src/kernel/arch/amd64/driver/serial.c b/src/kernel/arch/amd64/driver/serial.c
index 2b0812b..295f9b8 100644
--- a/src/kernel/arch/amd64/driver/serial.c
+++ b/src/kernel/arch/amd64/driver/serial.c
@@ -3,9 +3,10 @@
 #include <kernel/arch/amd64/interrupts/irq.h>
 #include <kernel/arch/amd64/port_io.h>
 #include <kernel/mem/virt.h>
-#include <kernel/ring.h>
 #include <kernel/panic.h>
 #include <kernel/proc.h>
+#include <kernel/ring.h>
+#include <kernel/util.h>
 #include <stdint.h>
 
 static volatile uint8_t backlog_buf[64];
@@ -14,7 +15,7 @@ static volatile ring_t backlog = {(void*)backlog_buf, sizeof backlog_buf, 0, 0};
 static const int COM1 = 0x3f8;
 
 static void accept(struct vfs_request *req);
-static struct vfs_request *blocked_on = NULL;
+static struct vfs_request *hung_reads = NULL;
 void serial_init(void) { vfs_root_register("/com1", accept); }
 
 
@@ -44,7 +45,7 @@ void serial_preinit(void) {
 
 void serial_irq(void) {
 	ring_put1b((void*)&backlog, port_in8(COM1));
-	postqueue_pop(&blocked_on, accept);
+	postqueue_ringreadall(&hung_reads, (void*)&backlog);
 }
 
 
@@ -58,7 +59,6 @@ void serial_write(const char *buf, size_t len) {
 		serial_putchar(buf[i]);
 }
 
-
 static void accept(struct vfs_request *req) {
 	int ret;
 	bool valid;
@@ -68,15 +68,8 @@ static void accept(struct vfs_request *req) {
 			vfsreq_finish_short(req, valid ? 0 : -1);
 			break;
 		case VFSOP_READ:
-			if (ring_used((void*)&backlog) == 0) {
-				postqueue_join(&blocked_on, req);
-			} else if (req->caller) {
-				ret = ring_to_virt((void*)&backlog, req->caller->pages, req->output.buf, req->output.len);
-				// TODO output.len can overflow here
-				vfsreq_finish_short(req, ret);
-			} else {
-				vfsreq_finish_short(req, -1);
-			}
+			postqueue_join(&hung_reads, req);
+			postqueue_ringreadall(&hung_reads, (void*)&backlog);
 			break;
 		case VFSOP_WRITE:
 			if (req->caller && !req->flags) {
diff --git a/src/kernel/arch/amd64/driver/util.c b/src/kernel/arch/amd64/driver/util.c
index 4eeaa0d..b8895b8 100644
--- a/src/kernel/arch/amd64/driver/util.c
+++ b/src/kernel/arch/amd64/driver/util.c
@@ -32,3 +32,25 @@ bool postqueue_pop(struct vfs_request **queue, void (*accept)(struct vfs_request
 	accept(req);
 	return true;
 }
+
+void postqueue_ringreadall(struct vfs_request **queue, ring_t *r) {
+	struct vfs_request *req;
+	char tmp[64];
+	size_t mlen = 0;
+	if (ring_used(r) == 0) return;
+
+	/* read as much as the biggest request wants */
+	for (req = *queue; req; req = req->postqueue_next)
+		mlen = max(mlen, req->output.len);
+	mlen = min(mlen, sizeof tmp);
+	mlen = ring_get(r, tmp, mlen);
+
+	for (req = *queue; req; req = req->postqueue_next) {
+		size_t ret = min(mlen, req->output.len);
+		assert(req->type == VFSOP_READ);
+		if (req->caller)
+			virt_cpy_to(req->caller->pages, req->output.buf, tmp, ret);
+		vfsreq_finish_short(req, ret);
+	}
+	*queue = NULL;
+}
diff --git a/src/kernel/arch/amd64/driver/util.h b/src/kernel/arch/amd64/driver/util.h
index d89992a..06ca672 100644
--- a/src/kernel/arch/amd64/driver/util.h
+++ b/src/kernel/arch/amd64/driver/util.h
@@ -1,4 +1,5 @@
 #pragma once
+#include <shared/container/ring.h>
 #include <stdbool.h>
 #include <stddef.h>
 
@@ -14,3 +15,7 @@ int req_readcopy(struct vfs_request *req, const void *buf, size_t len);
 
 void postqueue_join(struct vfs_request **queue, struct vfs_request *req);
 bool postqueue_pop(struct vfs_request **queue, void (*accept)(struct vfs_request *));
+
+/** If there are any pending read requests, and the ring buffer isn't empty, fulfill them
+ * all with a single read. */
+void postqueue_ringreadall(struct vfs_request **queue, ring_t *r);
-- 
cgit v1.2.3