From: jeh@cmkrnl.com
Newsgroups: comp.os.vms
Subject: Re: $QIO: What happens within?
Date: 15 Dec 92 06:05:05 GMT
Distribution: na
Organization: Kernel Mode Consulting, San Diego, CA

In article <1992Dec14.164552.28939@bnlux1.bnl.gov>, kushmer@bnlux1.bnl.gov 
(christopher kushmerick) writes:
> What is the sequence of events when a qio is executed? In particular:
> 
> I am using a $QIO to send data between mail boxes. I have a global structure
> which is updated at AST level by various tasks. When I get a request from 
> another program, I send the structure by qio, specifying the address of the
> actual global structure, rather than first making a copy.
> 
> This is in FORTRAN so both the global structure and any local copies would be
> at some fixed location in memory and _NOT_ on the stack.
> 
> Here is the question: Does $qio (not $qiow) make its own copy of the data 
> prior to returning, or does it just use the data at the memory location speced?

It depends on the device driver.  The difference lies in the difference between
"buffered" and "direct" I/O.  

In buffered writes, the driver makes a copy of the user's data, and the actual
transfer is from the copy, which is in system address space.  For buffered
reads, the driver allocates a system-space buffer and the transfer is done into
the buffer, and the system buffer is copied to the user's buffer by the IPL 2
I/O post-processing AST routine. 

In direct I/O, the actual transfer is done to or from the user's buffer in 
process address space.  The pages containing the buffer are locked into 
the process working set and into physical memory for the duration of the I/O
request, so that pager and swapper activity can't reassign them to some other
process.  

In general, buffered I/O is associated with programmed I/O devices, and direct
I/O with DMA devices, but there are many exceptions to this.  Another general
rule is that buffered I/O is used when the driver needs to "touch" the data
in some way -- for example, expanding tabs to spaces when writing to a terminal
or line printer, or adding protocol headers "around" user data for transmission
via DECnet logical link -- but there are exceptions to this too.  

Ultimately, the selection of buffered vs. direct I/O is made by the person who 
writes the driver for the device.  It can vary with different types of I/O 
function codes and can even vary from one use to another of the same function
code to the same device (although this requires some non-standard coding in
the driver, and is quite rare).  Nor can you request buffered or direct I/O
via a parameter on the $QIO (or any other) system service call.  (I suppose it
would be possible to allow this, but it would take some bizarre coding in the
driver; I've never seen it done.)

For the specific question re the mailbox driver, writes are (in all versions of
VMS that I've looked at) buffered I/O.  This means that $QIO (both $QIO and
$QIOW, by the way) grabs a copy of your buffer before the procedure call
returns to you, whether or not the write has actually completed yet. 

About now you are probably wondering which type of I/O the various devices use.
Well, in general:

	device type	I/O type
	---------------	--------
	disk		direct
	tape		direct
	mailbox		buffered
	terminal	buffered, except unformatted writes to devices like the
			 DMF32, DH series, CX series, etc., with DMA output
			 enabled
	DECnet logical	buffered, except when the "other node" is the 
	 link (task-to-  local node, in which case it's direct I/O
	 task comm.)

For more information, see the _VMS Device Support Manual_.  For even more
information, see _VMS Advanced Device Driver Techniques_, by myself and Lee
Leahy.  

Note well:  The VMS documentation does not state whether a particular I/O
function to a particular device is buffered or direct.  This means that (like
any other undocumented details of VMS's behavior) it may change in a future
release, and so you shouldn't write code that counts on it being one way or the
other. 

(In fact, when the DMF32 was introduced, the driver's use of direct I/O writes
when possible broke some application code.) 

> 1) Stack frames: If I were doing this in Pascal or C and sent a local
> variable via qio. By the time the qio got around to actually sending the data,
> the routine that called the qio may have returned, the stack frame would no
> longed be valid and who knows what might be in the (stack) memory location that
> used to contain the data.

This is indeed a valid concern for operations that happen to be direct I/O.

It is also a concern for buffered reads:  The fact that the actual transfer is
done to a system buffer, which is then copied to your buffer, won't help you
if you redefine the virtual address space that includes your buffer between
the time you issue the $QIO and the time the system tries to copy the data back
to your buffer.

The same concern applies to the I/O status block.  It's zeroed when the request
is successfully queued to the device, and filled in later when the request
completes.  

> 2) The data in my case is updated by ast-level interrupt routines. What if
> the updating routine is executing when the $qio is ready to start shipping the 
> data. Will the qio interrupt the ast routine to ship the data? 

In the case of a buffered write, there is nothing to worry about.  The $QIO
write grabs a copy of the data while running in the context of your process,
but at IPL 2; this blocks the delivery of any ASTs to your process.  This is
really done to prevent things like process deletion from happening while the
record of the I/O operation isn't complete, but it also handily prevents user
ASTs from interrupting $QIO's buffer copy. 

> Does it
> matter if the qio was called at AST level or at "normal" level? 

No.

> Is it possible
> that the data will be corrupt due to incomplete updating of the data?

In the case of direct I/O, yes,  it would be possible, since the actual 
transfer may happen after $QIO returns control to you, especially if you are
queueing multiple write requests to the same device and one or more earlier
requests aren't done yet.  It isn't a problem when writing to a mailbox. 

> Perhaps a mailbox is a bad example because it is all memory. what if I was 
> sending large amounts of data down a slow serial line, using local 
> variables in a stack-frame language like pascal.

If by a serial line you mean either a terminal port or a DECnet logical link,
all such I/O is buffered in the current implementation, so you have nothing to
worry about.  Unless, that is, you are doing unformatted writes to a terminal
device that supports and is enabled for DMA. 

For direct I/O, and for buffered reads, with async (no-wait) $QIO, and for the
IOSB with *all* types of no-wait $QIO, you must indeed take some pains to
ensure that your buffer will stay around until the write is done.  This usually
means allocating it explicitly or using "own" or "static" storage.  In C, 
declaring it outside of any function declaration will also work. 

	--- Jamie Hanrahan, Kernel Mode Consulting, San Diego CA
drivers, internals, networks, applications, and training for VMS and Windows-NT
uucp 'g' protocol guru and release coordinator, VMSnet (DECUS uucp) W.G., and 
Chair, Programming and Internals Working Group, U.S. DECUS VMS Systems SIG 
Internet:  jeh@cmkrnl.com, hanrahan@eisner.decus.org, or jeh@crash.cts.com
Uucp:  ...{crash,eisner,uunet}!cmkrnl!jeh