Subject: Suggested changes to bus_dma interface
To: None <thorpej@nas.nasa.gov>
From: Charles M. Hannum <mycroft@mit.edu>
List: tech-kern
Date: 09/25/1996 02:37:51
I believe that bus_dma_map() should be turned into an iterative
function.  It would have a definition like the following.  Although
there are details omitted here, I believe the intent should be clear.

bus_dma_segment_t
bus_dma_map(struct uio *uio, int maxsize, int start, int end, int alignment)

uio -- contains an iovec and a flag indicated whether the buffer is in
user space or kernel space.  The mapping function will use this to
determine which map to resolve physical addresses from in the absence
of DVMA.  The mapping function will also use this to keep track of
where it is in the buffer from one iteration to the next.

maxsize -- the maximum size of the segment.

start, end -- boundaries of the address space in which the segment may
lie.

alignment -- any alignment constraint necessary.

The bus_dma_map() function returns one DMA segment describing a piece
of the I/O, and adjusts the uio pointers past this segment.  The
mapping function may thus be called several times to get several DMA
segments.

If the driver using the mapping function has a restriction on the size
of a segment, where it may lie in the address space, or how it must be
aligned, it can pass this restriction on to the mapping function.  If
it has a restriction on the total number of segments, it can simply
only call the function a certain number of times.  If it has a
restriction on the total size of a transfer, it may alter the
`maxsize' parameter between iterations to enforce this.

Mapping functions may be chained.  If an intermediate device
(typically a bridge) imposes its own size, boundary, or alignment
restrictions, it will pass the more strict of the two restrictions to
the next mapping function; otherwise it will pass the ones it
received.  An intermediate mapping function must map and/or scale
addresses from one bus to another in both directions, as necessary,
pretty much the same as the bus_space_map() functions.  (See the
bus_space document for additional information.)

The intent of the size, boundary, and alignment arguments is to be the
same as those used by the bus_space and extent functions.  In this
way, busses that must allocate DMA space and map things into it can
simply use an extent, and pass the restrictions directly to the extent
functions.


I believe this solves all of the following problems:

* It's actually quite simple to implement, probably less code than any
of the proposals out there already.

* It deals cleanly with mapping restrictions that the device may
impose, which has not previously been addressed.

* It solves Justin's memory usage complaints (which, BTW, become more
signifcant if you have a bridge).

* It solves Justin and Dennis's overhead complaints, although in a
very different way than had been suggested.  At the same time, it does
not require an implementation that uses jumps through pointers, and
thus doesn't necessarily lose on branch prediction or branch target
buffering.

* It allows reusing scatter/gather transformation functions, if you so
chose, because the whole mapping and iteration process is at an
intermediate point between the driver and the bus mapping code.  You
can implement it either in the driver or separately.  (Which also
resolves my complaints with Justin's suggestion, because you only need
one such transformation function for all {arch,bus} combinations.)

* It gives a direct interface for passing in scatter/gather
information, which could be taken advantage of for clustering, or a
variety of other things.

* It trivially allows mapping directly from user space.

* It makes the interface much more like the bus_space interface.