whether to use nopage() implementation or remap_page/pfn_range() api

From: yogeshwar sonawane
Date: Wed Mar 29 2006 - 07:16:37 EST


Hi all,

I am allocating a buffer with __get_free_pages, then trying to map it.
As remap_pfn/page_range() cannot be used, I tried nopage()
implementation (check nopage_module.c). But while looking through some
drivers (oss), i found that remap_pfn/page_range() can be used for
such physical memory after setting PG_reserved bit of all pages of the
buffer. So i tried setting PG_reserved bit & then using
remap_page_range() (check remap_module.c). its also working fine.

1) Now what will be the correct way to do such type of mapping?

then i wrote the above things for memory obtained by pci_alloc_consistent().
pci_remap_module.c contains implementation using remap_page_range().
pci_nopage_module.c contains implementation using nopage().

but while running nopage implementation, i am getting some kernel messeges like:

<0>Bad page state at __free_pages_ok (in process 'a.out', page c1632720)
flags:0x20001070 mapping:c19e23a4 mapcount:0 count:2
Backtrace:
[<c01426d9>] bad_page+0x58/0x89
[<c01429e3>] __free_pages_ok+0x77/0xcc
[<f89130d3>] my_close+0x6d/0x74 [pci_nopage_module]
[<c015a816>] __fput+0x55/0x100
[<c014df24>] remove_vm_struct+0x62/0x79
[<c014fc9c>] exit_mmap+0x13e/0x148
[<c012016a>] mmput+0x4e/0x72
[<c01240c7>] do_exit+0x1f1/0x3de
[<c012439f>] sys_exit_group+0x0/0xd
[<c02d0fb7>] syscall_call+0x7/0xb
Trying to fix it up, but a reboot is needed


Can somebody help me regarding this?

If i am missing something, let me know.

i have tried this on Fedora core 3 as well as RHEL4-U2 & getting
messeges on both.

thanks in advance.
Yogeshwar
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <asm/io.h>

struct file_operations fops ;
static int major ;

static int my_open(struct inode *inode, struct file *file)
{
int i ;
void *data_ptr ;
struct page *page ;
printk("I am in my_open\n") ;
data_ptr = (void *) __get_free_pages(GFP_KERNEL, 4) ;
if (data_ptr == NULL)
{
printk("__get_free_pages failed\n") ;
return -1 ;
}
else
{
printk("__get_free_pages successful data_ptr = %ld\n", (unsigned long)data_ptr) ;
}

for (i = 0 ; i < 16 ; i++)
{
page = virt_to_page(data_ptr + PAGE_SIZE * i) ;
set_bit(PG_reserved, &page->flags) ;
}

strcpy((char *)data_ptr, "HelloWorld") ;

file->private_data = data_ptr ;
return 0 ;
}

static int my_close(struct inode *inode, struct file *file)
{
int i ;
struct page *page ;
unsigned long data_ptr = (unsigned long)file->private_data ;
printk("I am in my_close\n") ;
printk("%s data_ptr = %ld\n", (char *)data_ptr, data_ptr) ;

for (i = 0 ; i < 16 ; i++)
{
page = virt_to_page(data_ptr + PAGE_SIZE * i) ;
clear_bit(PG_reserved, &page->flags) ;
}
free_pages(data_ptr, 4) ;
return 0 ;
}

static int my_mmap(struct file *file, struct vm_area_struct *vma)
{
void *data_ptr = (void *)file->private_data ;
printk("I am in my_mmap\n") ;
printk("data_ptr = %ld\n", (unsigned long) data_ptr) ;

if (remap_page_range(vma, vma->vm_start, virt_to_phys(data_ptr), (vma->vm_end - vma->vm_start), vma->vm_page_prot))
{
printk("remap_page_range failed\n") ;
return -1 ;
}
return 0 ;
}

