Skip to content

Commit

Permalink
nopage is obsolete in vm_operations_struct, saving
Browse files Browse the repository at this point in the history
  • Loading branch information
troydhanson committed Mar 5, 2015
1 parent 47737a1 commit eda46af
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,6 @@
*.i*86
*.x86_64
*.hex

# editor
*.swp
14 changes: 14 additions & 0 deletions 205.fault/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# invoke as
#
# make -C /path/to/kernel/source SUBDIRS=$PWD modules
#
# e.g.,
# make -C /lib/modules/3.13.0-24-generic/build/ SUBDIRS=$PWD modules
#

obj-m := kex.o

rig: rig.c

clean:
rm -f rig
47 changes: 47 additions & 0 deletions 205.fault/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
Pagefault
vma ops

This kex module implements mmap and includes a test rig to exercise it.
When the test rig invokes mmap on the device, kex uses alloc_page
to get as many pages as the rig requested. It uses vm_insert_page on
each page to map it into rig's address space. These pages, while
physically disjoint, appear as a single contiguous virtual memory area
(VMA) inside the rig address space. The VMA is visible using pmap:

```
% ./build.sh
% sudo insmod kex.ko
% cat /proc/devices | grep kex
250 kex
% sudo mknod dev c 250 0
% sudo chmod a+rw dev
% ./rig dev 1
pid: 10195
press a key to exercise memory write
```

Leave the rig waiting. In another terminal run pmap. See the dev VMA:

```
% pmap -x 10195
Address Kbytes RSS Dirty Mode Mapping
00007fd0125b6000 4 4 0 rw-s- dev
```

It's 4kb because rig asked for one page (the final argument), which is 4kb.
Now we can ask rig to write to this memory. In the original terminal, press
enter. Then re-run pmap in the second terminal:

```
Address Kbytes RSS Dirty Mode Mapping
00007fd0125b6000 4 4 4 rw-s- dev
```

Notice that the Dirty column now shows the mapped page is Dirty. (TODO: is
this an MMU flag set on write to the page?). Press enter to terminate rig.

Clean up:

% rm dev
% sudo rmmod kex
% ./build.sh clean
10 changes: 10 additions & 0 deletions 205.fault/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash
KERNELHEADERS=/lib/modules/$(uname -r)/build/
make -C ${KERNELHEADERS} SUBDIRS=$PWD $1

if [ "$1" == "clean" ]
then
make clean
else
make rig
fi
93 changes: 93 additions & 0 deletions 205.fault/kex.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/mm.h>

/* module information */
MODULE_AUTHOR("Troy D. Hanson");
MODULE_DESCRIPTION("Example of character device");
MODULE_LICENSE("Dual BSD/GPL");

#define NUM_MINORS 1

DEFINE_MUTEX(mutex);

struct chardev_t {
dev_t dev; /* has major and minor bits */
struct cdev cdev; /* has our ops, owner, etc */
} c;

static
struct page *fault(struct vm_area_struct *vma, unsigned long addr, int *type) {
struct page *page=NULL;
//get_page();
if (type) *type=VM_FAULT_MINOR;
return page;
}

/* ops for VMA open, close and fault. See Linux Device Drivers, 3rd ed, p427. */
static struct vm_operations_struct vm_ops = {
.nopage = fault, /* page fault handler */
};

int _mmap(struct file *f, struct vm_area_struct *vma) {
unsigned long pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
int rc=-ENODEV;
printk(KERN_DEBUG "vma->vm_end %lu vm_start %lu len %lu pages %lu vm_pgoff %lu\n",
vma->vm_end, vma->vm_start, vma->vm_end - vma->vm_start, pages, vma->vm_pgoff);

if (0) goto done;
vma->vm_ops = &vm_ops;
vma->vm_private_data = (void*)0xa1fa1fa; /* could store private data here */

rc = 0;

done:
return rc;
}

struct file_operations ops = {
.mmap = _mmap
};

int __init chardev_init(void) {
int rc;

/* ask for a dynamic major */
rc = alloc_chrdev_region(&c.dev, 0, NUM_MINORS, "kex");
if (rc) {
rc = -ENODEV;
goto done;
}

/* init the struct cdev */
cdev_init(&c.cdev, &ops);
c.cdev.owner = THIS_MODULE;

/* make device live */
rc = cdev_add(&c.cdev, c.dev, NUM_MINORS);
if (rc) {
rc = -ENODEV;
printk(KERN_WARNING "cdev_add: can't add device\n");
unregister_chrdev_region(c.dev, NUM_MINORS);
cdev_del(&c.cdev);
goto done;
}

rc = 0;

done:
return rc;
}

void __exit chardev_cleanup(void) {
cdev_del(&c.cdev);
unregister_chrdev_region(c.dev, NUM_MINORS);
}

module_init(chardev_init);
module_exit(chardev_cleanup);
48 changes: 48 additions & 0 deletions 205.fault/rig.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/fcntl.h>


int main(int argc, char * argv[]) {
char *file = (argc > 1) ? argv[1] : "dev";
int npages = (argc > 2) ? atoi(argv[2]) : 1;
int fd,rc=-1;
char *buf, *b, unused;
size_t len = 4096 * npages; // FIXME getpagesz

fprintf(stderr,"pid: %u\n", (int)getpid());

if ( (fd = open(file, O_RDWR)) == -1) {
fprintf(stderr,"can't open %s: %s\n", file, strerror(errno));
goto done;
}

buf = mmap(0, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (buf == MAP_FAILED) {
fprintf(stderr, "failed to mmap %s: %s\n", file, strerror(errno));
goto done;
}

rc = 0;

/* make the program block so we can examine it */

fprintf(stderr,"press a key to exercise memory write\n");
read(STDIN_FILENO,&unused,sizeof(unused));
for(b=buf; b < buf+len; b++) *b=1;

fprintf(stderr,"press a key to terminate\n");
read(STDIN_FILENO,&unused,sizeof(unused));


done:
munmap(buf, len);
close(fd);
return rc;
}
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ TODO
* ops: poll, mmap
* kernel linked list, kfifo
* pci, usb

* kgdb
* initramfs
* kvm
* net nic
* waitqueues

0 comments on commit eda46af

Please sign in to comment.