[prev in list] [next in list] [prev in thread] [next in thread] 

List:       linux-kernel
Subject:    Re:  pseudo device driver with mmap
From:       rubini () pop ! systemy ! it
Date:       1997-04-30 22:23:41
[Download RAW message or body]


> Now the problem is, what code do I need in buf_mmap to make a piece of
> allocated kernel memory available to userland?

The problem is you need to play with the vm-operations in order to
have it work right. It's not trivial, but I've done once just
to try it out.

You need to implement an open method, a close method and a no-page method
to have the things work right. Sure you also need to play with
the module usage count, because an mmapped region may survice device
close, and you don't want to remove a mapped module, do you?

The following code refers to 2.0 kernels, and works with intel, sparc,
alpha.

To keep track of the usage count you need something like this:

    static struct vm_operations_struct simple_vm_ops = {
        simple_vma_open,
        simple_vma_close, /* fill with NULLs */
        };

    void simple_vma_open(struct vm_area_struct * area)
    { MOD_INC_USE_COUNT; }

    void simple_vma_close(struct vm_area_struct * area)
    { MOD_DEC_USE_COUNT; }

    int simple_mmap(struct inode *inode, struct file *filp,
                 struct vm_area_struct *vma)
    {
    /* int remap_page_range(virt_add, phys_add, size, protection); */
        if (remap_page_range(vma->vm_start, vma->vm_offset,
                             vma->vm_end-vma->vm_start, vma->vm_page_prot))
            return -EAGAIN;
        if (vma->vm_ops)
            return -EINVAL; /* Hmm... shouldn't happen */
        vma->vm_ops = &simple_vm_ops;
        MOD_INC_USE_COUNT; /* open(vma) wasn't called this time */
        vma->vm_inode = inode;
        inode->i_count++;
        return 0;
    }


A nopage implementation is needed if you want mremap to work, and
makes your own job (mapping a kmalloc or vmalloc buffer) easier.

This is the simple case of nopage for I/O memory (like the previous one,
it uses remap_page_range() to obtain mapped buffers:


    static struct vm_operations_struct simple_vm_ops = {
        NULL, NULL, NULL, NULL, NULL, NULL, simple_nopage,
        };

    void simple_nopage(struct vm_area_struct *vma, unsigned long address,
                       int write)
    {
    /* int remap_page_range(virt_add, phys_add, size, protection); */
    remap_page_range(address & PAGE_MASK,
                     address - vma->vm_start + vma->vm_offset,
                     PAGE_SIZE, vma->vm_page_prot);
    }

    int simple_mmap(struct inode *inode, struct file *filp,
                 struct vm_area_struct *vma)
    {
        vma->vm_ops = &simple_vm_ops;
        vma->vm_inode = inode;
        inode->i_count++;
        return 0;
    }


If your buffer is made up of single pages -- get_free_pages(0), the
following will work:

unsigned long scullp_vma_nopage(struct vm_area_struct *vma,
                                unsigned long address, int write)
{
    unsigned long offset = address - vma->vm_start + vma->vm_offset;
    void *pageptr = NULL; /* default to "missing" */

    /*
     * Now retrieve the  page.
     * Don't want to allocate: I'm too lazy. If the device has holes,
     * the process receives a SIGBUS when accessing the hole.
     */

    /* retrieve pageptr, the address allocated via get_free_page() */

    if (!pageptr) return 0; /* hole or end-of-file: SIGBUS */

    /* got it, now increment the count */
    atomic_inc(&mem_map[MAP_NR(pageptr)].count);
    return (unsigned long)pageptr;
}


A vmalloc buffer needs some tweaking, as the pageptr you got
is not a physical one, so the following is needded:

    page = VMALLOC_VMADDR(pageptr);
    PDEBUG("page %lx\n",page); /* skip-this-line */

    pgd = pgd_offset(init_mm_ptr, page);
    pmd = pmd_offset(pgd, page);
    pte = pte_offset(pmd, page);
    page = pte_page(*pte);        /* this is the physical address */

    /* now increment the count and return */
    atomic_inc(&mem_map[MAP_NR(page)].count);
    return page;
}

--
    __ o         Tu vuo` fa` l'americano: sient'a me` chi t'o fa fa`.
   _`\<,                                               (Renato Carosone)
__( )/( )__  alessandro.rubini@linux.it  +39-382-529554

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic