Re: DMA from/to user-space memory

Linus Torvalds (torvalds@transmeta.com)
Fri, 15 May 1998 10:16:22 -0700 (PDT)


On Fri, 15 May 1998, David S. Miller wrote:
>
> The *big* problem I have with that is that the _application_ has to be
> aware that DMA will be used to fill the buffer. Thus, you will never
> be able to write a driver that implements, for instance, the plain
> simple read() and write() system calls using user-space DMA, even
> if your hardware is smart enough to do 32-bit addressing and unattended
> scatter/gather DMA.
>
> ...
>
> Yes, but the _need_ of the application to be aware of the DMA being used
> by the driver is a big disadvantage IMHO.

Note that I was by no means intending to keep the DMA interface away from
drivers. Yes, the application would have to be aware to some degree when
it wants to use direct DMA transfers, but a lot of that can be hidden in
the driver (so the dma_map() wouldn't necessarily be used _directly_ by
the user, because the driver could use it internally and squirrel away the
virtual->physical translations to make it more transparent to the user).

Note that doing the mlock at "read()" or "write()" time is just simply
unacceptable. If you want to do it completely transparently for the user,
then you follow the page table, you don't do anything like this at all.

I was assuming that you were concerned about something like a frame
grabber, where the operation would be something like

fd = open("/dev/framegrabber");
area = mmap(NULL, 1024*1024, PROT_READ, MAP_SHARED, fd, 0);
...
/* Trigger a grab */
ioctl(fd, IOCTL_FRAMEGRAB, area);
.. use the grabbed data here ..

where the need to map the data is not going to be invisible to the user
anyway, because the user does need to at the very least use a mmap() (and
this is where I'd prefer to have a dma_map() kind of thing).

Again, if you actually want to do something like

fd = socket(..)
write(fd, area, size);

and make that use DMA, then the way you do that is to do the page table
following (paging them in if required), and temporarily bump the usage
counter for that _page_ (you don't actually lock the page table, you only
lock the use the actual page - it may be unmapped or something, but you're
still going to access it).

Btw, this works, I did it several years ago (not for DMA, but for an
experimental zero-copy pipe). The code is not very complex, although there
are corner issues wrt shared memory etc that you have to look out for.
mlock() is not needed at any point. The good news is that all normal uses
are uses without any of these corner cases, so you can choose to make the
corner cases go slowly..

Finally, if somebody missed it, the real reason for map_dma() is that it
allows things that need no intervention from the kernel at all. Imagine
doing bus-master DMA from or to a graphics card, where you have user-level
access to the registers of the card (think XFree86) anyway, and what you
do is you actually do the dma_map() and really _never_ touch kernel space
after that.

And _that_ btw, is a requirement for me. I do _not_ want to have a
situation where I have to support multiple different interfaces, whre one
interface is for user mode without a driver, one is for mmap user mode
with a kernel driver, and one is for a driver that is transparent. I want
something that works and has no security implications.

(For example, mlock() has some huge security implications in that it locks
a potentially huge number of pages into memory at one time, while a
on-the-fly page table walker only keeps those pages around that it is
going to need next).

Linus

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu