select: first cut at select handling
authorAlan Cox <alan@linux.intel.com>
Mon, 12 Sep 2016 16:39:15 +0000 (17:39 +0100)
committerAlan Cox <alan@linux.intel.com>
Mon, 12 Sep 2016 16:39:15 +0000 (17:39 +0100)
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
Kernel/include/kernel.h
Kernel/include/tty.h
Kernel/platform-z80pack/config.h
Kernel/select.c
Kernel/tty.c

index 16ac86b..8a175bd 100644 (file)
@@ -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
index 6b8e9ee..5892875 100644 (file)
@@ -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 */
index dc18afd..91ef136 100644 (file)
@@ -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 */
index 521e06d..b0fdc1c 100644 (file)
@@ -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) */
index 871e31d..2a19b36 100644 (file)
@@ -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);
index 518edc0..56d6e5a 100644 (file)
 
 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