karl@sugar.UUCP (Karl Lehenbauer) (10/30/87)
I have an 8-MHz AT clone with 5 MB RAM and a Digicomm Digiboard 8 eight-port serial I/O board. It runs Microport Unix System V/AT 2.2U. The inability of System V/AT to handle much high-speed input and it's tendency to hang ports or crash on hangups during I/O has been well documented and much discussed on the net. In this posting I will enumerate several thoughts I've had on the subject of speeding up serial I/O. This will include a simple algorithm for a very fast character queue that I haven't seen before. First, it appears that following the standard method of using ttypatch results in the V/AT driver looking at all the UARTs configured to use that interrupt. The Digiboard 8 (and I presume others) has a status register that returns a mask showing which UARTs have interrupts pending, one UART per bit. If the V/AT (or one provided by Digiboard or someone else) driver examined that register, it would consume less overhead during interrupt processing than looking at each UART's status register to determine its interrupt status. Digiboard (and again, I presume others) owners can configure the board on a port-by-port basis as to address and particularly, interrupt. Assuming the V/AT driver looks only at UARTs ttypatch'ed to be on the interrupt received and not at all UARTs every serial interrupt, splitting four ports to COM1 and four to COM2 (or whatever) reduces interrupt processing overhead. Also, if the above condition is true, users with one really fast line (uucp to direct machine or very high speed modem) may instead wish to give that line its own interrupt. To anyone who will respond "buy a smart board": I can't afford a smart board. I already have a dumb board, and it still costs over $500. To Microport or any other driver writer: Please consider hacking it out in assembly. It's a critical performance area, particularly on the AT since its performance is marginal. Also to Microport: I suppose you consider the source to your AT serial driver to be really really proprietary. I presume it was one of the bigger pieces of code you had to write yourselves. I request though, if you could, that you post the source. Then, many hackers will take a crack at speeding it up (like the aforementioned Digiboard status register stuff) and they'll do so for free. I would like to have a crack at it, but without a sample to work from, the job is much harder. Here is an idea for a real fast character queue. This example is for one port only, i.e. no indirection based on minor device number...for example purposes only: Fast character queue: There are two buffers. Each buffer can contain many characters, probably a few hundred. The writer owns one of the buffers and writes serially into it. The reader reads from the other buffer and, when it's exhausted, swaps it's buffer for the writer's buffer (if there's any data in the writers buffer). If the writer's buffer overflows, it's a buffer overflow - no attempt is made to switch to the reader's buffer. To have the same number of characters buffered as with a standard circular queue, each of the buffers would have to contain as many characters as the standard circular queue, thus more memory, but in this era it's not much of a consideration. #define QSIZE 512 /* number of characters in each queue */ int windex; /* 0-relative writer's index */ int rindex; /* 0-relative reader's index */ int lastbyte; /* last byte (0-relative) of data in reader's queue */ char *wbp, *rbp, *tmpp; /* writer's and reader's and temp buffer pointers */ init() { wbp = &buffer_a[0]; /* write buffer pointer gets one buffer */ rbp = &buffer_b[0]; /* read buffer pointer gets the other */ rindex = windex = 0; /* set indexes to zero, no data in bufs */ } wq(c) /* write a character into the queue */ char c; { get(writelock); /* semaphore lock for writer's buf and vars */ if (windex > QSIZE) overflow; wbp[windex++] = c; /* write the char into the writer's buffer */ release(writelock); } int rq() /* read a character from the queue */ { if (rindex < lastbyte) /* there's data in the read buffer */ return(rbp[rindex++]); /* return the next byte */ get(writelock); /* There's no data in the read buffer, */ if (windex == 0) /* Is there data in write buffer? */ { release(writelock); /* no, blow out */ return empty; } tmpp = wbp; /* yes, swap reader's and writer's buffers */ wbp = rbp; lastbyte = windex; /* get n chars written in for reader */ windex = 0; /* set writer's index to 0 for new buf */ release(writelock); rindex = 1; /* set reader to the second char, we're * returning the first one */ rbp = tmpp; /* finish swap outside of the lock for speed */ return(rbp[0]); } The advantage over the usual circular queue is the algorithm doesn't have to do the usual "if ((inp + 1) mod QSIZE == outp" business; that is, it doesn't have to handle rolling around the end of the queue. A multibyte read would be a useful hack for speed improvement, as well. Very large buffers (several K bytes) might buy some breathing room. Note that this algorithm, because of its simplicity, is well suited for implementation in assembly languangua
greg@xios.XIOS.UUCP (Greg Franks) (11/06/87)
In article <943@sugar.UUCP> karl@sugar.UUCP (Karl Lehenbauer) writes
about how to do a tty driver, multiport cards etc.
Multiport cards: the one we have (Bell Tech HUB), comes with its own
driver. I don't think that uport's tty drivers handle many multiport
cards (they are all different...).
Re assembly code to buffer the characters: they already do it in the
standard drivers. It is still too slow at times. And yes, they have
special buffers. Remember: any driver (including sio) that sits at spl7
toooooo long kills the uarts.
Hopefully, version 2.3 will fix all this - we're waiting with baited
breath for our copies to arrive!
--
Greg Franks XIOS Systems Corporation, 1600 Carling Avenue,
(613) 725-5411 Ottawa, Ontario, Canada, K1Z 8R8
uunet!mnetor!dciem!nrcaer!xios!greg "There's so much to sea in Nova Scotia"