On Mon, Jan 2, 2017 at 7:48 PM, Michael Clark <
michae...@mac.com> wrote:
> Not all devices use DMA. Many just have buffers that they expose via MMIO
> regions. I am thinking of using circular buffers in an MMIO aperture
> (instead of DMA) and this seems similar to the VirtIO Channel IO
> implementation for S390. I guess this is like programmed IO and has lower
> performance.
From the context of the conversation so far, it's not clear to me that
background is known what Channel I/O is, so I'm going to summarize
below. Ignore if I am mistaken.
Channel I/O is most definitely a DMA-based communications method.
Devices operate as if they were independent computers (in fact, they
always are; all "controllers" on a channel are at least minimally
intelligent). A channel "program" is an effectively Turing-complete
(device support pending) program where each instruction is capable of
DMAing some number of bytes into or out of RAM. Each of these
instructions is called a Channel Control Word, or CCW.
Without going into too much detail, imagine:
struct CCW {
uint8_t command, flags;
void *buffer;
uint16_t length;
}
A channel program, then, can be written:
struct CCW channelProgram[PRG_LENGTH] = { ... };
The s390 kicks off a program by executing a SIO instruction (or, I
think on more modern machines, SSCH instruction):
int16_t deviceID;
sio(deviceID, &channelProgram); /* or */ ssch(deviceID, &channelProgram);
CCW.command includes the following four operations (there are more,
but are basically variations on the theme):
- read -- transfers data from the device into a buffer in memory.
Upon completion, CCW.length == number of bytes remaining to be
transferred, while CCW.buffer -> next byte to place said data into.
In effect, the hardware PCLSRs.
- write -- The same, except it sends data to the device.
- sense -- transfers data from the addressed device's *controller*
into a buffer in memory. This is used to retrieve the results of
sending a controller a "command", or sensing some other current status
indication.
- command -- transfers a command to the addressed device's controller.
- Transfer In Channel -- a normally unconditional jump. Destination
address is CCW.buffer; all other fields ignored.
You can configure a "sense" CCW (at least; not sure of others) so that
a subsequent CCW is skipped if some arbitrary (device-dependent)
boolean condition is found to be true. Thus, TIC instructions, while
technically unconditional, are used as conditional jumps in practice.
Another way of achieving Turing completeness is that CCWs often use
other CCWs as buffers to effect self-modifying channel programs. In
fact, this is how the IBM mainframe family *boots*!
Properties of Channel I/O include:
- It's message oriented. Commands are often sent to the device via
the upper bits of CCW.command if they're simple enough; if not,
command/sense CCWs are used to pass messages between the mainframe and
the peripheral a la how Plan 9 uses control files as out-of-band means
of talking to filesystem drivers, or how Linux might use ioctl().
Read/write commands *NEVER* frame their data (unless such framing is
deliberately part of the payload to be transferred), because doing so
invalidates their use in scatter/gather DMA scenarios.
- They're completely asynchronous. Of the thousands of instructions
the S390 and z/Architecture have today, only four are dedicated to I/O
(or, at least, are used with such regularity that this statement is
true in principle, if not in fact). All I/O happens in the
background, and is fully asynchronous from even the supervisor of the
OS. The four instructions amounts to, "Start I/O", "Start I/O on a
subchannel", "Test if I/O is possible", and "Cancel pending I/O".
Modern mainframes can actually support up to 320 to 640 concurrently
operating channel programs.
- It's *strictly* master-slave in relationship. While individual
channel programs might remind you of QDs and TDs in an Intel UHCI
driver stack, there is no equivalent concept to a frame counter or
other means of supporting isochronous data transfer.
- When a channel program terminates (normally or abnormally), an IRQ
is generated which can be trapped by the user's program somehow
(typically in the form of an asynchronous callback).
Relationship to virtio:
Virtio is a message-oriented device abstraction mechanism, where
*logically*, you arrange your command like so:
[command identifying what to do][any output buffers go here][any input
buffers go here]
This entire "string" of bytes comprising the above can come from a
single buffer, or it can come from a plurality of buffers queued in
the appropriate order. Once a virtio request is configured, the
operation is "kicked" into action. When the operation completes, the
client is notified through an asynchronous callback.
The act of configuring a virtio channel is equivalent to synthesizing
CCW programs. The act of "kicking" a virtio channel into action is
equivalent to dispatching that program with an SIO instruction. The
*precise* semantics don't line up perfectly of course, but the I/O
models are close enough that s390 support was one of the first
supported targets for virtio. It should be pointed out that no MMIO
buffers exist in channel I/O; the concept doesn't apply.