• Two components
– pfn_ctrl_dev.ko kernel module
• Creates a device file (/dev/pfn_ctrl)
• Assign a physical page upon a page fault
– test_pfn.c C program
• Open and mmap /dev/pfn_ctrl
• Read the mmaped area
• Write the mmaped area
static const struct file_operations pfn_ctrl_fops = {
.owner = THIS_MODULE,
.mmap = pfn_ctrl_mmap,
};
static struct miscdevice pfn_ctrl_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "pfn_ctrl",
.fops = &pfn_ctrl_fops,
};
static int __init pfn_ctrl_dev_init(void) // 로딩시 콜백함수
{
int err;
misc_register(&pfn_ctrl_dev); // 해당 디바이스에 대한 정보는 변수에 적혀있음
pr_info("Hello, pfn_ctrl_dev is ready\n");
return 0;
}
static int __init pfn_ctrl_dev_exit(void) // 언로딩시 콜백함수
{
misc_deregister(&pfn_ctrl_dev);
/***** Write your code *****/
// free pages used in this device
/***********************/
pr_info("Good bye!\n“);
}
module_init(pfn_ctrl_dev_init); /
module_exit(pfn_ctrl_dev_exit);
static const struct file_operations pfn_ctrl_fops = {
.owner = THIS_MODULE,
.mmap = pfn_ctrl_mmap, //
};
static struct miscdevice pfn_ctrl_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "pfn_ctrl",
.fops = &pfn_ctrl_fops,
};
static int __init pfn_ctrl_dev_init(void)
{
int err;
misc_register(&pfn_ctrl_dev);
pr_info("Hello, pfn_ctrl_dev is ready\n");
return 0;
}
static int __init pfn_ctrl_dev_exit(void)
{
misc_deregister(&pfn_ctrl_dev);
/***** Write your code *****/
// free pages used in this device
/***********************/
pr_info("Good bye!\n“);
}
module_init(pfn_ctrl_dev_init);
module_exit(pfn_ctrl_dev_exit);
static const struct vm_operations_struct
pfn_ctrl_vm_ops = {
.fault = pfn_ctrl_fault, // 작성해야할 코드
};
static int pfn_ctrl_mmap(struct file *file,
struct vm_area_struct *vma)
{
vma->vm_ops = &pfn_ctrl_vm_ops; // vma
pr_info("mmap success\n“);
return 0;
}
static vm_fault_t pfn_ctrl_fault(struct vm_fault *vmf) // vmf의 내용
{
struct page* page = NULL;
/***** Write your code *****/
/************************/
pr_info("%lx %lx\n", vmf->address, page_to_pfn(page)) // 해당 폴트가 어느 주소에서 발생했고 그 프레임 넘버는 ~다.
/* should return 0 if fauld is handled */
return VM_FAULT_SIGBUS; // 종료되도록
}
static vm_fault_t __do_fault(struct vm_fault *vmf)
{
...
ret = vma->vm_ops->fault(vmf);
/* This may call pfn_ctrl_fault() */
...
}
struct vm_fault {
const struct {
struct vm_area_struct *vma;
unsigned long address; /* fault address */ // fault 발생 주소
pgoff_t pgoff; /* offset of faulted page */ //
}
struct page *page; /* assigned page */ // 폴트 핸들링 수행 후 해당 어드레스에 매핑을 할 페이지 프레임에 대한 디스크립터
• User-level application to mmap a virtual address range and print PFNs
– open(/dev/pfn_ctrl) // 파일 오픈
– mmap() to the device “pfn_ctrl” // mmap 수행
– Touch mmaped region (to cause page fault) // 해당 메모리 영역을 touch(read,write)
– Read PFNs of mmapped region (will be done in Practice 2)
• Via /proc/[pid]/pagemap
• read_pfn() currently returns 0 // pfn을 읽어봄. 현재는 0을 출력
– Usage
• ./test_pfn
int main(int argc, char *argv[])
{
...
/* Open device file /dev/pfn_ctrl */
fd = open("/dev/pfn_ctrl", O_RDWR);
...
/* mmap nr_pages * PAGE_SIZE */
ptr = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
printf("mmaped address %p\n", ptr);
printf("reading address %p data:%d\n", ptr, *ptr);
printf("PFN of address %p is %lx\n", ptr, read_pfn(ptr)); // 실습2
printf("writing address %p\n", ptr);
*ptr = getpid();
printf("PFN of address %p is %lx\n", ptr, read_pfn(ptr));
munmap(ptr, PAGE_SIZE);
close(fd);
}
static vm_fault_t pfn_ctrl_fault(struct vm_fault *vmf)
{
struct page* page = NULL;
/***** Write your code *****/
/* Steps
* 1) identify the pgoff of this page fault // 어느 페이지 오프셋에서 발생했는지 판단
* 2) allocate a new page if that pgoff has no allocated page // 해당 페이지 오프셋에 해당하는 데이터페이지가 디바이스에 존재하는지? 그렇지 않으면 새로운 페이지 할당
* 3) return the page through the vm_fault structure
* 4) return 0; // do NOT return VM_FAULT_SIGBUS
*/
/************************/
pr_info("%lx %lx\n", vmf->address, page_to_pfn(page))
/* should return 0 if fauld is handled */
return VM_FAULT_SIGBUS;
}
• 페이지 할당 함수
– 버디 할당자
– alloc_pages(gfp, order)
• include/linux/gfp.h
• 페이지 reference counting
– get_page(page)
– fault handling은 PTE가 page를
reference하는 동작
static const struct file_operations pfn_ctrl_fops = {
.owner = THIS_MODULE,
.mmap = pfn_ctrl_mmap,
};
static struct miscdevice pfn_ctrl_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "pfn_ctrl",
.fops = &pfn_ctrl_fops,
};
static int __init pfn_ctrl_dev_init(void)
{
int err;
misc_register(&pfn_ctrl_dev);
pr_info("Hello, pfn_ctrl_dev is ready\n");
return 0;
}
static int __init pfn_ctrl_dev_exit(void)
{
misc_deregister(&pfn_ctrl_dev);
/***** Write your code *****/
// free pages used in this device // 해제 코드 (메모리 릭 방지)
/***********************/
pr_info("Good bye!\n“);
}
module_init(pfn_ctrl_dev_init);
module_exit(pfn_ctrl_dev_exit);
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/rmap.h>
#include <linux/dev_printk.h>
#include <linux/list.h>
#include <asm/atomic.h>
#include <asm/uaccess.h>
#define MAX_NR_PAGES 10
static struct page* pages[MAX_NR_PAGES];
static vm_fault_t pfn_ctrl_fault(struct vm_fault *vmf)
{
struct page* page = NULL;
if ( pages[vmf->pgoff] == NULL ) {
page = alloc_pages(GFP_KERNEL,0);
pages[vmf->pgoff] = page;
}
vmf->page = pages[vmf->pgoff];
get_page(vmf->page);
pr_info("%lx %lx\n", vmf->address, page_to_pfn(vmf->page));
return 0;
/* Steps
* 1) identify the pgoff of this page fault
* 2) allocate a new page if that pgoff has no allocated page
* 3) return the page through the vm_fault structure
* 4) return 0; // do NOT return VM_FAULT_SIGBUS
*/
}
static const struct vm_operations_struct pfn_ctrl_vm_ops = {
.fault = pfn_ctrl_fault,
};
/* mmap can map up to 10 pages */
static int pfn_ctrl_mmap(struct file *file, struct vm_area_struct *vma)
{
vma->vm_ops = &pfn_ctrl_vm_ops;
return 0;
}
static const struct file_operations pfn_ctrl_fops = {
.owner = THIS_MODULE,
.mmap = pfn_ctrl_mmap,
};
static struct miscdevice pfn_ctrl_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "pfn_ctrl",
.fops = &pfn_ctrl_fops,
};
static int __init pfn_ctrl_dev_init(void)
{
int err = 0;
err = misc_register(&pfn_ctrl_dev);
pr_info("Hello!\n");
return err;
}
static void __exit pfn_ctrl_dev_exit(void)
{
/* free pages that are used by this device */
int i;
for ( i=0; i < MAX_NR_PAGES; ++i ) {
if ( pages[i] != NULL )
__free_pages(pages[i], 0);
}
misc_deregister(&pfn_ctrl_dev);
pr_info("Good bye!\n");
}
module_init(pfn_ctrl_dev_init);
module_exit(pfn_ctrl_dev_exit);
MODULE_DESCRIPTION("Simple Page Fault Handling Device");
MODULE_LICENSE("GPL");