locking down memory and map_user_kiobuf

Lee, Shuyu (SLee@cognex.com)
Mon, 16 Jun 2003 15:51:35 -0400


Hi, all.

I am developing a Linux driver which allows the hardware to DMA image data
directly into an image buffer allocated by a user. Everything appears to be
working in my driver test, until the full application is run. After some
investigation, it seems that my code which should lock down the image buffer
does not really work. When the memory usage is up, the image buffer is paged
out, causing segmentation fault. If I force all pages to be resident by
writing one byte to every page of the image buffer right before I call
map_user_kiobuf(), it seems to work. By the way, I am using RedHat 8.0
(2.4.18-14) and gcc 3.2 that comes with RedHat 8.0

Here is the code that I use to lock down the image buffer allocated by user
and passed to the driver by an ioctl call:
retVal = alloc_kiovec(1, &pDMAField->iobuf);
// Test code: write to one byte of every page of the image buffer here
retVal = map_user_kiobuf(READ, pDMAField->iobuf, (ULONG)pImgVirt,
pDMAField->height * pDMAField->pitch);

My questions are:
1) Does map_user_kiobuf() only work when the memory to be locked down
is resident?
2) Do I need to call lock_kiovec() as well?
3) I have tried with both "READ" and "WRITE" when calling
map_user_kiobuf(), does it make any difference?
4) I took a look at map_user_kiobuf() implementation in memory.c
(attached below). It seems that all it does is to force all pages of the
buffer to be paged in, and then to flush the cache. Am I missing anything?

Any help and suggestion would be greatly appreciated.
Shuyu

// From RedHat 8.0: /usr/src/linux-2.4.18-14/mm/memory.c
int map_user_kiobuf(int rw, struct kiobuf *iobuf, unsigned long va, size_t
len)
{
int pgcount, err;
struct mm_struct * mm;

/* Make sure the iobuf is not already mapped somewhere. */
if (iobuf->nr_pages)
return -EINVAL;

mm = current->mm;
dprintk ("map_user_kiobuf: begin\n");

pgcount = (va + len + PAGE_SIZE - 1)/PAGE_SIZE - va/PAGE_SIZE;
/* mapping 0 bytes is not permitted */
if (!pgcount) BUG();
err = expand_kiobuf(iobuf, pgcount);
if (err)
return err;

iobuf->locked = 0;
iobuf->offset = va & (PAGE_SIZE-1);
iobuf->length = len;

/* Try to fault in all of the necessary pages */
down_read(&mm->mmap_sem);
/* rw==READ means read from disk, write into memory area */
err = get_user_pages(current, mm, va, pgcount,
(rw==READ), 0, iobuf->maplist, NULL);
up_read(&mm->mmap_sem);
if (err < 0) {
unmap_kiobuf(iobuf);
dprintk ("map_user_kiobuf: end %d\n", err);
return err;
}
iobuf->nr_pages = err;
while (pgcount--) {
/* FIXME: flush superflous for rw==READ,
* probably wrong function for rw==WRITE
*/
flush_dcache_page(iobuf->maplist[pgcount]);
}
dprintk ("map_user_kiobuf: end OK\n");
return 0;
}

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/