remap_pfn_range vs per-page fault handler

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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 ecartis@xxxxxxxxxxxx
Please read the FAQ at http://kernelnewbies.org/FAQ


[Index of Archives]     [Newbies FAQ]     [Linux Kernel Mentors]     [Linux Kernel Development]     [IETF Annouce]     [Git]     [Networking]     [Security]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux SCSI]     [Linux ACPI]
  Powered by Linux