├── .gitignore ├── 101.init ├── Makefile ├── README.md ├── build.sh └── kex.c ├── 102.info ├── Makefile ├── README.md ├── build.sh └── kex.c ├── 103.parms ├── Makefile ├── README.md ├── build.sh └── kex.c ├── 104.cdev ├── Makefile ├── README.md ├── build.sh └── kex.c ├── 105.ops ├── Makefile ├── README.md ├── build.sh └── kex.c ├── 106.rw ├── Makefile ├── README.md ├── build.sh ├── kex.c └── rw.py ├── 107.ioctl ├── Makefile ├── README.md ├── build.sh ├── kex.c ├── kex.h └── rig.c ├── 108.procfs ├── Makefile ├── README.md ├── build.sh └── kex.c ├── 109.inode ├── Makefile ├── README.md ├── build.sh └── kex.c ├── 110.linked_lists ├── Makefile ├── README.md ├── build.sh └── kex.c ├── 201.gfp ├── Makefile ├── README.md ├── build.sh ├── kex.c └── rw.py ├── 202.mutex ├── Makefile ├── README.md ├── build.sh └── kex.c ├── 203.alloc_page ├── Makefile ├── README.md ├── build.sh └── kex.c ├── 204.mmap ├── Makefile ├── README.md ├── build.sh ├── kex.c └── rig.c ├── 205.fault ├── Makefile ├── README.md ├── build.sh ├── kex.c └── rig.c ├── 301.HZ ├── Makefile ├── README.md ├── build.sh └── kex.c ├── 302.waitqueue └── README.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # editor 32 | *.swp 33 | -------------------------------------------------------------------------------- /101.init/Makefile: -------------------------------------------------------------------------------- 1 | # invoke as 2 | # 3 | # make -C /path/to/kernel/source SUBDIRS=$PWD modules 4 | # 5 | # e.g., 6 | # make -C /lib/modules/3.13.0-24-generic/build/ SUBDIRS=$PWD modules 7 | # 8 | 9 | obj-m := kex.o 10 | -------------------------------------------------------------------------------- /101.init/README.md: -------------------------------------------------------------------------------- 1 | The kex (kernel example) module initializes itself, then does nothing. 2 | Its debug messages are visible with dmesg when it's loaded and unloaded. 3 | 4 | Prerequisite: kernel headers 5 | 6 | The kernel headers are required to build and link a kernel module (.ko). 7 | Debian derivates like Ubuntu and Mint can use the first command below to 8 | check if they are installed. The second command installs them. 9 | 10 | % dpkg-query -s linux-headers-$(uname -r) 11 | % sudo apt-get install linux-headers-$(uname -r) 12 | 13 | Set an environment variable to the kernel headers then build the module: 14 | 15 | % KERNELHEADERS=/lib/modules/$(uname -r)/build/ 16 | % make -C ${KERNELHEADERS} SUBDIRS=$PWD modules 17 | 18 | To simplify matters there is a script that does these two commands: 19 | 20 | % ./build.sh 21 | 22 | Load and unload the module: 23 | 24 | % sudo insmod kex.ko 25 | % sudo rmmod kex 26 | 27 | See the debug messages in the kernel log: 28 | 29 | % dmesg | tail 30 | 31 | Clean up: 32 | 33 | % ./build.sh clean 34 | -------------------------------------------------------------------------------- /101.init/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | KERNELHEADERS=/lib/modules/$(uname -r)/build/ 3 | make -C ${KERNELHEADERS} SUBDIRS=$PWD $1 4 | -------------------------------------------------------------------------------- /101.init/kex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* __init and __exit make it work as a static driver too */ 6 | int __init kex_init(void) { 7 | printk("kex module init called\n"); 8 | return 0; 9 | } 10 | 11 | void __exit kex_cleanup(void) { 12 | printk("kex module exit called\n"); 13 | } 14 | 15 | /* module_init and module_exit make it work as a loadable module */ 16 | module_init(kex_init); 17 | module_exit(kex_cleanup); 18 | -------------------------------------------------------------------------------- /102.info/Makefile: -------------------------------------------------------------------------------- 1 | # invoke as 2 | # 3 | # make -C /path/to/kernel/source SUBDIRS=$PWD modules 4 | # 5 | # e.g., 6 | # make -C /lib/modules/3.13.0-24-generic/build/ SUBDIRS=$PWD modules 7 | # 8 | 9 | obj-m := kex.o 10 | -------------------------------------------------------------------------------- /102.info/README.md: -------------------------------------------------------------------------------- 1 | This version of the kex module adds author and license info. 2 | The compiled module retains this information. The modinfo 3 | command displays it. The kernel also tracks whether a GPL- 4 | compatible license applies to the module. 5 | 6 | ``` 7 | % ./build.sh 8 | % modinfo kex.ko 9 | 10 | filename: /home/kernel/kex/./kex.ko 11 | license: Dual BSD/GPL 12 | description: Example of module information 13 | author: Troy D. Hanson 14 | srcversion: 2249017AD0425E81C3EFBC9 15 | depends: 16 | vermagic: 3.13.0-24-generic SMP mod_unload modversions 17 | ``` 18 | 19 | A "Proprietary" license flags the kernel as tainted. Some 20 | modules only expose their exported symbols to GPL code. 21 | 22 | Clean up: 23 | 24 | % ./build.sh clean 25 | -------------------------------------------------------------------------------- /102.info/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | KERNELHEADERS=/lib/modules/$(uname -r)/build/ 3 | make -C ${KERNELHEADERS} SUBDIRS=$PWD $1 4 | -------------------------------------------------------------------------------- /102.info/kex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* module information */ 6 | MODULE_AUTHOR("Troy D. Hanson"); 7 | MODULE_DESCRIPTION("Example of module information"); 8 | MODULE_LICENSE("Dual BSD/GPL"); 9 | 10 | 11 | /* __init and __exit make it work as a static driver too */ 12 | int __init kex_init(void) { 13 | printk("kex module init called\n"); 14 | return 0; 15 | } 16 | 17 | void __exit kex_cleanup(void) { 18 | printk("kex module exit called\n"); 19 | } 20 | 21 | /* module_init and module_exit make it work as a loadable module */ 22 | module_init(kex_init); 23 | module_exit(kex_cleanup); 24 | -------------------------------------------------------------------------------- /103.parms/Makefile: -------------------------------------------------------------------------------- 1 | # invoke as 2 | # 3 | # make -C /path/to/kernel/source SUBDIRS=$PWD modules 4 | # 5 | # e.g., 6 | # make -C /lib/modules/3.13.0-24-generic/build/ SUBDIRS=$PWD modules 7 | # 8 | 9 | obj-m := kex.o 10 | -------------------------------------------------------------------------------- /103.parms/README.md: -------------------------------------------------------------------------------- 1 | This version of kex shows how to add a parameter to a module. 2 | Their types include byte, bool, int, long, charp, uint, etc. 3 | 4 | ``` 5 | % ./build.sh 6 | % modinfo kex.ko 7 | parm: verbose:set verbosity level (int) 8 | ``` 9 | 10 | Parameters can be given values when the module is loaded: 11 | 12 | % sudo insmod kex.ko verbose=1 13 | 14 | Now the the module is loaded, its parameters appear in sysfs: 15 | 16 | ``` 17 | % cd /sys/module/kex/parameters 18 | % ls -l 19 | -rw-r--r-- 1 root root 4096 Feb 15 18:23 verbose 20 | % cat verbose 21 | 1 22 | % # need to be root for the next line 23 | % echo 10 > verbose 24 | % cat verbose 25 | 10 26 | ``` 27 | 28 | Change back to the build directory, and clean up: 29 | 30 | % sudo rmmod kex 31 | % ./build.sh clean 32 | -------------------------------------------------------------------------------- /103.parms/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | KERNELHEADERS=/lib/modules/$(uname -r)/build/ 3 | make -C ${KERNELHEADERS} SUBDIRS=$PWD $1 4 | -------------------------------------------------------------------------------- /103.parms/kex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* module information */ 6 | MODULE_AUTHOR("Troy D. Hanson"); 7 | MODULE_DESCRIPTION("Example of module parameters"); 8 | MODULE_LICENSE("Dual BSD/GPL"); 9 | 10 | 11 | static int verbose=0; 12 | #ifdef MODULE 13 | module_param(verbose, int, 0644); 14 | MODULE_PARM_DESC(verbose, "set verbosity level"); 15 | #endif 16 | 17 | /* __init and __exit make it work as a static driver too */ 18 | int __init kex_init(void) { 19 | printk("kex module init called (verbosity %u)\n",verbose); 20 | return 0; 21 | } 22 | 23 | void __exit kex_cleanup(void) { 24 | printk("kex module exit called (verbosity %u)\n",verbose); 25 | } 26 | 27 | /* module_init and module_exit make it work as a loadable module */ 28 | module_init(kex_init); 29 | module_exit(kex_cleanup); 30 | -------------------------------------------------------------------------------- /104.cdev/Makefile: -------------------------------------------------------------------------------- 1 | # invoke as 2 | # 3 | # make -C /path/to/kernel/source SUBDIRS=$PWD modules 4 | # 5 | # e.g., 6 | # make -C /lib/modules/3.13.0-24-generic/build/ SUBDIRS=$PWD modules 7 | # 8 | 9 | obj-m := kex.o 10 | -------------------------------------------------------------------------------- /104.cdev/README.md: -------------------------------------------------------------------------------- 1 | This version of kex registers a character device driver. 2 | Many modern drivers get their major numbers dynamically. 3 | This is what kex does. After it is loaded, its major and 4 | minor numbers are exposed in /proc/devices. 5 | 6 | ``` 7 | % ./build.sh 8 | % sudo insmod kex.ko 9 | % cat /proc/devices | grep kex 10 | 250 kex 11 | ``` 12 | 13 | Make a device node that uses this driver: 14 | 15 | ``` 16 | % sudo mknod dev c 250 0 17 | % ls -l dev 18 | crw-r--r-- 1 root root 250, 0 Feb 18 09:30 dev 19 | ``` 20 | 21 | This driver has no "ops" yet- it does not do anything. 22 | 23 | Clean up: 24 | 25 | ``` 26 | % ./build.sh clean 27 | % rm dev 28 | % sudo rmmod kex 29 | ``` 30 | -------------------------------------------------------------------------------- /104.cdev/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | KERNELHEADERS=/lib/modules/$(uname -r)/build/ 3 | make -C ${KERNELHEADERS} SUBDIRS=$PWD $1 4 | -------------------------------------------------------------------------------- /104.cdev/kex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | /* module information */ 8 | MODULE_AUTHOR("Troy D. Hanson"); 9 | MODULE_DESCRIPTION("Example of character device"); 10 | MODULE_LICENSE("Dual BSD/GPL"); 11 | 12 | #define NUM_MINORS 1 13 | 14 | /* a global in the driver to keep state */ 15 | struct kex_t { 16 | dev_t dev; /* has major and minor bits */ 17 | struct cdev cdev; /* has our ops, owner, etc */ 18 | } c; 19 | 20 | struct file_operations ops = { 21 | .read = NULL, 22 | .write = NULL, 23 | .unlocked_ioctl = NULL, 24 | .open = NULL, 25 | .release = NULL 26 | }; 27 | 28 | int __init kex_init(void) { 29 | int rc; 30 | 31 | /* ask for a dynamic major */ 32 | rc = alloc_chrdev_region(&c.dev, 0, NUM_MINORS, "kex"); 33 | if (rc) { 34 | printk(KERN_WARNING "alloc_chrdev_region: failed to get major\n"); 35 | goto done; 36 | } 37 | 38 | /* init the struct cdev */ 39 | cdev_init(&c.cdev, &ops); 40 | c.cdev.owner = THIS_MODULE; 41 | 42 | /* make device live */ 43 | rc = cdev_add(&c.cdev, c.dev, NUM_MINORS); 44 | if (rc) { 45 | printk(KERN_WARNING "cdev_add: can't add device\n"); 46 | unregister_chrdev_region(c.dev, NUM_MINORS); 47 | cdev_del(&c.cdev); 48 | goto done; 49 | } 50 | 51 | rc = 0; 52 | 53 | done: 54 | return rc ? -ENODEV : 0; 55 | } 56 | 57 | void __exit kex_cleanup(void) { 58 | cdev_del(&c.cdev); 59 | unregister_chrdev_region(c.dev, NUM_MINORS); 60 | } 61 | 62 | module_init(kex_init); 63 | module_exit(kex_cleanup); 64 | -------------------------------------------------------------------------------- /105.ops/Makefile: -------------------------------------------------------------------------------- 1 | # invoke as 2 | # 3 | # make -C /path/to/kernel/source SUBDIRS=$PWD modules 4 | # 5 | # e.g., 6 | # make -C /lib/modules/3.13.0-24-generic/build/ SUBDIRS=$PWD modules 7 | # 8 | 9 | obj-m := kex.o 10 | -------------------------------------------------------------------------------- /105.ops/README.md: -------------------------------------------------------------------------------- 1 | This version adds support for read() to the kex driver. 2 | 3 | ``` 4 | % ./build.sh 5 | % sudo insmod kex.ko 6 | % cat /proc/devices | grep kex 7 | 250 kex 8 | % sudo mknod dev c 250 0 9 | ``` 10 | 11 | Read from the device: 12 | 13 | ``` 14 | % dd if=dev of=/dev/null bs=101 count=1 15 | 100 bytes (100 B) copied, 0.000175226 s, 57.1 kB/s 16 | ``` 17 | 18 | Each process that opens this device can read up to 100 bytes 19 | from it; further reads return 0 to indicate EOF. This is how 20 | this version of kex behaves. The dd above tried to read 101 21 | bytes but kex only made 100 bytes available. 22 | 23 | Clean up: 24 | 25 | % rm dev 26 | % sudo rmmod kex 27 | % ./build.sh clean 28 | -------------------------------------------------------------------------------- /105.ops/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | KERNELHEADERS=/lib/modules/$(uname -r)/build/ 3 | make -C ${KERNELHEADERS} SUBDIRS=$PWD $1 4 | -------------------------------------------------------------------------------- /105.ops/kex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* module information */ 9 | MODULE_AUTHOR("Troy D. Hanson"); 10 | MODULE_DESCRIPTION("Example of character device"); 11 | MODULE_LICENSE("Dual BSD/GPL"); 12 | 13 | #define NUM_MINORS 1 14 | 15 | /* a global to keep state. must be thread safe. */ 16 | struct kex_t { 17 | dev_t dev; /* has major and minor bits */ 18 | struct cdev cdev; /* has our ops, owner, etc */ 19 | } c; 20 | 21 | int _open(struct inode *inode, struct file *f) { 22 | void *per_fd_state = NULL; 23 | /* we can store away some per fd state here. */ 24 | f->private_data = per_fd_state; 25 | /* we can access the rw and blocking mode */ 26 | printk(KERN_DEBUG "open for %c%c\n", ((f->f_mode & FMODE_READ) ? 'r':'-'), 27 | ((f->f_mode & FMODE_WRITE) ? 'w':'-')); 28 | return 0; 29 | } 30 | 31 | /* kernel calls release when fd usage count reaches 0. e.g. if a process opens 32 | * a device, forks, fd usage count is now two; when both close, its released. */ 33 | int _release(struct inode *inode, struct file *f) { 34 | return 0; 35 | } 36 | 37 | /* read data into the userspace buf. some notes: 38 | 1. buf is annotated with a leading __user to help us remember its userspace 39 | 2. on real device a 'read' may block. 40 | 3. a driver can implement both blocking and non-blocking mode if desired. 41 | 4. copy_to_user can sleep (e.g. if userspace buffer needs to be paged in). 42 | this example returns a zeroed buffer. 43 | */ 44 | char device_buf[100]; 45 | ssize_t _read(struct file *f, char __user *buf, size_t count, loff_t *offp) { 46 | ssize_t c = (count < sizeof(device_buf)) ? count : sizeof(device_buf); 47 | memset(device_buf,0,c); 48 | if (copy_to_user(buf, device_buf, c)) return -EFAULT; 49 | *offp += c; 50 | return c; 51 | } 52 | 53 | struct file_operations ops = { 54 | .read = _read, 55 | .write = NULL, 56 | .unlocked_ioctl = NULL, 57 | .open = _open, 58 | .release = _release 59 | }; 60 | 61 | int __init kex_init(void) { 62 | int rc; 63 | 64 | /* ask for a dynamic major */ 65 | rc = alloc_chrdev_region(&c.dev, 0, NUM_MINORS, "kex"); 66 | if (rc) { 67 | printk(KERN_WARNING "alloc_chrdev_region: failed to get major\n"); 68 | goto done; 69 | } 70 | 71 | /* init the struct cdev */ 72 | cdev_init(&c.cdev, &ops); 73 | c.cdev.owner = THIS_MODULE; 74 | 75 | /* make device live */ 76 | rc = cdev_add(&c.cdev, c.dev, NUM_MINORS); 77 | if (rc) { 78 | printk(KERN_WARNING "cdev_add: can't add device\n"); 79 | unregister_chrdev_region(c.dev, NUM_MINORS); 80 | cdev_del(&c.cdev); 81 | goto done; 82 | } 83 | 84 | rc = 0; 85 | 86 | done: 87 | return rc ? -ENODEV : 0; 88 | } 89 | 90 | void __exit kex_cleanup(void) { 91 | cdev_del(&c.cdev); 92 | unregister_chrdev_region(c.dev, NUM_MINORS); 93 | } 94 | 95 | module_init(kex_init); 96 | module_exit(kex_cleanup); 97 | -------------------------------------------------------------------------------- /106.rw/Makefile: -------------------------------------------------------------------------------- 1 | # invoke as 2 | # 3 | # make -C /path/to/kernel/source SUBDIRS=$PWD modules 4 | # 5 | # e.g., 6 | # make -C /lib/modules/3.13.0-24-generic/build/ SUBDIRS=$PWD modules 7 | # 8 | 9 | obj-m := kex.o 10 | -------------------------------------------------------------------------------- /106.rw/README.md: -------------------------------------------------------------------------------- 1 | This version of kex supports both read and write. This version also keeps 2 | per-descriptor state. In other words, each process that opens it gets a 3 | state variable. The state is simply the count of bytes written to it on 4 | that descriptor. When we read from it, it returns that count as a long. 5 | 6 | ``` 7 | % ./build.sh 8 | % sudo insmod kex.ko 9 | % cat /proc/devices | grep kex 10 | 250 kex 11 | % sudo mknod dev c 250 0 12 | % sudo chmod a+rw dev 13 | ``` 14 | 15 | We use a helper script to open the device, write to it, and then read it: 16 | 17 | ``` 18 | % sudo ./rw.py 19 | 5 20 | ``` 21 | 22 | Clean up: 23 | 24 | % rm dev 25 | % sudo rmmod kex 26 | % ./build.sh clean 27 | -------------------------------------------------------------------------------- /106.rw/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | KERNELHEADERS=/lib/modules/$(uname -r)/build/ 3 | make -C ${KERNELHEADERS} SUBDIRS=$PWD $1 4 | -------------------------------------------------------------------------------- /106.rw/kex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /* module information */ 10 | MODULE_AUTHOR("Troy D. Hanson"); 11 | MODULE_DESCRIPTION("Example of character device"); 12 | MODULE_LICENSE("Dual BSD/GPL"); 13 | 14 | #define NUM_MINORS 1 15 | 16 | /* a global to keep state. must be thread safe. */ 17 | struct kex_t { 18 | dev_t dev; /* has major and minor bits */ 19 | struct cdev cdev; /* has our ops, owner, etc */ 20 | } c; 21 | 22 | /* keep a count of bytes "sunk" (written) to this fd. */ 23 | int _open(struct inode *inode, struct file *f) { 24 | long *bytes_sunk = (long*)kmalloc(sizeof(long), GFP_KERNEL); 25 | if (bytes_sunk==NULL) return -ENOMEM; 26 | *bytes_sunk = 0; 27 | f->private_data = bytes_sunk; 28 | return 0; 29 | } 30 | 31 | int _release(struct inode *inode, struct file *f) { 32 | long *bytes_sunk = (long*)f->private_data; 33 | printk(KERN_DEBUG "sunk %lu bytes @ close\n", *bytes_sunk); 34 | kfree(f->private_data); 35 | return 0; 36 | } 37 | 38 | /* return the number of bytes (as a long) ever written to this fd */ 39 | ssize_t _read(struct file *f, char __user *buf, size_t count, loff_t *offp) { 40 | if (count < sizeof(long)) return -EINVAL; 41 | if (copy_to_user(buf, f->private_data, sizeof(long))) return -EFAULT; 42 | *offp += sizeof(long); 43 | return sizeof(long); 44 | } 45 | 46 | /* we simply add the byte count to the tally for this fd */ 47 | ssize_t _write(struct file *f, const char __user *buf, size_t count, 48 | loff_t *offp) { 49 | long *bytes_sunk = (long*)f->private_data; 50 | *bytes_sunk += count; 51 | *offp += count; 52 | return count; 53 | } 54 | 55 | struct file_operations ops = { 56 | .read = _read, 57 | .write = _write, 58 | .unlocked_ioctl = NULL, 59 | .open = _open, 60 | .release = _release 61 | }; 62 | 63 | int __init kex_init(void) { 64 | int rc; 65 | 66 | /* ask for a dynamic major */ 67 | rc = alloc_chrdev_region(&c.dev, 0, NUM_MINORS, "kex"); 68 | if (rc) { 69 | printk(KERN_WARNING "alloc_chrdev_region: failed to get major\n"); 70 | goto done; 71 | } 72 | 73 | /* init the struct cdev */ 74 | cdev_init(&c.cdev, &ops); 75 | c.cdev.owner = THIS_MODULE; 76 | 77 | /* make device live */ 78 | rc = cdev_add(&c.cdev, c.dev, NUM_MINORS); 79 | if (rc) { 80 | printk(KERN_WARNING "cdev_add: can't add device\n"); 81 | unregister_chrdev_region(c.dev, NUM_MINORS); 82 | cdev_del(&c.cdev); 83 | goto done; 84 | } 85 | 86 | rc = 0; 87 | 88 | done: 89 | return rc ? -ENODEV : 0; 90 | } 91 | 92 | void __exit kex_cleanup(void) { 93 | cdev_del(&c.cdev); 94 | unregister_chrdev_region(c.dev, NUM_MINORS); 95 | } 96 | 97 | module_init(kex_init); 98 | module_exit(kex_cleanup); 99 | -------------------------------------------------------------------------------- /106.rw/rw.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import os,struct 3 | 4 | dev = os.open("dev",os.O_RDWR) 5 | os.write(dev, "hello") 6 | count = os.read(dev,8) 7 | c = struct.unpack("l",count) 8 | print c[0] 9 | -------------------------------------------------------------------------------- /107.ioctl/Makefile: -------------------------------------------------------------------------------- 1 | # invoke as 2 | # 3 | # make -C /path/to/kernel/source SUBDIRS=$PWD modules 4 | # 5 | # e.g., 6 | # make -C /lib/modules/3.13.0-24-generic/build/ SUBDIRS=$PWD modules 7 | # 8 | 9 | obj-m := kex.o 10 | 11 | rig: rig.c 12 | 13 | clean: 14 | rm -f rig 15 | -------------------------------------------------------------------------------- /107.ioctl/README.md: -------------------------------------------------------------------------------- 1 | We add an ioctl handler to our kex driver. It toggles 2 | whether kex is in "counting mode". In counting mode, it 3 | counts bytes written to it (on a per descriptor basis). 4 | 5 | There are several new things. We are using a structure 6 | as the per-descriptor state rather than a simple long. 7 | We have created a header file using some kernel macros 8 | to define an ioctl that userspace (rig.c) can utilize. 9 | 10 | ``` 11 | % ./build.sh 12 | % sudo insmod kex.ko 13 | % cat /proc/devices | grep kex 14 | 250 kex 15 | % sudo mknod dev c 250 0 16 | % sudo chmod a+rw dev 17 | ``` 18 | 19 | Try out the test program: 20 | 21 | ``` 22 | % ./rig 23 | count: 5 24 | count: 10 25 | count: 10 26 | count: 15 27 | ``` 28 | 29 | Notice that the third count remained at 10. This is 30 | because it had toggled "counting mode" with the ioctl. 31 | 32 | Clean up: 33 | 34 | % rm dev 35 | % sudo rmmod kex 36 | % ./build.sh clean 37 | -------------------------------------------------------------------------------- /107.ioctl/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | KERNELHEADERS=/lib/modules/$(uname -r)/build/ 3 | make -C ${KERNELHEADERS} SUBDIRS=$PWD $1 4 | 5 | if [ "$1" == "clean" ] 6 | then 7 | make clean 8 | else 9 | make rig 10 | fi 11 | -------------------------------------------------------------------------------- /107.ioctl/kex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "kex.h" 11 | 12 | /* module information */ 13 | MODULE_AUTHOR("Troy D. Hanson"); 14 | MODULE_DESCRIPTION("Example of character device"); 15 | MODULE_LICENSE("Dual BSD/GPL"); 16 | 17 | #define NUM_MINORS 1 18 | 19 | /* a global to keep state. must be thread safe. */ 20 | struct kex_t { 21 | dev_t dev; /* has major and minor bits */ 22 | struct cdev cdev; /* has our ops, owner, etc */ 23 | } c; 24 | 25 | struct fd_state { 26 | long bytes_sunk; /* count of bytes written to fd */ 27 | int counting; /* 1 if we're in counting mode */ 28 | }; 29 | 30 | /* keep a count of bytes "sunk" (written) to this fd. */ 31 | int _open(struct inode *inode, struct file *f) { 32 | struct fd_state *s = (struct fd_state*)kmalloc(sizeof(*s), GFP_KERNEL); 33 | if (s==NULL) return -ENOMEM; 34 | s->bytes_sunk = 0; 35 | s->counting = 1; 36 | f->private_data = s; 37 | return 0; 38 | } 39 | 40 | int _release(struct inode *inode, struct file *f) { 41 | struct fd_state *s = (struct fd_state*)f->private_data; 42 | printk(KERN_DEBUG "sunk %lu bytes @ close\n", s->bytes_sunk); 43 | kfree(f->private_data); 44 | return 0; 45 | } 46 | 47 | /* return the number of bytes (as a long) ever written to this fd */ 48 | ssize_t _read(struct file *f, char __user *buf, size_t count, loff_t *offp) { 49 | struct fd_state *s = (struct fd_state*)f->private_data; 50 | if (count < sizeof(long)) return -EINVAL; 51 | if (copy_to_user(buf, &s->bytes_sunk, sizeof(long))) return -EFAULT; 52 | *offp += sizeof(long); 53 | return sizeof(long); 54 | } 55 | 56 | /* we simply add the byte count to the tally for this fd */ 57 | ssize_t _write(struct file *f, const char __user *buf, size_t count, 58 | loff_t *offp) { 59 | struct fd_state *s = (struct fd_state*)f->private_data; 60 | if (s->counting) s->bytes_sunk += count; 61 | *offp += count; 62 | return count; 63 | } 64 | 65 | long _ioctl(struct file *f, unsigned int cmd, unsigned long arg) { 66 | struct fd_state *s = (struct fd_state*)f->private_data; 67 | long rc = -ENOTTY; 68 | 69 | if (_IOC_TYPE(cmd) != FOO_MAGIC) goto done; /* wrong ioctl for device */ 70 | if (_IOC_NR(cmd) > FOO_NR) goto done; /* ioctl is not supported */ 71 | 72 | switch (cmd) { 73 | case FOO_IOCTL_TOGGLE: 74 | s->counting = s->counting ? 0 : 1; 75 | break; 76 | default: 77 | goto done; 78 | break; 79 | } 80 | 81 | rc = 0; 82 | 83 | done: 84 | return rc; 85 | } 86 | 87 | struct file_operations ops = { 88 | .read = _read, 89 | .write = _write, 90 | .unlocked_ioctl = _ioctl, 91 | .open = _open, 92 | .release = _release 93 | }; 94 | 95 | int __init kex_init(void) { 96 | int rc; 97 | 98 | /* ask for a dynamic major */ 99 | rc = alloc_chrdev_region(&c.dev, 0, NUM_MINORS, "kex"); 100 | if (rc) { 101 | printk(KERN_WARNING "alloc_chrdev_region: failed to get major\n"); 102 | goto done; 103 | } 104 | 105 | /* init the struct cdev */ 106 | cdev_init(&c.cdev, &ops); 107 | c.cdev.owner = THIS_MODULE; 108 | 109 | /* make device live */ 110 | rc = cdev_add(&c.cdev, c.dev, NUM_MINORS); 111 | if (rc) { 112 | printk(KERN_WARNING "cdev_add: can't add device\n"); 113 | unregister_chrdev_region(c.dev, NUM_MINORS); 114 | cdev_del(&c.cdev); 115 | goto done; 116 | } 117 | 118 | rc = 0; 119 | 120 | done: 121 | return rc ? -ENODEV : 0; 122 | } 123 | 124 | void __exit kex_cleanup(void) { 125 | cdev_del(&c.cdev); 126 | unregister_chrdev_region(c.dev, NUM_MINORS); 127 | } 128 | 129 | module_init(kex_init); 130 | module_exit(kex_cleanup); 131 | -------------------------------------------------------------------------------- /107.ioctl/kex.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define FOO_MAGIC (0xff) /* anything we want */ 4 | 5 | /* let user apps use FOO_IOCTL_TOGGLE in ioctl */ 6 | #define FOO_IOCTL_TOGGLE _IO(FOO_MAGIC,0) 7 | 8 | #define FOO_NR 1 /* number of ioctls */ 9 | 10 | -------------------------------------------------------------------------------- /107.ioctl/rig.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "kex.h" 9 | 10 | /* 11 | * This is a userspace test rig. 12 | * It exercises our driver's read, write and ioctl. 13 | */ 14 | 15 | /* write bytes, then read the count */ 16 | int do_write(int fd) { 17 | int rc = -1, nr; 18 | long count; 19 | 20 | nr = write(fd,"hello",5); 21 | if (nr != 5) { 22 | fprintf(stderr,"write: %s\n", nr<0 ? strerror(errno) : "incomplete"); 23 | goto done; 24 | } 25 | nr = read(fd,&count,sizeof(count)); 26 | if (nr != sizeof(count)) { 27 | fprintf(stderr,"read: %s\n", nr<0 ? strerror(errno) : "incomplete"); 28 | goto done; 29 | } 30 | fprintf(stderr,"count: %lu\n", count); 31 | 32 | rc = 0; 33 | 34 | done: 35 | return rc; 36 | } 37 | 38 | /* use ioctl to toggle whether driver counts bytes written */ 39 | int do_toggle(int fd) { 40 | int nr, rc = -1; 41 | 42 | nr = ioctl(fd,FOO_IOCTL_TOGGLE); 43 | if (nr < 0) { 44 | fprintf(stderr,"ioctl: %s\n", strerror(errno)); 45 | goto done; 46 | } 47 | 48 | rc = 0; 49 | 50 | done: 51 | return rc; 52 | } 53 | 54 | int main(int argc, char *argv[]) { 55 | int rc = -1, fd; 56 | char *dev = (argc > 1) ? argv[1] : "dev"; 57 | 58 | fd = open(dev,O_RDWR); 59 | if (fd < 0) { 60 | fprintf(stderr,"open '%s': %s\n", dev, strerror(errno)); 61 | goto done; 62 | } 63 | 64 | if (do_write(fd)) goto done; /* prints 5 */ 65 | if (do_write(fd)) goto done; /* prints 10 */ 66 | 67 | if (do_toggle(fd)) goto done; /* stop counting */ 68 | if (do_write(fd)) goto done; /* prints 10 */ 69 | 70 | if (do_toggle(fd)) goto done; /* resume counting */ 71 | if (do_write(fd)) goto done; /* prints 15 */ 72 | 73 | close(fd); 74 | rc = 0; 75 | 76 | done: 77 | return rc; 78 | } 79 | -------------------------------------------------------------------------------- /108.procfs/Makefile: -------------------------------------------------------------------------------- 1 | # invoke as 2 | # 3 | # make -C /path/to/kernel/source SUBDIRS=$PWD modules 4 | # 5 | # e.g., 6 | # make -C /lib/modules/3.13.0-24-generic/build/ SUBDIRS=$PWD modules 7 | # 8 | 9 | obj-m := kex.o 10 | 11 | -------------------------------------------------------------------------------- /108.procfs/README.md: -------------------------------------------------------------------------------- 1 | This version of kex adds an entry in procfs (/proc). When this entry is 2 | read, kex prints its name, and major and minor device numbers. 3 | 4 | ``` 5 | % ./build.sh 6 | % sudo insmod kex.ko 7 | % cat /proc/kex_info 8 | kex_info dev (250,0) 9 | ``` 10 | 11 | Clean up: 12 | 13 | % sudo rmmod kex 14 | % ./build.sh clean 15 | -------------------------------------------------------------------------------- /108.procfs/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | KERNELHEADERS=/lib/modules/$(uname -r)/build/ 3 | make -C ${KERNELHEADERS} SUBDIRS=$PWD $1 4 | 5 | -------------------------------------------------------------------------------- /108.procfs/kex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /* module information */ 11 | MODULE_AUTHOR("Troy D. Hanson"); 12 | MODULE_DESCRIPTION("Example of character device"); 13 | MODULE_LICENSE("Dual BSD/GPL"); 14 | 15 | #define NUM_MINORS 1 16 | 17 | /* a global to keep state. must be thread safe. */ 18 | struct kex_t { 19 | dev_t dev; /* has major and minor bits */ 20 | struct cdev cdev; /* has our ops, owner, etc */ 21 | 22 | /* static stuff we expose in procfs */ 23 | char procfs_buf[100]; 24 | int procfs_buf_len; 25 | 26 | } c; 27 | 28 | struct file_operations ops; 29 | 30 | /****************************************************************************** 31 | * procfs plumbing here 32 | * we need to track whether they've consumed the buffer, otherwise repeated 33 | * read would keep returning the same buffer. use f->private_data to track. 34 | *****************************************************************************/ 35 | #define PROC_ENTRY_NAME "kex_info" 36 | int proc_open(struct inode *inode, struct file *f) { 37 | unsigned long *len = (unsigned long*)&f->private_data; 38 | *len = c.procfs_buf_len; /* we need to write this much data */ 39 | return 0; 40 | } 41 | 42 | /* this is invoked when userspace reads /proc/kex_info */ 43 | ssize_t proc_read(struct file *f, char __user *buf, size_t count, loff_t *offp) { 44 | unsigned long *len = (unsigned long*)&f->private_data; 45 | if (*len == 0) return 0; /* eof */ 46 | if (count < *len) return -EINVAL; 47 | if (copy_to_user(buf, c.procfs_buf, *len)) return -EFAULT; 48 | *offp += *len; 49 | *len = 0; 50 | return c.procfs_buf_len; 51 | } 52 | 53 | /* file ops for our procfs entry */ 54 | struct file_operations procfs_ops = { 55 | .read = proc_read, 56 | .open = proc_open, 57 | }; 58 | 59 | /****************************************************************************** 60 | * module init below. 61 | *****************************************************************************/ 62 | int __init kex_init(void) { 63 | int rc; 64 | struct proc_dir_entry *p; 65 | 66 | /* ask for a dynamic major */ 67 | rc = alloc_chrdev_region(&c.dev, 0, NUM_MINORS, "kex"); 68 | if (rc) { 69 | printk(KERN_WARNING "alloc_chrdev_region: failed to get major\n"); 70 | goto done; 71 | } 72 | 73 | /* init the struct cdev */ 74 | cdev_init(&c.cdev, &ops); 75 | c.cdev.owner = THIS_MODULE; 76 | 77 | /* make device live */ 78 | rc = cdev_add(&c.cdev, c.dev, NUM_MINORS); 79 | if (rc) { 80 | printk(KERN_WARNING "cdev_add: can't add device\n"); 81 | unregister_chrdev_region(c.dev, NUM_MINORS); 82 | cdev_del(&c.cdev); 83 | goto done; 84 | } 85 | 86 | /* create the proc entry */ 87 | p = proc_create(PROC_ENTRY_NAME, 0444, NULL, &procfs_ops); 88 | if (p==NULL) { 89 | printk(KERN_WARNING "create_proc_read_entry: failed\n"); 90 | goto done; 91 | } 92 | c.procfs_buf_len = snprintf(c.procfs_buf, sizeof(c.procfs_buf), 93 | PROC_ENTRY_NAME " dev (%d,%d)\n", MAJOR(c.dev), MINOR(c.dev)); 94 | 95 | rc = 0; 96 | 97 | done: 98 | return rc ? -ENODEV : 0; 99 | } 100 | 101 | void __exit kex_cleanup(void) { 102 | cdev_del(&c.cdev); 103 | unregister_chrdev_region(c.dev, NUM_MINORS); 104 | remove_proc_entry(PROC_ENTRY_NAME, NULL); 105 | } 106 | 107 | module_init(kex_init); 108 | module_exit(kex_cleanup); 109 | -------------------------------------------------------------------------------- /109.inode/Makefile: -------------------------------------------------------------------------------- 1 | # invoke as 2 | # 3 | # make -C /path/to/kernel/source SUBDIRS=$PWD modules 4 | # 5 | # e.g., 6 | # make -C /lib/modules/3.13.0-24-generic/build/ SUBDIRS=$PWD modules 7 | # 8 | 9 | obj-m := kex.o 10 | -------------------------------------------------------------------------------- /109.inode/README.md: -------------------------------------------------------------------------------- 1 | This kex identifies which device inode, on which disk device, opened it. 2 | This distinguishes two devices having the same major and minor from each 3 | other. If they are on the same filesystem, their inodes would differ. If 4 | they are on two different filesystems, their disk device major and minor 5 | would differ (and in all likelihood their inode numbers as well). We can 6 | treat the combination of disk device major and minor and device inode as 7 | a "key" to tell whether two processes opened the same device node. 8 | 9 | ``` 10 | % ./build.sh 11 | % sudo insmod kex.ko 12 | % cat /proc/devices | grep kex 13 | 250 kex 14 | % sudo mknod dev c 250 0 15 | % ls -i dev 16 | 1075684 dev 17 | ``` 18 | 19 | We used ls -i to see the inode number of our device node above. We open 20 | the device, without writing any bytes to it, to provoke its log message: 21 | 22 | ``` 23 | % cat /dev/null > dev 24 | % dmesg 25 | [38890.769944] open(): inode 1075684 on dev (8,1) 26 | ``` 27 | 28 | Notice our driver's log message includes the inode number of our device. 29 | It matches the one we expected. The log also says the device node inode 30 | resides on device (8,1). We can see that the current directory in which 31 | the device node resides is on /dev/sda1 which is device (8,1) as shown: 32 | 33 | ``` 34 | % df . 35 | /dev/sda1 19478204 9067468 9398256 50% / 36 | % ls -l /dev/sda1 37 | brw-rw---- 1 root disk 8, 1 Mar 2 16:54 /dev/sda1 38 | ``` 39 | 40 | Clean up: 41 | 42 | % rm dev 43 | % sudo rmmod kex 44 | % ./build.sh clean 45 | -------------------------------------------------------------------------------- /109.inode/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | KERNELHEADERS=/lib/modules/$(uname -r)/build/ 3 | make -C ${KERNELHEADERS} SUBDIRS=$PWD $1 4 | -------------------------------------------------------------------------------- /109.inode/kex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* module information */ 9 | MODULE_AUTHOR("Troy D. Hanson"); 10 | MODULE_DESCRIPTION("Example of character device"); 11 | MODULE_LICENSE("Dual BSD/GPL"); 12 | 13 | #define NUM_MINORS 1 14 | 15 | struct kex_t { 16 | dev_t dev; /* has major and minor bits */ 17 | struct cdev cdev; /* has our ops, owner, etc */ 18 | } c; 19 | 20 | int _open(struct inode *inode, struct file *f) { 21 | printk(KERN_DEBUG "open(): inode %lu on dev (%u,%u)\n", inode->i_ino, 22 | MAJOR(inode->i_sb->s_dev), MINOR(inode->i_sb->s_dev)); 23 | return 0; 24 | } 25 | 26 | struct file_operations ops = { 27 | .open = _open, 28 | }; 29 | 30 | int __init kex_init(void) { 31 | int rc; 32 | 33 | /* ask for a dynamic major */ 34 | rc = alloc_chrdev_region(&c.dev, 0, NUM_MINORS, "kex"); 35 | if (rc) { 36 | printk(KERN_WARNING "alloc_chrdev_region: failed to get major\n"); 37 | goto done; 38 | } 39 | 40 | /* init the struct cdev */ 41 | cdev_init(&c.cdev, &ops); 42 | c.cdev.owner = THIS_MODULE; 43 | 44 | /* make device live */ 45 | rc = cdev_add(&c.cdev, c.dev, NUM_MINORS); 46 | if (rc) { 47 | printk(KERN_WARNING "cdev_add: can't add device\n"); 48 | unregister_chrdev_region(c.dev, NUM_MINORS); 49 | cdev_del(&c.cdev); 50 | goto done; 51 | } 52 | 53 | rc = 0; 54 | 55 | done: 56 | return rc ? -ENODEV : 0; 57 | } 58 | 59 | void __exit kex_cleanup(void) { 60 | cdev_del(&c.cdev); 61 | unregister_chrdev_region(c.dev, NUM_MINORS); 62 | } 63 | 64 | module_init(kex_init); 65 | module_exit(kex_cleanup); 66 | -------------------------------------------------------------------------------- /110.linked_lists/Makefile: -------------------------------------------------------------------------------- 1 | # invoke as 2 | # 3 | # make -C /path/to/kernel/source SUBDIRS=$PWD modules 4 | # 5 | # e.g., 6 | # make -C /lib/modules/3.13.0-24-generic/build/ SUBDIRS=$PWD modules 7 | # 8 | 9 | obj-m := kex.o 10 | -------------------------------------------------------------------------------- /110.linked_lists/README.md: -------------------------------------------------------------------------------- 1 | There is a standard linked list implementation in the kernel. 2 | 3 | #include 4 | 5 | This header defines the `list_head` structure: 6 | 7 | ``` 8 | struct list_head { 9 | struct list_head *next, *prev; 10 | }; 11 | ``` 12 | 13 | To make a structure into a linked list, embed this structure in it. 14 | Declare a standalone `struct list_head` to serve as the list head. 15 | The kernel defines several list manipulation primitives. A few of 16 | them used in kex.c include `list_add`, `list_for_each`, `list_del` 17 | and `list_for_each_safe`. 18 | 19 | ``` 20 | % ./build.sh 21 | % sudo insmod kex.ko items=5 22 | % dmesg | tail 23 | ``` 24 | 25 | Clean up: 26 | 27 | % sudo rmmod kex 28 | % dmesg | tail 29 | % ./build.sh clean 30 | -------------------------------------------------------------------------------- /110.linked_lists/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | KERNELHEADERS=/lib/modules/$(uname -r)/build/ 3 | make -C ${KERNELHEADERS} SUBDIRS=$PWD $1 4 | -------------------------------------------------------------------------------- /110.linked_lists/kex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | /* module information */ 8 | MODULE_AUTHOR("Troy D. Hanson"); 9 | MODULE_DESCRIPTION("Example of linked list"); 10 | MODULE_LICENSE("Dual BSD/GPL"); 11 | 12 | static int items=0; 13 | #ifdef MODULE 14 | module_param(items, int, 0644); 15 | MODULE_PARM_DESC(items, "number of items in linked list"); 16 | #endif 17 | 18 | struct kex_list_item { 19 | struct list_head list; 20 | int ordinal; 21 | }; 22 | 23 | /* global state. must be thread safe. */ 24 | struct list_head kex_list; 25 | 26 | void free_kex_list(void) { 27 | struct list_head *e, *n; 28 | struct kex_list_item *k; 29 | 30 | printk(KERN_INFO "releasing kex list\n"); 31 | 32 | list_for_each_safe(e, n, &kex_list) { 33 | k = (struct kex_list_item*)list_entry(e, struct kex_list_item, list); 34 | printk(KERN_INFO "free kex list item %d\n", k->ordinal); 35 | list_del(e); 36 | kfree(k); 37 | } 38 | printk(KERN_INFO "list_empty:%d\n", list_empty(&kex_list)); 39 | } 40 | 41 | int __init kex_init(void) { 42 | struct list_head *e; 43 | struct kex_list_item *k; 44 | int i, rc; 45 | 46 | printk(KERN_INFO "module_init: building linked list of %d items\n", items); 47 | 48 | INIT_LIST_HEAD(&kex_list); 49 | 50 | /* add items to our list */ 51 | for(i=0; i < items; i++) { 52 | k = (struct kex_list_item*)kmalloc(sizeof(*k), GFP_KERNEL); 53 | if (k==NULL) goto done; 54 | k->ordinal = i; 55 | list_add(&k->list, &kex_list); 56 | } 57 | 58 | /* iterate over the list */ 59 | printk(KERN_INFO "iterating over list\n"); 60 | list_for_each(e, &kex_list) { 61 | k = (struct kex_list_item*)list_entry(e, struct kex_list_item, list); 62 | printk(KERN_INFO "item %d\n", k->ordinal); 63 | } 64 | 65 | rc = 0; 66 | 67 | done: 68 | if (rc < 0) free_kex_list(); 69 | return rc ? -ENOMEM : 0; 70 | } 71 | 72 | void __exit kex_cleanup(void) { 73 | printk(KERN_INFO "module exit\n"); 74 | free_kex_list(); 75 | } 76 | 77 | module_init(kex_init); 78 | module_exit(kex_cleanup); 79 | -------------------------------------------------------------------------------- /201.gfp/Makefile: -------------------------------------------------------------------------------- 1 | # invoke as 2 | # 3 | # make -C /path/to/kernel/source SUBDIRS=$PWD modules 4 | # 5 | # e.g., 6 | # make -C /lib/modules/3.13.0-24-generic/build/ SUBDIRS=$PWD modules 7 | # 8 | 9 | obj-m := kex.o 10 | -------------------------------------------------------------------------------- /201.gfp/README.md: -------------------------------------------------------------------------------- 1 | This version of the kex module uses get_free_page and free_page. 2 | It associates one page with each opened descriptor on the device. 3 | When a program writes to the descriptor, kex copies the data into 4 | the page, up to PAGE_SIZE bytes. When the program reads from the 5 | descriptor, kex copies those bytes back out to it. 6 | 7 | ``` 8 | % ./build.sh 9 | % sudo insmod kex.ko 10 | % cat /proc/devices | grep kex 11 | 250 kex 12 | % sudo mknod dev c 250 0 13 | % sudo chmod a+rw dev 14 | % ./rw.py 15 | ``` 16 | 17 | In this driver we could have used kmalloc and kfree instead. With 18 | get_free_page we get page-sized allocations; in contrast, kmalloc 19 | is used for byte-sized (or physically contiguous) allocations. 20 | 21 | Clean up: 22 | 23 | % rm dev 24 | % sudo rmmod kex 25 | % ./build.sh clean 26 | -------------------------------------------------------------------------------- /201.gfp/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | KERNELHEADERS=/lib/modules/$(uname -r)/build/ 3 | make -C ${KERNELHEADERS} SUBDIRS=$PWD $1 4 | -------------------------------------------------------------------------------- /201.gfp/kex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /* module information */ 11 | MODULE_AUTHOR("Troy D. Hanson"); 12 | MODULE_DESCRIPTION("Example of character device"); 13 | MODULE_LICENSE("Dual BSD/GPL"); 14 | 15 | #define NUM_MINORS 1 16 | 17 | /* a global to keep state. must be thread safe. */ 18 | struct chardev_t { 19 | dev_t dev; /* has major and minor bits */ 20 | struct cdev cdev; /* has our ops, owner, etc */ 21 | } c; 22 | 23 | int _open(struct inode *inode, struct file *f) { 24 | unsigned long page = __get_free_page(GFP_KERNEL); 25 | if (!page) return -ENOMEM; 26 | printk(KERN_DEBUG " page virt addr %p\n", (void*)page); 27 | f->private_data = (void*)page; 28 | return 0; 29 | } 30 | 31 | int _release(struct inode *inode, struct file *f) { 32 | unsigned long page = (unsigned long)f->private_data; 33 | free_page(page); 34 | return 0; 35 | } 36 | 37 | ssize_t _read(struct file *f, char __user *buf, size_t count, loff_t *offp) { 38 | unsigned long page = (unsigned long)f->private_data; 39 | size_t sz = (count > PAGE_SIZE) ? PAGE_SIZE : count; 40 | if (copy_to_user(buf, (void*)page, sz)) return -EFAULT; 41 | *offp += sz; 42 | return sz; 43 | } 44 | 45 | ssize_t _write(struct file *f, const char __user *buf, size_t count, 46 | loff_t *offp) { 47 | unsigned long page = (unsigned long)f->private_data; 48 | size_t sz = (count > PAGE_SIZE) ? PAGE_SIZE : count; 49 | if (copy_from_user((void*)page, buf, sz)) return -EFAULT; 50 | *offp += sz; 51 | return sz; 52 | } 53 | 54 | struct file_operations ops = { 55 | .read = _read, 56 | .write = _write, 57 | .unlocked_ioctl = NULL, 58 | .open = _open, 59 | .release = _release 60 | }; 61 | 62 | int __init chardev_init(void) { 63 | int rc; 64 | 65 | /* ask for a dynamic major */ 66 | rc = alloc_chrdev_region(&c.dev, 0, NUM_MINORS, "kex"); 67 | if (rc) { 68 | printk(KERN_WARNING "alloc_chrdev_region: failed to get major\n"); 69 | goto done; 70 | } 71 | 72 | /* init the struct cdev */ 73 | cdev_init(&c.cdev, &ops); 74 | c.cdev.owner = THIS_MODULE; 75 | 76 | /* make device live */ 77 | rc = cdev_add(&c.cdev, c.dev, NUM_MINORS); 78 | if (rc) { 79 | printk(KERN_WARNING "cdev_add: can't add device\n"); 80 | unregister_chrdev_region(c.dev, NUM_MINORS); 81 | cdev_del(&c.cdev); 82 | goto done; 83 | } 84 | 85 | rc = 0; 86 | 87 | done: 88 | return rc ? -ENODEV : 0; 89 | } 90 | 91 | void __exit chardev_cleanup(void) { 92 | cdev_del(&c.cdev); 93 | unregister_chrdev_region(c.dev, NUM_MINORS); 94 | } 95 | 96 | module_init(chardev_init); 97 | module_exit(chardev_cleanup); 98 | -------------------------------------------------------------------------------- /201.gfp/rw.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import os,struct 3 | 4 | dev = os.open("dev",os.O_RDWR) 5 | os.write(dev, "hello") 6 | back = os.read(dev,5) 7 | print back 8 | -------------------------------------------------------------------------------- /202.mutex/Makefile: -------------------------------------------------------------------------------- 1 | # invoke as 2 | # 3 | # make -C /path/to/kernel/source SUBDIRS=$PWD modules 4 | # 5 | # e.g., 6 | # make -C /lib/modules/3.13.0-24-generic/build/ SUBDIRS=$PWD modules 7 | # 8 | 9 | obj-m := kex.o 10 | -------------------------------------------------------------------------------- /202.mutex/README.md: -------------------------------------------------------------------------------- 1 | This is a more fun variation of the previous kex. This kex keeps the 2 | page inside the driver global state. In other words, one process 3 | can write to it, and another process can read the buffer out. 4 | 5 | Since multiple processes may read or write the device at once, 6 | this kex adds a mutex to lock the page buffer. 7 | 8 | ``` 9 | % ./build.sh 10 | % sudo insmod kex.ko 11 | % cat /proc/devices | grep kex 12 | 250 kex 13 | % sudo mknod dev c 250 0 14 | % sudo chmod a+rw dev 15 | % echo hello > dev 16 | % cat dev 17 | hello 18 | ``` 19 | You can write up to a full page (PAGE_SIZE is 4k on many systems) in 20 | a single write operation; kex buffers that in its page. 21 | 22 | ``` 23 | % dd if=/dev/urandom of=dev bs=4096 count=1 24 | % wc -c dev 25 | 4096 dev 26 | ``` 27 | 28 | If we try to write more than a page worth of data, dd (the writing 29 | process) has to loop to complete writing the full buffer. If we write 30 | 4097 bytes, dd has to make a second write for the final byte. 31 | 32 | ``` 33 | % dd if=/dev/urandom of=dev bs=4097 count=1 34 | % wc -c dev 35 | 1 dev 36 | ``` 37 | 38 | Here we see that our driver only uses the last write as its measure 39 | of how much data is available to read. 40 | 41 | Clean up: 42 | 43 | % rm dev 44 | % sudo rmmod kex 45 | % ./build.sh clean 46 | -------------------------------------------------------------------------------- /202.mutex/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | KERNELHEADERS=/lib/modules/$(uname -r)/build/ 3 | make -C ${KERNELHEADERS} SUBDIRS=$PWD $1 4 | -------------------------------------------------------------------------------- /202.mutex/kex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /* module information */ 11 | MODULE_AUTHOR("Troy D. Hanson"); 12 | MODULE_DESCRIPTION("Example of character device"); 13 | MODULE_LICENSE("Dual BSD/GPL"); 14 | 15 | #define NUM_MINORS 1 16 | 17 | DEFINE_MUTEX(mutex); 18 | 19 | /* a global to keep state. must be thread safe. */ 20 | struct chardev_t { 21 | dev_t dev; /* has major and minor bits */ 22 | struct cdev cdev; /* has our ops, owner, etc */ 23 | 24 | /* we have a single buffer shared by all users */ 25 | unsigned long page; // virtual address of page 26 | size_t bytes_available; // bytes available to read 27 | } c; 28 | 29 | int _open(struct inode *inode, struct file *f) { 30 | return 0; 31 | } 32 | 33 | int _release(struct inode *inode, struct file *f) { 34 | return 0; 35 | } 36 | 37 | ssize_t _read(struct file *f, char __user *buf, size_t count, loff_t *offp) { 38 | ssize_t rc = -EFAULT, sz; 39 | unsigned long mc; 40 | 41 | /* in linux you are allowed to sleep while holding a mutex. 42 | copy_to_user may sleep if the page needs to be brought in */ 43 | mutex_lock(&mutex); 44 | sz = (count > c.bytes_available) ? c.bytes_available : count; 45 | mc = copy_to_user(buf, (void*)c.page, sz); 46 | if (mc == 0) c.bytes_available -= sz; 47 | mutex_unlock(&mutex); 48 | 49 | if (mc) goto done; 50 | 51 | *offp += sz; 52 | rc = sz; 53 | 54 | done: 55 | return rc; 56 | } 57 | 58 | ssize_t _write(struct file *f, const char __user *buf, size_t count, 59 | loff_t *offp) { 60 | ssize_t rc = -EFAULT, sz; 61 | unsigned long mc; 62 | 63 | mutex_lock(&mutex); 64 | sz = (count > PAGE_SIZE) ? PAGE_SIZE : count; 65 | mc = copy_from_user((void*)c.page, buf, sz); 66 | if (mc == 0) c.bytes_available = sz; 67 | mutex_unlock(&mutex); 68 | 69 | if (mc) goto done; 70 | 71 | *offp += sz; 72 | rc = sz; 73 | 74 | done: 75 | return rc; 76 | } 77 | 78 | struct file_operations ops = { 79 | .read = _read, 80 | .write = _write, 81 | .unlocked_ioctl = NULL, 82 | .open = _open, 83 | .release = _release 84 | }; 85 | 86 | int __init chardev_init(void) { 87 | int rc; 88 | 89 | /* get the page that is our shared buffer */ 90 | c.page = __get_free_page(GFP_KERNEL); 91 | if (!c.page) {rc = -ENOMEM; goto done; } 92 | printk(KERN_DEBUG " page virt addr %p\n", (void*)c.page); 93 | 94 | /* ask for a dynamic major */ 95 | rc = alloc_chrdev_region(&c.dev, 0, NUM_MINORS, "kex"); 96 | if (rc) { rc = -ENODEV; free_page(c.page); goto done; } 97 | 98 | /* init the struct cdev */ 99 | cdev_init(&c.cdev, &ops); 100 | c.cdev.owner = THIS_MODULE; 101 | 102 | /* make device live */ 103 | rc = cdev_add(&c.cdev, c.dev, NUM_MINORS); 104 | if (rc) { 105 | printk(KERN_WARNING "cdev_add: can't add device\n"); 106 | unregister_chrdev_region(c.dev, NUM_MINORS); 107 | cdev_del(&c.cdev); 108 | free_page(c.page); 109 | rc = -ENODEV; 110 | goto done; 111 | } 112 | 113 | rc = 0; 114 | 115 | done: 116 | return rc; 117 | } 118 | 119 | void __exit chardev_cleanup(void) { 120 | free_page(c.page); 121 | cdev_del(&c.cdev); 122 | unregister_chrdev_region(c.dev, NUM_MINORS); 123 | } 124 | 125 | module_init(chardev_init); 126 | module_exit(chardev_cleanup); 127 | -------------------------------------------------------------------------------- /203.alloc_page/Makefile: -------------------------------------------------------------------------------- 1 | # invoke as 2 | # 3 | # make -C /path/to/kernel/source SUBDIRS=$PWD modules 4 | # 5 | # e.g., 6 | # make -C /lib/modules/3.13.0-24-generic/build/ SUBDIRS=$PWD modules 7 | # 8 | 9 | obj-m := kex.o 10 | -------------------------------------------------------------------------------- /203.alloc_page/README.md: -------------------------------------------------------------------------------- 1 | This kex behaves the same as the previous one. Internally it uses alloc_page 2 | instead of get_free_page. This returns to us a struct page pointer. Then kex 3 | gets its kernel virtual address using page_address(). 4 | 5 | ``` 6 | % ./build.sh 7 | % sudo insmod kex.ko 8 | % cat /proc/devices | grep kex 9 | 250 kex 10 | % sudo mknod dev c 250 0 11 | % sudo chmod a+rw dev 12 | % dd if=/dev/urandom of=dev bs=4096 count=1 13 | % wc -c dev 14 | 4096 dev 15 | ``` 16 | 17 | Clean up: 18 | 19 | % rm dev 20 | % sudo rmmod kex 21 | % ./build.sh clean 22 | -------------------------------------------------------------------------------- /203.alloc_page/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | KERNELHEADERS=/lib/modules/$(uname -r)/build/ 3 | make -C ${KERNELHEADERS} SUBDIRS=$PWD $1 4 | -------------------------------------------------------------------------------- /203.alloc_page/kex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /* module information */ 11 | MODULE_AUTHOR("Troy D. Hanson"); 12 | MODULE_DESCRIPTION("Example of character device"); 13 | MODULE_LICENSE("Dual BSD/GPL"); 14 | 15 | #define NUM_MINORS 1 16 | 17 | DEFINE_MUTEX(mutex); 18 | 19 | /* a global to keep state. must be thread safe. */ 20 | struct chardev_t { 21 | dev_t dev; /* has major and minor bits */ 22 | struct cdev cdev; /* has our ops, owner, etc */ 23 | 24 | /* we have a single buffer shared by all users */ 25 | struct page *page; // alloc_page gets this 26 | void *addr; // vaddr of page 27 | size_t bytes_available; // bytes available to read 28 | } c; 29 | 30 | int _open(struct inode *inode, struct file *f) { 31 | return 0; 32 | } 33 | 34 | int _release(struct inode *inode, struct file *f) { 35 | return 0; 36 | } 37 | 38 | ssize_t _read(struct file *f, char __user *buf, size_t count, loff_t *offp) { 39 | ssize_t rc = -EFAULT, sz; 40 | unsigned long mc; 41 | 42 | /* in linux you are allowed to sleep while holding a mutex. 43 | copy_to_user may sleep if the page needs to be brought in */ 44 | mutex_lock(&mutex); 45 | sz = (count > c.bytes_available) ? c.bytes_available : count; 46 | mc = copy_to_user(buf, c.addr, sz); 47 | if (mc == 0) c.bytes_available -= sz; 48 | mutex_unlock(&mutex); 49 | 50 | if (mc) goto done; 51 | 52 | *offp += sz; 53 | rc = sz; 54 | 55 | done: 56 | return rc; 57 | } 58 | 59 | ssize_t _write(struct file *f, const char __user *buf, size_t count, 60 | loff_t *offp) { 61 | ssize_t rc = -EFAULT, sz; 62 | unsigned long mc; 63 | 64 | mutex_lock(&mutex); 65 | sz = (count > PAGE_SIZE) ? PAGE_SIZE : count; 66 | mc = copy_from_user(c.addr, buf, sz); 67 | if (mc == 0) c.bytes_available = sz; 68 | mutex_unlock(&mutex); 69 | 70 | if (mc) goto done; 71 | 72 | *offp += sz; 73 | rc = sz; 74 | 75 | done: 76 | return rc; 77 | } 78 | 79 | struct file_operations ops = { 80 | .read = _read, 81 | .write = _write, 82 | .unlocked_ioctl = NULL, 83 | .open = _open, 84 | .release = _release 85 | }; 86 | 87 | int __init chardev_init(void) { 88 | int rc; 89 | 90 | /* get the page that is our shared buffer */ 91 | c.page = alloc_page(GFP_KERNEL); 92 | if (!c.page) {rc = -ENOMEM; goto done; } 93 | c.addr = page_address(c.page); 94 | if (!c.addr) { 95 | rc = -ENOMEM; 96 | printk(KERN_DEBUG "page_address failed\n"); 97 | __free_page(c.page); 98 | goto done; 99 | } 100 | printk(KERN_DEBUG "page virt addr %p\n", c.addr); 101 | 102 | /* ask for a dynamic major */ 103 | rc = alloc_chrdev_region(&c.dev, 0, NUM_MINORS, "kex"); 104 | if (rc) { 105 | rc = -ENODEV; 106 | __free_page(c.page); 107 | goto done; 108 | } 109 | 110 | /* init the struct cdev */ 111 | cdev_init(&c.cdev, &ops); 112 | c.cdev.owner = THIS_MODULE; 113 | 114 | /* make device live */ 115 | rc = cdev_add(&c.cdev, c.dev, NUM_MINORS); 116 | if (rc) { 117 | rc = -ENODEV; 118 | printk(KERN_WARNING "cdev_add: can't add device\n"); 119 | unregister_chrdev_region(c.dev, NUM_MINORS); 120 | cdev_del(&c.cdev); 121 | __free_page(c.page); 122 | goto done; 123 | } 124 | 125 | rc = 0; 126 | 127 | done: 128 | return rc; 129 | } 130 | 131 | void __exit chardev_cleanup(void) { 132 | __free_page(c.page); 133 | cdev_del(&c.cdev); 134 | unregister_chrdev_region(c.dev, NUM_MINORS); 135 | } 136 | 137 | module_init(chardev_init); 138 | module_exit(chardev_cleanup); 139 | -------------------------------------------------------------------------------- /204.mmap/Makefile: -------------------------------------------------------------------------------- 1 | # invoke as 2 | # 3 | # make -C /path/to/kernel/source SUBDIRS=$PWD modules 4 | # 5 | # e.g., 6 | # make -C /lib/modules/3.13.0-24-generic/build/ SUBDIRS=$PWD modules 7 | # 8 | 9 | obj-m := kex.o 10 | 11 | rig: rig.c 12 | 13 | clean: 14 | rm -f rig 15 | -------------------------------------------------------------------------------- /204.mmap/README.md: -------------------------------------------------------------------------------- 1 | This kex module implements mmap and includes a test rig to exercise it. 2 | When the test rig invokes mmap on the device, kex uses alloc_page 3 | to get as many pages as the rig requested. It uses vm_insert_page on 4 | each page to map it into rig's address space. These pages, while 5 | physically disjoint, appear as a single contiguous virtual memory area 6 | (VMA) inside the rig address space. The VMA is visible using pmap: 7 | 8 | ``` 9 | % ./build.sh 10 | % sudo insmod kex.ko 11 | % cat /proc/devices | grep kex 12 | 250 kex 13 | % sudo mknod dev c 250 0 14 | % sudo chmod a+rw dev 15 | % ./rig dev 1 16 | pid: 10195 17 | press a key to exercise memory write 18 | ``` 19 | 20 | Leave the rig waiting. In another terminal run pmap. See the dev VMA: 21 | 22 | ``` 23 | % pmap -x 10195 24 | Address Kbytes RSS Dirty Mode Mapping 25 | 00007fd0125b6000 4 4 0 rw-s- dev 26 | ``` 27 | 28 | It's 4kb because rig asked for one page (the final argument), which is 4kb. 29 | Now we can ask rig to write to this memory. In the original terminal, press 30 | enter. Then re-run pmap in the second terminal: 31 | 32 | ``` 33 | Address Kbytes RSS Dirty Mode Mapping 34 | 00007fd0125b6000 4 4 4 rw-s- dev 35 | ``` 36 | 37 | Notice that the Dirty column now shows the mapped page is Dirty. (TODO: is 38 | this an MMU flag set on write to the page?). Press enter to terminate rig. 39 | 40 | Clean up: 41 | 42 | % rm dev 43 | % sudo rmmod kex 44 | % ./build.sh clean 45 | -------------------------------------------------------------------------------- /204.mmap/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | KERNELHEADERS=/lib/modules/$(uname -r)/build/ 3 | make -C ${KERNELHEADERS} SUBDIRS=$PWD $1 4 | 5 | if [ "$1" == "clean" ] 6 | then 7 | make clean 8 | else 9 | make rig 10 | fi 11 | -------------------------------------------------------------------------------- /204.mmap/kex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /* module information */ 11 | MODULE_AUTHOR("Troy D. Hanson"); 12 | MODULE_DESCRIPTION("Example of character device"); 13 | MODULE_LICENSE("Dual BSD/GPL"); 14 | 15 | #define NUM_MINORS 1 16 | 17 | /* a global to keep state. must be thread safe. */ 18 | struct chardev_t { 19 | dev_t dev; /* has major and minor bits */ 20 | struct cdev cdev; /* has our ops, owner, etc */ 21 | } c; 22 | 23 | int _mmap(struct file *f, struct vm_area_struct *vma) { 24 | unsigned long pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; 25 | unsigned long addr; 26 | struct page *page; 27 | int i,rc=-ENODEV; 28 | // TODO need any semaphore for vma manipulation? 29 | printk(KERN_DEBUG "vma->vm_end %lu vm_start %lu len %lu pages %lu vm_pgoff %lu\n", 30 | vma->vm_end, vma->vm_start, vma->vm_end - vma->vm_start, pages, vma->vm_pgoff); 31 | /* allocate and insert pages to fill the vma. */ 32 | for(i=0; i < pages; i++) { 33 | page = alloc_page(GFP_KERNEL); // TODO IO RESERVE? 34 | if (!page) { 35 | // TODO free previous pages 36 | printk(KERN_DEBUG "alloc_page failed\n"); 37 | goto done; 38 | } 39 | addr = vma->vm_start+i*PAGE_SIZE; 40 | if (vm_insert_page(vma,addr,page) < 0) { 41 | // TODO free previous pages 42 | printk(KERN_DEBUG "vm_insert_page failed\n"); 43 | goto done; 44 | } 45 | printk(KERN_DEBUG "inserted page %d at %p\n",i,(void*)addr); 46 | // TODO __free_page now, should be ok, since vm_insert_page incremented its 47 | // refcount. that way, upon munmap, refcount hits zer0, pages get freed 48 | __free_page(page); 49 | } 50 | printk(KERN_DEBUG "completed inserting %lu pages\n", pages); 51 | 52 | rc = 0; 53 | 54 | done: 55 | return rc; 56 | } 57 | 58 | struct file_operations ops = { 59 | .mmap = _mmap 60 | }; 61 | 62 | int __init chardev_init(void) { 63 | int rc; 64 | 65 | /* ask for a dynamic major */ 66 | rc = alloc_chrdev_region(&c.dev, 0, NUM_MINORS, "kex"); 67 | if (rc) { 68 | rc = -ENODEV; 69 | goto done; 70 | } 71 | 72 | /* init the struct cdev */ 73 | cdev_init(&c.cdev, &ops); 74 | c.cdev.owner = THIS_MODULE; 75 | 76 | /* make device live */ 77 | rc = cdev_add(&c.cdev, c.dev, NUM_MINORS); 78 | if (rc) { 79 | rc = -ENODEV; 80 | printk(KERN_WARNING "cdev_add: can't add device\n"); 81 | unregister_chrdev_region(c.dev, NUM_MINORS); 82 | cdev_del(&c.cdev); 83 | goto done; 84 | } 85 | 86 | rc = 0; 87 | 88 | done: 89 | return rc; 90 | } 91 | 92 | void __exit chardev_cleanup(void) { 93 | cdev_del(&c.cdev); 94 | unregister_chrdev_region(c.dev, NUM_MINORS); 95 | } 96 | 97 | module_init(chardev_init); 98 | module_exit(chardev_cleanup); 99 | -------------------------------------------------------------------------------- /204.mmap/rig.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | int main(int argc, char * argv[]) { 13 | char *file = (argc > 1) ? argv[1] : "dev"; 14 | int npages = (argc > 2) ? atoi(argv[2]) : 1; 15 | int fd,rc=-1; 16 | char *buf, *b, unused; 17 | size_t len = 4096 * npages; // FIXME getpagesz 18 | 19 | fprintf(stderr,"pid: %u\n", (int)getpid()); 20 | 21 | if ( (fd = open(file, O_RDWR)) == -1) { 22 | fprintf(stderr,"can't open %s: %s\n", file, strerror(errno)); 23 | goto done; 24 | } 25 | 26 | buf = mmap(0, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 27 | if (buf == MAP_FAILED) { 28 | fprintf(stderr, "failed to mmap %s: %s\n", file, strerror(errno)); 29 | goto done; 30 | } 31 | 32 | rc = 0; 33 | 34 | /* make the program block so we can examine it */ 35 | 36 | fprintf(stderr,"press a key to exercise memory write\n"); 37 | read(STDIN_FILENO,&unused,sizeof(unused)); 38 | for(b=buf; b < buf+len; b++) *b=1; 39 | 40 | fprintf(stderr,"press a key to terminate\n"); 41 | read(STDIN_FILENO,&unused,sizeof(unused)); 42 | 43 | 44 | done: 45 | munmap(buf, len); 46 | close(fd); 47 | return rc; 48 | } 49 | -------------------------------------------------------------------------------- /205.fault/Makefile: -------------------------------------------------------------------------------- 1 | # invoke as 2 | # 3 | # make -C /path/to/kernel/source SUBDIRS=$PWD modules 4 | # 5 | # e.g., 6 | # make -C /lib/modules/3.13.0-24-generic/build/ SUBDIRS=$PWD modules 7 | # 8 | 9 | obj-m := kex.o 10 | 11 | rig: rig.c 12 | 13 | clean: 14 | rm -f rig 15 | -------------------------------------------------------------------------------- /205.fault/README.md: -------------------------------------------------------------------------------- 1 | This version of kex introduces one of the most interesting features of 2 | memory management - page faulting. 3 | 4 | When the test rigs mmaps the device, this version of kex leaves the 5 | VMA's unpopulated. In other words, the rig has pages mapped into its 6 | address space, for which no RAM backing exists. 7 | 8 | When the rig process tries to read or write one of these pages, the MMU 9 | looks up the corresponding physical RAM page in the page tables. Seeing 10 | none it interrupts the kernel to decide how to handle the fault. In the 11 | VMA kex has registered a fault handler, so the kernel calls it. Now kex 12 | allocates a page of RAM to satisfy the fault. The page fault, an on- 13 | demand VMA manipulation, is invisible to the userspace application. 14 | 15 | ``` 16 | % ./build.sh 17 | % sudo insmod kex.ko 18 | % cat /proc/devices | grep kex 19 | 250 kex 20 | % sudo mknod dev c 250 0 21 | % sudo chmod a+rw dev 22 | % ./rig dev 1 23 | pid: 24995 24 | press a key to exercise memory write 25 | ``` 26 | 27 | While rig waits, use another terminal to examine its memory map. 28 | Notice RSS is 0 on the page mapped to the dev device- it's not in RAM: 29 | 30 | ``` 31 | % pmap -x 24995 32 | Address Kbytes RSS Dirty Mode Mapping 33 | 00007fdd3fff0000 4 0 0 rw-s- dev 34 | ``` 35 | 36 | In the rig window, press enter to cause it to write to this page. 37 | The page fault occurs as dmesg confirms. The page is now resident. 38 | 39 | ``` 40 | % pmap -x 24995 41 | Address Kbytes RSS Dirty Mode Mapping 42 | 00007fdd3fff0000 4 4 4 rw-s- dev 43 | ``` 44 | 45 | We could also use ps to see the count of minor page faults increase 46 | when rig accesses the page for the first time using a command like: 47 | 48 | ``` 49 | % ps -o pid,maj_flt,min_flt -C rig 50 | PID MAJFL MINFL 51 | 24995 0 195 52 | ``` 53 | 54 | Clean up: 55 | 56 | % rm dev 57 | % sudo rmmod kex 58 | % ./build.sh clean 59 | -------------------------------------------------------------------------------- /205.fault/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | KERNELHEADERS=/lib/modules/$(uname -r)/build/ 3 | make -C ${KERNELHEADERS} SUBDIRS=$PWD $1 4 | 5 | if [ "$1" == "clean" ] 6 | then 7 | make clean 8 | else 9 | make rig 10 | fi 11 | -------------------------------------------------------------------------------- /205.fault/kex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /* module information */ 11 | MODULE_AUTHOR("Troy D. Hanson"); 12 | MODULE_DESCRIPTION("Example of character device"); 13 | MODULE_LICENSE("Dual BSD/GPL"); 14 | 15 | #define NUM_MINORS 1 16 | 17 | struct chardev_t { 18 | dev_t dev; /* has major and minor bits */ 19 | struct cdev cdev; /* has our ops, owner, etc */ 20 | } c; 21 | 22 | int kex_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { 23 | int flags = 0; /* VM_FAULT_MINOR. see mm.h */ 24 | 25 | printk(KERN_DEBUG "fault on %p (userspace:%d write:%d)\n", 26 | (void*)vmf->virtual_address, 27 | (vmf->flags & FAULT_FLAG_USER) ? 1:0, 28 | (vmf->flags & FAULT_FLAG_WRITE) ? 1:0); 29 | 30 | vmf->page = alloc_page(GFP_KERNEL); 31 | if (!vmf->page) { 32 | flags = VM_FAULT_OOM; 33 | goto done; 34 | } 35 | 36 | // XXX 37 | //get_page() not needed; -- alloc_page already took a reference 38 | //@unmap does __free_page - that should drop refcount to 0 39 | 40 | done: 41 | return flags; // VM_FAULT_MINOR 42 | } 43 | 44 | /* see linux/include/linux/mm.h - this struct has changed since LDD3 p.427 */ 45 | static struct vm_operations_struct vm_ops = { 46 | // .open = kex_open, 47 | // .close = kex_close, 48 | .fault = kex_fault, 49 | }; 50 | 51 | int _mmap(struct file *f, struct vm_area_struct *vma) { 52 | unsigned long pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; 53 | int rc=-ENODEV; 54 | printk(KERN_DEBUG "vma->vm_end %lu vm_start %lu len %lu pages %lu vm_pgoff %lu\n", 55 | vma->vm_end, vma->vm_start, vma->vm_end - vma->vm_start, pages, vma->vm_pgoff); 56 | 57 | vma->vm_ops = &vm_ops; 58 | vma->vm_private_data = (void*)0xa1fa1fa; /* can store private data here */ 59 | 60 | rc = 0; 61 | 62 | return rc; 63 | } 64 | 65 | struct file_operations ops = { 66 | .mmap = _mmap 67 | }; 68 | 69 | int __init chardev_init(void) { 70 | int rc; 71 | 72 | /* ask for a dynamic major */ 73 | rc = alloc_chrdev_region(&c.dev, 0, NUM_MINORS, "kex"); 74 | if (rc) { 75 | rc = -ENODEV; 76 | goto done; 77 | } 78 | 79 | /* init the struct cdev */ 80 | cdev_init(&c.cdev, &ops); 81 | c.cdev.owner = THIS_MODULE; 82 | 83 | /* make device live */ 84 | rc = cdev_add(&c.cdev, c.dev, NUM_MINORS); 85 | if (rc) { 86 | rc = -ENODEV; 87 | printk(KERN_WARNING "cdev_add: can't add device\n"); 88 | unregister_chrdev_region(c.dev, NUM_MINORS); 89 | cdev_del(&c.cdev); 90 | goto done; 91 | } 92 | 93 | rc = 0; 94 | 95 | done: 96 | return rc; 97 | } 98 | 99 | void __exit chardev_cleanup(void) { 100 | cdev_del(&c.cdev); 101 | unregister_chrdev_region(c.dev, NUM_MINORS); 102 | } 103 | 104 | module_init(chardev_init); 105 | module_exit(chardev_cleanup); 106 | -------------------------------------------------------------------------------- /205.fault/rig.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | int main(int argc, char * argv[]) { 13 | char *file = (argc > 1) ? argv[1] : "dev"; 14 | int npages = (argc > 2) ? atoi(argv[2]) : 1; 15 | int fd,rc=-1; 16 | char *buf, *b, unused; 17 | size_t len = 4096 * npages; // FIXME getpagesz 18 | 19 | fprintf(stderr,"pid: %u\n", (int)getpid()); 20 | 21 | if ( (fd = open(file, O_RDWR)) == -1) { 22 | fprintf(stderr,"can't open %s: %s\n", file, strerror(errno)); 23 | goto done; 24 | } 25 | 26 | buf = mmap(0, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 27 | if (buf == MAP_FAILED) { 28 | fprintf(stderr, "failed to mmap %s: %s\n", file, strerror(errno)); 29 | goto done; 30 | } 31 | 32 | rc = 0; 33 | 34 | /* make the program block so we can examine it */ 35 | 36 | fprintf(stderr,"press a key to exercise memory write\n"); 37 | read(STDIN_FILENO,&unused,sizeof(unused)); 38 | for(b=buf; b < buf+len; b++) *b=1; 39 | 40 | fprintf(stderr,"press a key to terminate\n"); 41 | read(STDIN_FILENO,&unused,sizeof(unused)); 42 | 43 | 44 | done: 45 | munmap(buf, len); 46 | close(fd); 47 | return rc; 48 | } 49 | -------------------------------------------------------------------------------- /301.HZ/Makefile: -------------------------------------------------------------------------------- 1 | # invoke as 2 | # 3 | # make -C /path/to/kernel/source SUBDIRS=$PWD modules 4 | # 5 | # e.g., 6 | # make -C /lib/modules/3.13.0-24-generic/build/ SUBDIRS=$PWD modules 7 | # 8 | 9 | obj-m := kex.o 10 | -------------------------------------------------------------------------------- /301.HZ/README.md: -------------------------------------------------------------------------------- 1 | The kernel measures time in jiffies. This is a 32-bit counter that gets 2 | incremented every time the kernel gets an timer interrupt. At boot up the 3 | kernel configures the frequency of the timer interrupt to the #define HZ. 4 | 5 | ``` 6 | % ./build.sh 7 | % sudo insmod kex 8 | % dmesg | grep HZ 9 | [82477.886790] HZ is 250 10 | ``` 11 | 12 | On newer kernels a tickless mode is supported (Dyn Ticks or NO_HZ). In 13 | this mode the kernel increments jiffies by an appropriate amount only 14 | when other events generate interrupts. This is a power saving feature. 15 | 16 | -------------------------------------------------------------------------------- /301.HZ/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | KERNELHEADERS=/lib/modules/$(uname -r)/build/ 3 | make -C ${KERNELHEADERS} SUBDIRS=$PWD $1 4 | -------------------------------------------------------------------------------- /301.HZ/kex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* module information */ 6 | MODULE_AUTHOR("Troy D. Hanson"); 7 | MODULE_DESCRIPTION("Example of module information"); 8 | MODULE_LICENSE("Dual BSD/GPL"); 9 | 10 | 11 | /* __init and __exit make it work as a static driver too */ 12 | int __init kex_init(void) { 13 | printk("kex module init called\n"); 14 | printk("HZ is %u\n", HZ); 15 | printk("jiffies is %lu\n", jiffies); 16 | printk("jiffies_64 is %lu\n", (unsigned long)jiffies_64); 17 | return 0; 18 | } 19 | 20 | void __exit kex_cleanup(void) { 21 | printk("kex module exit called\n"); 22 | } 23 | 24 | /* module_init and module_exit make it work as a loadable module */ 25 | module_init(kex_init); 26 | module_exit(kex_cleanup); 27 | -------------------------------------------------------------------------------- /302.waitqueue/README.md: -------------------------------------------------------------------------------- 1 | This version of kex uses a wait queue to implement a blocking read. 2 | A wait queue is the mechanism by which a process is moved from the 3 | runnable state to the sleeping state. When kex calls wait_event the 4 | current process becomes ineligible for scheduling. The three things 5 | that can happen from this point are: the wait queue becomes awoken 6 | by an external event that issues a wake up (often from an interrupt 7 | handler), or a time out expires, or the process receives a signal. 8 | In any of those cases, the kex driver code returns from wait_event. 9 | 10 | * parameter to set the timeout 11 | * try it with a signal 12 | * use another process write to wake up the wait queue 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Kernel module examples 2 | 3 | These examples demonstrate kernel programming using a loadable module. Each 4 | version of the module shows how to use a different feature of the kernel. 5 | 6 | Kernel sources: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 7 | 8 | TODO 9 | 10 | * IRQ's 11 | * timers 12 | * remap_pfn_range 13 | * get_user_pages 14 | * udev device instantiation 15 | * GPL_EXPORT_SYMBOLS, /proc/kallsyms 16 | * ops: poll, mmap 17 | * kernel kfifo 18 | * pci, usb 19 | * kgdb 20 | * initramfs 21 | * kvm 22 | * net nic 23 | * waitqueues 24 | * memory barrier 25 | * sysfs 26 | --------------------------------------------------------------------------------