int init_module(void)
{
printk("I am in init_module\n") ;

major = register_chrdev(0, "mydriver", &fops) ;
if(major == -1)
{
printk("register_chrdev failed\n") ;
return -1 ;
}
printk("module is registered with major no = %d\n", major) ;
return 0 ;
}

void cleanup_module(void)
{
printk("I am in cleanup_module\n") ;
unregister_chrdev(major, "mydriver") ;
}

struct file_operations fops =
{
.open = my_open,
.release = my_close,
.mmap = my_mmap,
} ;










#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <asm/io.h>

struct file_operations fops ;
static int major ;

static int my_open(struct inode *inode, struct file *file)
{
int i ;
void *data_ptr ;
struct page *page ;
printk("I am in my_open\n") ;
data_ptr = (void *) __get_free_pages(GFP_KERNEL, 4) ;
if (data_ptr == NULL)
{
printk("__get_free_pages failed\n") ;
return -1 ;
}
else
{
printk("__get_free_pages successful data_ptr = %ld\n", (unsigned long)data_ptr) ;
}

for (i = 0 ; i < 16 ; i++)
{
page = virt_to_page(data_ptr + PAGE_SIZE * i) ;
set_bit(PG_reserved, &page->flags) ;
}

strcpy((char *)data_ptr, "HelloWorld") ;

file->private_data = data_ptr ;
return 0 ;
}

static int my_close(struct inode *inode, struct file *file)
{
int i ;
struct page *page ;
unsigned long data_ptr = (unsigned long)file->private_data ;
printk("I am in my_close\n") ;
printk("%s data_ptr = %ld\n", (char *)data_ptr, data_ptr) ;

for (i = 0 ; i < 16 ; i++)
{
page = virt_to_page(data_ptr + PAGE_SIZE * i) ;
clear_bit(PG_reserved, &page->flags) ;
}
free_pages(data_ptr, 4) ;
return 0 ;
}

void my_vma_open(struct vm_area_struct *vma)
{
printk("I am in my_vma_open\n") ;
}

void my_vma_close(struct vm_area_struct *vma)
{
printk("I am in my_vma_close\n") ;
}

struct page *my_vma_nopage(struct vm_area_struct *vma, unsigned long address, int *type)
{
struct page *page ;
unsigned long offset = address - vma->vm_start ;
void *base_kern_addr = vma->vm_private_data ;
printk("I am in my_vma_nopage\n") ;

page = virt_to_page(base_kern_addr + offset) ;

get_page(page) ;

if (type)
{
*type = VM_FAULT_MINOR ;
}

return page ;
}

static struct vm_operations_struct my_vm_ops =
{
.open = my_vma_open,
.close = my_vma_close,
.nopage = my_vma_nopage,
} ;

static int my_mmap(struct file *file, struct vm_area_struct *vma)
{
void *data_ptr = (void *)file->private_data ;
printk("I am in my_mmap\n") ;
printk("data_ptr = %ld\n", (unsigned long) data_ptr) ;

vma->vm_flags |= VM_RESERVED ;
vma->vm_ops = &my_vm_ops ;
vma->vm_private_data = data_ptr ;
my_vma_open(vma) ;

return 0 ;
}

int init_module(void)
{
printk("I am in init_module\n") ;

major = register_chrdev(0, "mydriver", &fops) ;
if(major == -1)
{
printk("register_chrdev failed\n") ;
return -1 ;
}
printk("module is registered with major no = %d\n", major) ;
return 0 ;
}

void cleanup_module(void)
{
printk("I am in cleanup_module\n") ;
unregister_chrdev(major, "mydriver") ;
}

struct file_operations fops =
{
.open = my_open,
.release = my_close,
.mmap = my_mmap,
} ;










#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <asm/io.h>
#include <linux/pci.h>

#define VENDOR_ID 0x14E4
#define DEVICE_ID 0x1600
#define SIZE 10000

struct file_operations fops ;
static int major ;
struct pci_dev *pci_dev = NULL ;
dma_addr_t bus_addr ;

