Linux Kernel Modules and Kernel IO CMSC 421

  • Slides: 16
Download presentation
Linux Kernel Modules and Kernel I/O CMSC 421 – Section 02 March 24, 2020

Linux Kernel Modules and Kernel I/O CMSC 421 – Section 02 March 24, 2020 Adapted from slides from Professor Jason Tang (UMBC)

Linux Virtual Filesystem The Linux Virtual Filesystem allows for a variety of non-file objects

Linux Virtual Filesystem The Linux Virtual Filesystem allows for a variety of non-file objects to be set up in the filesystem Memory-backed files, such as those in /tmp (via tmpfs) and shared memory files (via shmfs) are two examples of this Devices also have a presence in the filesystem on /dev, through devfs Virtual files generated by the kernel such as those in /proc (procfs), /sys (sysfs), and /sys/debug (debugfs) are also available

Device Filesystem The /dev filesystem is where one can access various attached devices through

Device Filesystem The /dev filesystem is where one can access various attached devices through their device driers Accessing a given file within the /dev filesystem will match the kernel driver of the file and pass off access to the appropriate module (or part of the kernel code) Examples: /dev/mem, /dev/zero call into the memory driver of the kernel /dev/random, /dev/urandom call into the kernel’s random driver Devices can be read-only, write-only, or read-write Hotplug devices are typically represented in /sys, not in /dev (but not always)

Device Classes There a variety of different types of device classes available in the

Device Classes There a variety of different types of device classes available in the Linux kernel Character devices are those that are accessed through a stream of bytes /dev/console, /dev/random, /dev/tty. S 2 Block devices are those that are accessed as a set of blocks, where reads and writes take place in block-sized increments (often 512 bytes at a time) /dev/sda, /dev/sr 0, /dev/cdrom Network devices are those that perform I/O over a network interface These do not have entries in /dev, typically

Kernel Modules A kernel module is a piece of code that can be added

Kernel Modules A kernel module is a piece of code that can be added to the kernel at runtime to extend its functionality. Often, device drivers for proprietary devices are provided this way Like the kernel itself, device drivers are written in C 89/C 90/ANSI C No // comments, no mixed declarations and code, etc. No variable-length arrays Floating-point arithmetic is not allowed within the kernel! Requires library support, which is part of user-space, not the kernel

Example “Hello World” kernel module #include <linux/kernel. h> #include <linux/module. h> MODULE_LICENSE("GPL"); static int

Example “Hello World” kernel module #include <linux/kernel. h> #include <linux/module. h> MODULE_LICENSE("GPL"); static int __init hello_init(void) { printk(KERN_ALERT "Hello, worldn"); return 0; } static void __exit hello_exit(void) { printk(KERN_ALERT "Goodbye, cruel worldn"); } module_init(hello_init); module_exit(hello_exit);

Creating Kernel Modules Kernel modules are part of the kernel itself (once loaded) and

Creating Kernel Modules Kernel modules are part of the kernel itself (once loaded) and thus cannot user-space libraries Normal exported kernel library functions are available (like kmalloc, printk, etc) Some exported functions require that the module be licensed under the GPL, which can be signaled by the MODULE_LICENSE(“GPL”) line in the previous example These are usually only internal interfaces – most useful functions have no such restriction Like with any other kernel code, you are responsible for your own memory management You MUST ensure that you free all memory allocated in your module before it is unloaded Kernel modules, like other kernel code, often use goto statements for error handling

Creating Kernel Modules Kernel modules can create threads to run, however typically modules are

Creating Kernel Modules Kernel modules can create threads to run, however typically modules are implemented in an eventdriven architecture In the initializer, you will typically register callbacks into some kernel system to happen on various events When the event occurs, the kernel will call your callback function automatically Initializers are specified using the module_init() macro A cleanup function that is run when the module is unloaded can be specified with the module_exit() macro

Synchronization and Concurrency As the kernel itself is multithreaded (and various applications can call

Synchronization and Concurrency As the kernel itself is multithreaded (and various applications can call into the kernel at the same time), care must be taken to properly handle concurrency within kernel module code The kernel provides various locking mechanisms like mutexes, semaphores, condition variables, reader/writer locks, reader/writer semaphores As well as lower-level primitives like the futex, rcu_lock, or spinlock

Compiling/Loading Modules can be built in the kernel tree or out of the kernel

Compiling/Loading Modules can be built in the kernel tree or out of the kernel tree In-tree modules can usually also be built-in to the kernel A Makefile must be provided to build a kernel module An example is provided on the next slide Modules are loaded into the kernel by the insmod program insmod my_module. ko Modules can be unloaded by the rmmod program rmmod my_module

Kernel Module Makefile obj-m += my_module. o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD)

Kernel Module Makefile obj-m += my_module. o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

Linux Devices and Drivers In Linux, devices are represented internally as a device number

Linux Devices and Drivers In Linux, devices are represented internally as a device number consisting of a major number and a minor number Major number: type of device Minor number: instance of device The kernel can instantiate multiple copies of a device driver to handle multiple instances of a given type of device See the include/uapi/linux/major. h file in the kernel source code for a mapping of major device numbers to various types of devices

Device Setup and Cleanup Device drivers include a probe function to handle setup for

Device Setup and Cleanup Device drivers include a probe function to handle setup for multiple instances A remove function is provided to be called when a device is removed (such as being unplugged) Should clean up whatever the probe function initialized for that instance When a device driver module is loaded, the kernel calls the module’s init function, which sets up the probe/remove callbacks Each time a device matching the driver is detected, the probe function will be called When a module is unloaded, its exit function will be called The remove function will be called for every initialized instance of the device first!

Device Operations Device drivers typically have three parts: registration, interrupt handling, and user-space interactions

Device Operations Device drivers typically have three parts: registration, interrupt handling, and user-space interactions Registration is required, the others are usually provided Registration is to set up the kernel’s callbacks for the device Interrupt handling deals with low-level hardware interrupts and DMA User-space interactions are to provide entries in /dev, respond to system calls for device status, and otherwise interact with the user

Creating Device Nodes In order to interact with user-space, drivers typically create a device

Creating Device Nodes In order to interact with user-space, drivers typically create a device node in devfs to allow the user to perform various operations This requires registering a set of callbacks that provide for the system calls involved to call on the driver Registration of the callbacks happens in the probe function Creation of a device node requires the major/minor numbers of the device, its name, and permission bits

Miscellaneous Devices There are many steps involved in creating devices The kernel provides an

Miscellaneous Devices There are many steps involved in creating devices The kernel provides an interface called miscdevice that can automate some of the boilerplate for you for creating simple character devices Each miscdevice instance is implemented using a struct miscdevice Include/linux/miscdevice. h Callbacks for file system calls are provided with a struct file_operations Miscellaneous devices all share major number 10 The kernel usually dynamically assigns minor numbers to misc devices For an example, see the kernel’s real-time clock (rtc) driver