From 0cf552e48ea5ace5a1ed1178f56b715c8f2eaed6 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 12 Sep 2016 17:39:15 +0100 Subject: [PATCH] select: first cut at select handling Some bits probably want a redesign but the basics are now present and we can start debugging the code meaningfully. --- Kernel/dev/net/net_native.c | 33 +++++++++++++---- Kernel/include/kernel.h | 7 ++-- Kernel/include/tty.h | 2 +- Kernel/platform-z80pack/config.h | 2 +- Kernel/select.c | 53 +++++++++++++++++++++------ Kernel/tty.c | 62 +++++++++++++++++++++++++++++++- 6 files changed, 137 insertions(+), 22 deletions(-) diff --git a/Kernel/dev/net/net_native.c b/Kernel/dev/net/net_native.c index 16ac86bc..8a175bd4 100644 --- a/Kernel/dev/net/net_native.c +++ b/Kernel/dev/net/net_native.c @@ -15,6 +15,7 @@ static void wakeup_all(struct socket *s) wakeup(s); wakeup(&s->s_data); wakeup(&s->s_iflag); + selwake_dev(4, 65, SELECT_IN); } /* @@ -113,6 +114,17 @@ static int netdev_report(struct sockdata *sd) * the first one we find at the daemon. We should possibly round-robin * these but it's not clear it's that important */ +static struct sockdata *netdev_findevent(void) +{ + struct sockdata *sd = sockdata; + while (sd != sockdata + NSOCKET) { + if (sd->event) + return sd; + sd++; + } + return NULL; +} + int netdev_read(uint8_t flag) { if (net_ino == NULL || udata.u_count != sizeof(struct sockmsg)) { @@ -120,12 +132,9 @@ int netdev_read(uint8_t flag) return -1; } while(1) { - struct sockdata *sd = sockdata; - while (sd != sockdata + NSOCKET) { - if (sd->event) - return netdev_report(sd); - sd++; - } + struct sockdata *sd = netdev_findevent(); + if (sd) + return netdev_report(sd); if (psleep_flags(&ne, flag)) return -1; } @@ -152,7 +161,15 @@ int netdev_ioctl(uarg_t request, char *data) if ((net_ino = getinode(fd)) == NULLINODE) return -1; i_ref(net_ino); - return 0; + return 0; + case SELECT_BEGIN: + case SELECT_TEST: + /* We are always writable, but may not alway be readable */ + if (*data & SELECT_IN) { + if (netdev_findevent() == NULL) + *data &= ~SELECT_IN; + } + *data &= SELECT_IN|SELECT_OUT; } udata.u_error = ENOTTY; return -1; @@ -193,6 +210,7 @@ static int netn_synchronous_event(struct socket *s, uint8_t state) sd->event |= NEV_STATE | NEVW_STATE; sd->newstate = state; wakeup(&ne); + selwake_dev(4, 65, SELECT_IN); do { if( s->s_state == SS_CLOSED ) @@ -213,6 +231,7 @@ static void netn_asynchronous_event(struct socket *s, uint8_t event) struct sockdata *sd = s->s_priv; sd->event |= event; wakeup(&ne); + selwake_dev(4, 65, SELECT_IN); } /* General purpose ring buffer operator. Non re-entrant so use every diff --git a/Kernel/include/kernel.h b/Kernel/include/kernel.h index 6b8e9eef..58928753 100644 --- a/Kernel/include/kernel.h +++ b/Kernel/include/kernel.h @@ -598,7 +598,8 @@ struct s_argblk { * by userspace. */ #define SELECT_BEGIN 0x8000 -#define SELECT_END 0x8001 +#define SELECT_TEST 0x8001 +#define SELECT_END 0x8002 #define IOCTL_NORMAL 0x0000 /* No special rules */ #define IOCTL_SUPER 0x4000 /* Superuser needed */ @@ -825,18 +826,20 @@ extern void exec_or_die(void); /* select.c */ +#ifdef CONFIG_LEVEL_2 extern void seladdwait(struct selmap *s); extern void selrmwait(struct selmap *s); extern void selwake(struct selmap *s); -#ifdef CONFIG_LEVEL_2 extern void selwait_inode(inoptr i, uint8_t smask, uint8_t setit); extern void selwake_inode(inoptr i, uint16_t mask); extern void selwake_pipe(inoptr i, uint16_t mask); +extern void selwake_dev(uint8_t major, uint8_t minor, uint16_t mask); extern int _select(void); #else #define selwait_inode(i,smask,setit) do {} while(0) #define selwake_inode(i,smask) do {} while(0) #define selwake_pipe(i,smask) do {} while(0) +#define selwake_dev(major,minor,smask) do {} while(0) #endif /* swap.c */ diff --git a/Kernel/include/tty.h b/Kernel/include/tty.h index dc18afda..91ef1360 100644 --- a/Kernel/include/tty.h +++ b/Kernel/include/tty.h @@ -226,7 +226,7 @@ extern void tty_putc_wait(uint8_t minor, unsigned char c); typedef enum { TTY_READY_NOW=1, /* port is ready immediately */ TTY_READY_SOON=0, /* we'll be ready shortly, kernel should spin, polling the port repeatedly */ - TTY_READY_LATER=-1 /* we'll be a long time, put this process to sleep and schedule another */ + TTY_READY_LATER=2 /* we'll be a long time, put this process to sleep and schedule another */ } ttyready_t; /* provided by platform */ diff --git a/Kernel/platform-z80pack/config.h b/Kernel/platform-z80pack/config.h index 521e06d1..b0fdc1c3 100644 --- a/Kernel/platform-z80pack/config.h +++ b/Kernel/platform-z80pack/config.h @@ -1,7 +1,7 @@ /* We have an RTC */ #define CONFIG_RTC /* Enable to make ^Z dump the inode table for debug */ -#undef CONFIG_IDUMP +#define CONFIG_IDUMP /* Enable to make ^A drop back into the monitor */ #undef CONFIG_MONITOR /* Profil syscall support (not yet complete) */ diff --git a/Kernel/select.c b/Kernel/select.c index 871e31d1..2a19b360 100644 --- a/Kernel/select.c +++ b/Kernel/select.c @@ -80,6 +80,7 @@ void selwait_inode(inoptr i, uint8_t smask, uint8_t setit) void selwake_inode(inoptr i, uint16_t mask) { struct selmap *s = (struct selmap *) (&i->c_node.i_addr[17]); + irqflags_t irq = di(); if (mask & SELECT_IN) selwake(s); s++; @@ -88,6 +89,21 @@ void selwake_inode(inoptr i, uint16_t mask) s++; if (mask & SELECT_EX) selwake(s); + irqrestore(irq); +} + +void selwake_dev(uint8_t major, uint8_t minor, uint16_t mask) +{ + irqflags_t irq = di(); + uint16_t v = (major << 8) | minor; + inoptr i = i_tab; + + while (i <= &i_tab[ITABSIZE - 1]) { /* Convoluted form to keep SDCC happy */ + if (i->c_refs && i->c_node.i_addr[0] == v) + selwake_inode(i, mask); + i++; + } + irqrestore(irq); } static int pipesel_begin(inoptr i, uint8_t bits) @@ -128,22 +144,28 @@ void selwake_pipe(inoptr i, uint16_t mask) int _select(void) { + irqflags_t irq; + uint16_t seltype = SELECT_BEGIN; inoptr ino; uint16_t sumo; uint8_t i, m, n; uint16_t inr = 0, outr = 0, exr = 0; /* Second 16bits of each spare for expansion */ uint16_t in = ugetw(base); - uint16_t out = ugetw(base + 4); - uint16_t ex = ugetw(base + 8); + uint16_t out = ugetw(base + 2); + uint16_t ex = ugetw(base + 4); uint16_t sum = in | out | ex; /* Timeout in 1/10th of a second (BSD api mangling done by libc) */ - udata.u_ptab->p_timeout = ugetw(base + 12); + /* 0 means return immediately, need to sort out a 'forever' FIXME */ + udata.u_ptab->p_timeout = ugetw(base + 6); do { m = 1; + + irq = di(); + for (i = 0; i < nfd; i++) { if (sum & m) { if (in & m) @@ -172,10 +194,11 @@ int _select(void) /* If unsupported we report the device as read/write ready */ if (d_ioctl (ino->c_node.i_addr[0], - SELECT_BEGIN, &n) == -1) { + seltype, &n) == -1) { udata.u_error = 0; n = SELECT_IN | SELECT_OUT; - } + } else if (seltype == SELECT_BEGIN) + selwait_inode(ino, n, 1); setbits: /* Set the outputs */ if (n & SELECT_IN) @@ -192,19 +215,25 @@ int _select(void) inr &= in; /* Don't reply with bits not being selected */ outr &= out; exr &= ex; + /* FIXME lock against time race */ sumo = inr | outr | exr; /* Are we there yet ? */ - if (!sumo - && psleep_flags(&udata.u_ptab->p_timeout, 0) == -1) + /* No successes, wait requested, not timed out */ + if (!sumo && udata.u_ptab->p_timeout > 1 && + psleep_flags(&udata.u_ptab->p_timeout, 0) == -1) break; + irqrestore(irq); + seltype = SELECT_TEST; } - while (!sumo && udata.u_ptab->p_timeout != 1); + while (!sumo && udata.u_ptab->p_timeout > 1); + + irqrestore(irq); udata.u_ptab->p_timeout = 0; /* Return the values to user space */ uputw(inr, base); - uputw(outr, base + 4); - uputw(exr, base + 8); + uputw(outr, base + 2); + uputw(exr, base + 4); /* Tell the device less people care, we may want to remove all this and just select check. The 0 check we could do instead is cheap */ @@ -216,6 +245,10 @@ int _select(void) case MODE_R(F_CDEV): d_ioctl(ino->c_node.i_addr[0], SELECT_END, NULL); + /* Kill the wait */ + selwait_inode(ino, SELECT_IN|SELECT_OUT|SELECT_EX, 0); + /* May be unknown in which case ignore */ + udata.u_error = 0; break; case MODE_R(F_PIPE): pipesel_end(ino); diff --git a/Kernel/tty.c b/Kernel/tty.c index 518edc06..56d6e5aa 100644 --- a/Kernel/tty.c +++ b/Kernel/tty.c @@ -18,6 +18,21 @@ struct tty ttydata[NUM_DEV_TTY + 1]; /* ttydata[0] is not used */ +#ifdef CONFIG_LEVEL_2 +static uint16_t tty_select; /* Fast path if no selects, could do with being per tty ? */ + +/* Might be worth tracking tty minor <> inode for performance FIXME */ +static void tty_selwake(uint8_t minor, uint16_t event) +{ + if (tty_select) { + /* 2 is the tty devices */ + selwake_dev(2, minor, event); + } +} +#else +#define tty_selwake(a,b) do {} while(0) +#endif + int tty_read(uint8_t minor, uint8_t rawflag, uint8_t flag) { usize_t nread; @@ -223,11 +238,21 @@ void tty_exit(void) int tty_ioctl(uint8_t minor, uarg_t request, char *data) { /* Data in User Space */ struct tty *t; + if (minor > NUM_DEV_TTY + 1) { udata.u_error = ENODEV; return -1; } t = &ttydata[minor]; + + /* Special case select ending after a hangup */ +#ifdef CONFIG_LEVEL_2 + if (request == SELECT_END) { + tty_select--; + return 0; + } +#endif + if (jobcontrol_ioctl(minor, t, request)) return -1; if (t->flag & TTYF_DEAD) { @@ -249,11 +274,13 @@ int tty_ioctl(uint8_t minor, uarg_t request, char *data) if (uget(data, &t->termios, sizeof(struct termios)) == -1) return -1; tty_setup(minor); + tty_selwake(minor, SELECT_IN|SELECT_OUT); break; case TIOCINQ: return uput(&ttyinq[minor].q_count, data, 2); case TIOCFLUSH: clrq(&ttyinq[minor]); + tty_selwake(minor, SELECT_OUT); break; case TIOCHANGUP: tty_hangup(minor); @@ -263,6 +290,7 @@ int tty_ioctl(uint8_t minor, uarg_t request, char *data) break; case TIOCOSTART: t->flag &= ~TTYF_STOP; + tty_selwake(minor, SELECT_OUT); break; case TIOCGWINSZ: return uput(&t->winsize, data, sizeof(struct winsize)); @@ -281,6 +309,31 @@ int tty_ioctl(uint8_t minor, uarg_t request, char *data) return -1; } return tcsetpgrp(t, data); + case SELECT_BEGIN: + tty_select++; + /* Fall through */ + case SELECT_TEST: + { + uint8_t n = *data; + *data = 0; + if (n & SELECT_EX) { + if (t->flag & TTYF_DEAD) { + *data = SELECT_IN|SELECT_EX; + return 0; + } + } + /* FIXME: IRQ race */ + if (n & SELECT_IN) { + /* TODO - this one is hard, we need to peek down the queue to see if + a canonical input would succeed */ + } + if (n & SELECT_OUT) { + if ((!(t->flag & TTYF_STOP)) && + tty_writeready(minor) != TTY_READY_LATER) + *data |= SELECT_OUT; + } + return 0; + } #endif default: udata.u_error = ENOTTY; @@ -356,6 +409,7 @@ sigout: if (c == t->termios.c_cc[VSTART]) { /* ^Q */ t->flag &= ~TTYF_STOP; wakeup(&t->flag); + tty_selwake(minor, SELECT_OUT); return 1; } } @@ -382,8 +436,10 @@ sigout: tty_putc(minor, '\007'); /* Beep if no more room */ if (!canon || c == t->termios.c_cc[VEOL] || c == '\n' - || c == t->termios.c_cc[VEOF]) + || c == t->termios.c_cc[VEOF]) { wakeup(q); + tty_selwake(minor, SELECT_IN); + } return wr; eraseout: @@ -404,6 +460,7 @@ eraseout: void tty_outproc(uint8_t minor) { wakeup(&ttydata[minor]); + tty_selwake(minor, SELECT_OUT); } void tty_echo(uint8_t minor, unsigned char c) @@ -462,6 +519,7 @@ void tty_hangup(uint8_t minor) /* Wake stopped stuff */ wakeup(&t->flag); /* and deadflag will clear when the last user goes away */ + tty_selwake(minor, SELECT_IN|SELECT_OUT|SELECT_EX); } void tty_carrier_drop(uint8_t minor) @@ -473,6 +531,7 @@ void tty_carrier_drop(uint8_t minor) void tty_carrier_raise(uint8_t minor) { wakeup(&ttydata[minor].termios.c_cflag); + tty_selwake(minor, SELECT_IN|SELECT_OUT); } /* @@ -581,6 +640,7 @@ void pty_putc_wait(uint8_t minor, char c) struct s_queue q = &ptyq[minor + PTY_OFFSET + PTY_PAIR]; /* tty output queue to pty */ insq(q, c); + /* FIXME: select */ wakeup(q); } #endif -- 2.34.1