static int my_open(struct inode *inode, struct file *file)
{
void *virt_addr = (void *)NULL ;
struct page *page, *end_page ;
printk("I am in my_open\n") ;

virt_addr = pci_alloc_consistent(pci_dev, SIZE, &bus_addr) ;

if (virt_addr == NULL)
{
printk("pci_alloc_consistent failed\n") ;
return -ENOMEM ;
}
else
{
printk("pci_alloc_consistent successful, virt_addr = %ld\n", (unsigned long)virt_addr) ;
}

end_page = virt_to_page(virt_addr + SIZE - 1) ;

for (page = virt_to_page(virt_addr) ; page <= end_page ; page++)
{
set_bit(PG_reserved, &page->flags) ;
}

file->private_data = virt_addr ;
return 0 ;
}

static int my_close(struct inode *inode, struct file *file)
{
int *ptr, i ;
struct page *page, *end_page ;
void *virt_addr = file->private_data ;
printk("I am in my_close\n") ;
printk("virt_addr = %ld\n", (unsigned long)virt_addr) ;

ptr = (int *)virt_addr ;

printk("my_close : data read from kernel : \n") ;

for(i = 0 ; i < 2048 ; i++)
{
printk("%d ", *(ptr + i)) ;
}

end_page = virt_to_page(virt_addr + SIZE - 1) ;

for (page = virt_to_page(virt_addr) ;
page <= end_page ; page++)
{
clear_bit(PG_reserved, &page->flags) ;
}

pci_free_consistent(pci_dev,SIZE,virt_addr,bus_addr) ;

return 0 ;
}

static int my_mmap(struct file *file, struct vm_area_struct *vma)
{
void *data_ptr = (void *)file->private_data ;
printk("I am in my_mmap\n") ;
printk("data_ptr = %ld\n", (unsigned long) data_ptr) ;

if (remap_page_range(vma, vma->vm_start, virt_to_phys(data_ptr), (vma->vm_end - vma->vm_start), vma->vm_page_prot))
{
printk("remap_page_range failed\n") ;
return -1 ;
}
return 0 ;
}

int init_module(void)
{
printk("I am in init_module\n") ;

pci_dev = pci_get_device(VENDOR_ID, DEVICE_ID, pci_dev) ;

if (pci_dev == (struct pci_dev *)NULL)
{
printk("pci_get_device failed\n") ;
return -1 ;
}

printk("pci_dev found, pci_dev = %ld\n", (unsigned long)pci_dev) ;

major = register_chrdev(0, "mydriver", &fops) ;
if(major == -1)
{
printk("register_chrdev failed\n") ;
return -1 ;
}
printk("module is registered with major no = %d\n", major) ;
return 0 ;
}

void cleanup_module(void)
{
printk("I am in cleanup_module\n") ;
unregister_chrdev(major, "mydriver") ;
}

struct file_operations fops =
{
.open = my_open,
.release = my_close,
.mmap = my_mmap,
} ;










#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <asm/io.h>
#include <linux/pci.h>

#define VENDOR_ID 0x14E4
#define DEVICE_ID 0x1600
#define SIZE 10000

struct file_operations fops ;
static int major ;
struct pci_dev *pci_dev = NULL ;
dma_addr_t bus_addr ;

static int my_open(struct inode *inode, struct file *file)
{
void *virt_addr = (void *)NULL ;
printk("I am in my_open\n") ;

virt_addr = pci_alloc_consistent(pci_dev, SIZE, &bus_addr) ;

if (virt_addr == NULL)
{
printk("pci_alloc_consistent failed\n") ;
return -ENOMEM ;
}
else
{
printk("pci_alloc_consistent successful, virt_addr = %ld\n", (unsigned long)virt_addr) ;
}

file->private_data = virt_addr ;
return 0 ;
}

