Hi,
I am writing a toy driver to understand a few memory mapping principles.
This character driver is supposed to be able to mmap any part of physical memory
to userspace, in a similar fashion to /dev/mem. The driver provides a .mmap
interface to
physical memory using two different methods.
- in the first method, the mmap implementation calls remap_pfn_range to map all
physical pages together. I believe this is similar to what /dev/mem does
- in the second method, I use a .fault handler in vm_area_operations to map
each individual
requested page when its page fault happens.
The mmap method for the device is simple:
static int memphys_mmap(struct file *filp, struct vm_area_struct *vma)
{
unsigned long physaddr = vma->vm_pgoff << PAGE_SHIFT;
struct page *pageptr;
unsigned long size = vma->vm_end - vma->vm_start;
unsigned long virtualaddr;
printk("%s mmaping physical memory at: %llx (page number %d) \n",
__FUNCTION__, physaddr, vma->vm_pgoff);
if (physaddr + size > __pa(high_memory)
{
printk("%s invalid memory location
for device \n", __FUNCTION__);
return -EINVAL;
}
/* this call is uncommented in the first method
remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, vma->vm_end -
vma->vm_start, vma->vm_page_prot);
*/
vma->vm_flags |= VM_RESERVED;
vma->vm_ops = &memphys_vmops;
return 0;
}
The .fault handler code is:
... /* installing function in the vm_operations_struct */
.fault = memphys_vma_fault;
...
static int memphys_vma_fault( struct vm_area_struct *vma,
struct vm_fault *vmf)
{
struct page *pageptr;
unsigned long offset = vmf->vm_pgoff << PAGE_SHIFT;
unsigned long physaddr = vmf->virtual_address - vma->vm_start + offset;
unsigned long pfn = physaddr >> PAGE_SHIFT;
unsigned long virtualaddr;
printk("%s vmastart:%llx vmaend:%llx offset:%llx physical_address:%llx
pfn: %d\n",
__FUNCTION__, vma->vm_start,
vma->vm_end,
offset, physaddr, pfn);
if (!pfn_valid(pfn))
return NULL;
pageptr = pfn_to_page (pfn);
get_page(pageptr);
vmf->page = pageptr;
return pageptr;
}
The remap_pfn_range method works as expected. The .fault handler method works
fine when mmaping a small number of pages (up until 5 pages). However, for >5
of pages,
the user process gets stuck. Using printk in my .fault handler, I see that the
handler keeps trying to map the the 6th page but never succeeds.
[14349.275138] memphys_open
[14349.275147] memphys_mmap requested physical memory at: 41400000 (page number
267264)
[14349.275154] memphys_vma_fault offset:41400000 physical_address:41400000 pfn:
267264
[14349.275186] memphys_vma_fault offset:41400000 physical_address:41401000 pfn:
267265
[14349.275217] memphys_vma_fault offset:41400000 physical_address:41402000 pfn:
267266
[14349.275248] memphys_vma_fault offset:41400000 physical_address:41403000 pfn:
267267
[14349.275261] memphys_vma_fault offset:41400000 physical_address:41404000 pfn:
267268
[14349.275272] memphys_vma_fault offset:41400000 physical_address:41405000 pfn:
267269
[14349.275295] memphys_vma_fault offset:41400000 physical_address:41405000 pfn:
267269
[14349.275301] memphys_vma_nofault vmastart:7f6499046000 vmaend:7f649904c000
...
[nonstop continuing retries to map pfn: 267269, the 6th page)
...
[14349.275330] memphys_vma_nofault offset:41400000 physical_address:41405000
pfn: 267269
[14349.275330] memphys_vma_nofault offset:41400000 physical_address:41405000
pfn: 267269
[...forever...]
At other times, the system hangs as soon as I enter the command (I haven't
tried kgdb here yet)
The test user program calls mmap with the following flags:
fd = open("/dev/memphys", O_RDONLY);
buf = mmap(0, size, PROT_READ | PROT_WRITE , MAP_PRIVATE , fd, offset);
[use buf in some way...]
close(fd);
- Can anyone help me understand what the driver code is doing wrong? Are either
the mmap or the .fault handler implementation missing something crucial?
I have tried mapping several different physical memory locations, but I always
get the fault handler to fail on the 6th page or the system to hang. The
remap_pfn_range method never crashes or hangs. This is on a dual-core x86_64
machine with 3GB of physical RAM. I am wondering whether there is a standard
I/O memory hole that my handler doesn't take into account, resulting in the
hang.
Also note that currently I set the VM_RESERVED flag, though I am not sure this
is needed for the functionality of this driver.
- bonus question: ldd 3rd edition mentions that remap_pfn_range won't
allow you to remap conventional addresses (chapter 15, p.430, section remapping
RAM). Is this section obsolete? It seems that /dev/mem (drivers/char/mem.c)
uses
remap_pfn_range, and therefore the function allows mapping conventional memory
now. I just wanted to confirm that this part of the book is obsolete.
thanks for any help,
- Vasilis
--
To unsubscribe from this list: send an email with
"unsubscribe kernelnewbies" to [email protected]
Please read the FAQ at http://kernelnewbies.org/FAQ