Interrupts and PIO within Linux

Richard B. Johnson (root@chaos.analogic.com)
Thu, 16 Oct 1997 22:25:53 -0400 (EDT)


Sorry this is large...

This is intended to be a definitive report on the Intel
REP OUTSB and
REP OUTSW
REP OUTSD
operations used within the Linux Kernel.

o These instructions have been used since the first
PC/AT for loading the hard disk sector buffer which is a
hardware FIFO. The BIOS source code is provided in the IBM
Technical Reference Manual for the IBM Personal Computer AT.

o These instructions have also been used for loading
Serial Network Interface Controllers (SNICS) in various
Network cards.

o The Intel reference manuals, including the Intel 486
Programmer's Reference Manual document their use. ISBN 1-
55512-159-4. Page 26-245 and Page 26-227.

o These instructions allow a programmer to use the
Microprocessor as a DMA controller, reading data from memory
and writing it to a port. The source address memory pointer
is automatically adjusted and a counter is automatically
decremented during the Microprocessor's execution of these
codes. The data transfer operation stops when the counter
register is decremented to zero. This is generally called
Programmed I/O (PIO) operation.

After each transfer, and after the counter is decremented,
the CPU will acknowledge any pending interrupt. As long as
the values within the appropriate registers are saved and
then restored by the ISR, operation will continue after an
interrupt. If the registers ESI(si), EDX(dx), ECX(cx), or
the direction flag is changed, operation is undefined.
Otherwise, operation continues after interrupt execution.

o Unlike DMA, the CPU is used continually for the
operation. Therefore, in many applications, these
instructions may not be the most efficient way to transfer
data from memory to a port. However, these instructions
provide the fastest method possible within the PC/AT
Architecture.

Data transfer is so fast that the cited reference warns that
all ports may not be suitable for such operation. A proper
port uses the I/O Channel Ready line as a throttle. Note
that DMA Controllers use DRQ/DACK for the same purpose.

Also DMA controllers are generally not allowed to use the
bus for an entire data transfer length of a buffer. DMA
Controllers send bursts. This is why PIO is faster than DMA.
However, as previously stated, total system performance may
be slower without a DMA Controller because the CPU can't be
used for something else during the data transfer.

o Since the OUTSx instructions are interruptible, there
should not be any reason for disabling interrupts during PIO
operation. Unless.....

(1) The procedure using PIO can be recursively called from
an interrupt.

(2) Code executing during an interrupt can modify data
that has not yet been sent out the port.

(3) Code executing during an interrupt can modify the
controller's usage of these data.

o Allowing interrupts during PIO operation potentially
slows down data transfer. There is no known mechanism by
which slowing down data flow could cause an overrun, while
at the same time, running at full speed does not.

o PIO operation transfers data to a hardware FIFO. It
does not transfer data to the heads of a Disk Drive. It is
only after data fills the FIFO that hardware serializes it,
converts it to NRZ (or other non-polarizing format), and
feeds it to disk-drive heads. These operations are
synchronized by hardware. Software does not need to "keep
up".

For the fastest operation, software should "keep up" and
have new data available for the next sector before the head
gets to that sector. However, failure to do this just makes
the machine slower.

o In Linux there are many macros used for strange things
like "slowing down I/O" (read from the keyboard controller
port between each I/O operation). These macros were used to
fix something.

What has happened in the past was that something was getting
trashed so the programmer found that by delaying the
operation a little bit, the problem(s) went away.

Much of the Linux Code has matured greatly since those
macros were first introduced. This may mean that the real
problems that slowing down I/O masked, have now gone away so
they are no longer needed.

My '486-DX/66 at home uses Linux version 2.0.12. It uses PIO
for feeding data to a Network SNIC, and has all of it's
slow-down I/O macros changed to no-ops. It has been running
for quite some time with an Adaptec AHA-1542 (DMA), and a 3-
COM 3c503 (PIO). We know that 2.0.12 has several bugs that
have been fixed in later versions. None of these bugs have
resulted in visibly incorrect operation.

The only bug-fix I added was to mask off (using the
controller) each interrupt until its respective ISR
completed. Further, I ACKed each interrupt with a SPECIFIC
EOI immediately after masking it off. This allows the
hardware to queue a new interrupt while the present ISR is
executing. This code is similar to the code used within the
newer kernels.

Early Network code allowed network ISRs to be interrupted by
new network interrupts. The results were predictably
problematical. Later versions fixed these problems (Top
level interrupt code masks each interrupt now).

It would be useful for some respected Programmer to
investigate whether or not many of the "fixes" such as
"slow down I/O" and clearing interrupts during PIO, are
really necessary any more. About a year ago, I rewrote a
portion of several Network Card Drivers to get rid of these
kludges. Unfortunately, I am not a "respected" Programmer
within the Linux community so the result was several days of
email flames in spite of the fact that I tested all the
patches on several different machines with some real killer
I/O on dedicated links (continuous 1460-byte broadcast).

o What has apparently happened is some dedicated
Programmers have spent major portions of their lifetimes
getting drivers to work. They are naturally quite reluctant
to allow their hard work to be modified in spite of the very
real possibility that the "fixes" that they devised are no
longer needed and some are reducing the performance of the
machines.

o Kernels can be readily modified by compile-time
options. However, we can't really use boot-time options
because the "compare-and-jump" operations could reduce
performance by more than we could gain with the
modifications.

Cheers,
Dick Johnson

Richard B. Johnson
Project Engineer
Analogic Corporation
Penguin : Linux version 2.1.55 on an i586 machine (66.15 BogoMips).
Warning : It's hard to remain at the trailing edge of technology.