static int my_close(struct inode *inode, struct file *file)
{
int *ptr, i ;
void *virt_addr = file->private_data ;
printk("I am in my_close\n") ;
printk("%s virt_addr = %ld\n", (char *)virt_addr, (unsigned long)virt_addr) ;

ptr = (int *)virt_addr ;

printk("my_close : data read from kernel : \n") ;

for(i = 0 ; i < 2048 ; i++)
{
printk("%d ", *(ptr + i)) ;
}

pci_free_consistent(pci_dev,SIZE,virt_addr,bus_addr) ;

return 0 ;
}

void my_vma_open(struct vm_area_struct *vma)
{
printk("I am in my_vma_open\n") ;
}

void my_vma_close(struct vm_area_struct *vma)
{
printk("I am in my_vma_close\n") ;
}

struct page *my_vma_nopage(struct vm_area_struct *vma, unsigned long address, int *type)
{
struct page *page ;
unsigned long offset = address - vma->vm_start ;
void *base_kern_addr = vma->vm_private_data ;
printk("I am in my_vma_nopage\n") ;

printk("nopage : address = %ld, vma->vm_start = %ld, offset = %ld, base_kern_addr = %ld\n",
address, (unsigned long)vma->vm_start, offset, (unsigned long)base_kern_addr) ;

page = virt_to_page(base_kern_addr + offset) ;

get_page(page) ;

if (type)
{
*type = VM_FAULT_MINOR ;
}

return page ;
}

static struct vm_operations_struct my_vm_ops =
{
.open = my_vma_open,
.close = my_vma_close,
.nopage = my_vma_nopage,
} ;

static int my_mmap(struct file *file, struct vm_area_struct *vma)
{
void *data_ptr = (void *)file->private_data ;
printk("I am in my_mmap\n") ;
printk("data_ptr = %ld\n", (unsigned long) data_ptr) ;

vma->vm_flags |= VM_RESERVED ;
vma->vm_ops = &my_vm_ops ;
vma->vm_private_data = data_ptr ;
my_vma_open(vma) ;

return 0 ;
}

int init_module(void)
{
printk("I am in init_module\n") ;

pci_dev = pci_get_device(VENDOR_ID, DEVICE_ID, pci_dev) ;

if (pci_dev == (struct pci_dev *)NULL)
{
printk("pci_get_device failed\n") ;
return -1 ;
}

printk("pci_dev found, pci_dev = %ld\n", (unsigned long)pci_dev) ;

major = register_chrdev(0, "mydriver", &fops) ;
if(major == -1)
{
printk("register_chrdev failed\n") ;
return -1 ;
}
printk("module is registered with major no = %d\n", major) ;
return 0 ;
}

void cleanup_module(void)
{
printk("I am in cleanup_module\n") ;
unregister_chrdev(major, "mydriver") ;
}

struct file_operations fops =
{
.open = my_open,
.release = my_close,
.mmap = my_mmap,
} ;










#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/mman.h>

main()
{
int fd ;
void *virt_addr ;
int *ptr1, *ptr2, i ;

fd = open("mydevice", O_RDWR) ;
if (fd == -1)
{
printf("open failed\n") ;
exit(-1) ;
}

virt_addr = mmap(NULL, 10000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0) ;
if (virt_addr == MAP_FAILED)
{
printf("mmap failed\n") ;
exit(-1) ;
}

ptr1 = (int *) virt_addr ;

ptr2 = (int *) virt_addr ;

printf("ptr1 = %ld, ptr2 = %ld virt_addr=%ld \n",

(unsigned long)ptr1, (unsigned long)ptr2,

(unsigned long)virt_addr) ;

for(i = 0 ; i < 2048 ; i++)
{
*(ptr1 + i) = i ;
}

for(i = 0 ; i < 2048 ; i++)
{
printf("%d ", *(ptr2 + i)) ;
}

munmap(virt_addr, 80) ;

close(fd) ;

return 0 ;
}










Attachment: Makefile
Description: Binary data