개요

• 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

pfn_ctrl_dev.c Kernel Module (1)

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);

pfn_ctrl_dev.c Kernel Module (2)

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;
}

pfn_ctrl_dev.c Kernel Module (3)

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; // 종료되도록
}

Kernel (mm/memory.c)

static vm_fault_t __do_fault(struct vm_fault *vmf) 
{
...
ret = vma->vm_ops->fault(vmf); 
/* This may call pfn_ctrl_fault() */
...
}

Kernel (include/linux/mm.h)

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 */ // 폴트 핸들링 수행 후 해당 어드레스에 매핑을 할 페이지 프레임에 대한 디스크립터

test_pfn.c (1)

• 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

test_pfn.c (2)

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);

  • 5969는 writing 한것의 pid
    • 이전에 이 메모리를 접근했던 프로세스의 pid

작성 코드

#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");

  • 왼쪽 주소에 pfn을 할당했다.
  • pfn을 fault를 계속 발생시켰지만 메모리할당은 한번만.
  • 그 이후에는 페이지를 이미 가지고 있었기 때문에 이후 동일한 프레임이 사용됨

0개의 댓글

Powered by GraphCDN, the GraphQL CDN