HOWTO: Linux Device Driver Dos and Don'ts Linux Device Drivers DOs and DON'Ts A guide to writing Robust Linux Device Drivers Copyright (c) 2003 by Intel Corporation. This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v1.0 or later. The latest version is presently available at: http://www.opencontent.org/openpub. Author: Tariq Shureih Version: 1.1 Date updated: $Date: 2003/06/02 21:51:37 $ --------------------------------------------------------------------- Introduction This document is not a real driver HOWTO -- there are books out there on how to write a linux kernel driver. Writing a linux kernel driver can be as simple as writing three lines of code or an extensive task which requires understanding of how Linux addresses hardware on the various architectures it supports as well as the understanding of PC concepts (all reference in this document is x86 architecture-centric yet not specific). What this document will address is the DOs and DON'Ts when writing a linux kernel device driver. These DOs and DON'Ts are based on the kernel-janitor project's TODO list. Further, concepts introduced by the original Hardened Drivers spec published at http://hardeneddrivers.sf.net are also present in this document. For more information on the Kernel-Janitor project, visit http://kernel-janitor.sourceforge.net/. 1-Overview 1.1 Why this document? I wanted to collect the information I learned when I got involved in kernel developmentin a single document and hopefully make it a guide to newbies and/or people looking for those little tricks that go into writing a robust device driver. This document is rather a simple guide to known methods and techniques when writing a Linux device driver and it could be regarded as a companion to other available resources. 1.2 What's a "Hardened Device Driver"? The answer to this question depends on who you ask. To some, a hardened device driver is a stable, reliable, instrumental and highly available device driver. In a previous effort to specify what constitutes a hardened driver, a hardened driver was described as consisting of three levels: 1-Stability and Reliability: The use of best-known coding practices within the driver to detect and report errant conditions in hardware and software, to protect the driver, kernel, and other software components from corruption, to provide for fault injection testing, and to make the code clear and easy to read for accurate maintenance. 2-Instrumentation: Statistics reporting interface, diagnostic test interface, and POSIX error logging for monitoring and management by higher-level system management components. 3-High Availability: Special features, such as device redundancy, hot swap, and fault recovery, to enhance availability. This document will attempt to describe "hardened drivers" with a slightly different approach; yet building on some of the highlights above, what a hardened (robust) device driver should mean and how it should be implemented and measured. To avoid confusion with previous efforts to speficy the requirements of a hardened driver, this document will refer to the ideal driver as a robust driver. 1.3 Robust device drivers A robust driver is really just a robust, bug free and maintainable example of kernel level code. As Linux evolves, the specifics of what makes up a robust device driver will change, but the following general attributes will probably hold consistent: -Follows the Linux CodingStyle. making it is easy to maintain and consistent with the kernel coding style.. Reference: /Documentation/CodingStyle Reference: http://www.kroah.com/linux/talks/ols_2002_kernel_codingstyle_talk/html/ Note: no more is discussed on this topic since the references above cover all aspects. -Efficient in managing faults and handling, reporting and recovering from errors. (printk levels, enumerated return codes, etc.) Also not panic() happy. Device drivers can't panic for small reasons. This goes hand in hand with proper error management and recovery. -Complies with latest Linux kernel APIs and interfaces. This means it's maintained and ported periodically, aware of deprecated functions, macros and interfaces. Uses the new device driver model, kobjects and sysfs. Cli-sti removal. -Properly manages resources (memory, semaphores, interrupts, etc.). -Uses kernel-provided "C" I/O functions instead of assembly code. (inb, outb, etc.) -Does not contain any dead code or unused variable. If dead code exists and for a reason (future usage), it should be properly marked as such, not compiled so it does not take up space. -Optimizes usage of space once compiled (inlining when appropriate for example). The above bullets provide a high-level description of the various areas a developer should be aware of during, or should review after, writing or porting a device driver. As mentioned before, these are general attributes that would hold true for most robust device drivers. 2.0 Where do I start? If you're new to writing Linux device drivers, you're probably better off getting a copy of O'Reilly's book "Linux Device Drivers", reading the Linux-kernel-source/Documentation/driver-model, and looking through some of the simple drivers existing today in the kernel source (such as linux/drivers/char/nvram.c). Also, you can get a great deal of information at www.kernelnewbies.org. Once you've written your driver and gotten the basics to work (compiling, loading, unloading, queries, file operations, etc.), you can look over this document and gain some insight into areas you might have missed or that would help you with your driver implementation. If you're porting an existing driver or you're experienced in writing Linux device drivers, this document might help guide you to get up to speed on areas that require special attention or common porting tasks you need to be aware of. Further, I hope this document will help you in avoiding common DON'Ts and shed light on advocated DOs that will help your driver and code function at the highest levels of performance. 3.0 OK, I'm ready... Let's start examining the aspects of a robust device driver code and the various techniques to achieve high efficiency and stability. 3.1 Efficient error handling, reporting and recovery: You can follow some simple and proven ways to handle errors and faults within your code: According to the kernel-janitor TODO list, you should always check printk() calls to include appropriate KERN_* constant. Sample code: [snip] int res; .... if (res = register_chrdev(MAJOR, MOD_NAME, &fops)) { printk( KERN_ERR "Failed to register device %s with major %d\n", MOD_NAME, MAJOR); return res; } .... KERN_* are defined in linux/include/linux/kernel.h as follows: #define KERN_EMERG "<0>" /* system is unusable */ #define KERN_ALERT "<1>" /* action must be taken immediately */ #define KERN_CRIT "<2>" /* critical conditions */ #define KERN_ERR "<3>" /* error conditions */ #define KERN_WARNING "<4>" /* warning conditions */ #define KERN_NOTICE "<5>" /* normal but significant condition */ #define KERN_INFO "<6>" /* informational */ #define KERN_DEBUG "<7>" /* debug-level messages */ Note: Use printk only when necessary. Be informative and avoid flooding the console with error messages when error condition is detected (flood control). Goto statements are advocated in the Linux community for the following reasons: -Uses less repeated code. -Is just as readable as other methods. -Is less error-prone. -Easier to change/maintain for the next person who has to tweak your code. -Keeps the error handling code out of the way for more efficiency in cache utilizations, having (normally) more frequent successful code be a fall through and thus avoiding jumps. [gcc provides directives likely/unlikely to pick the fall through paths of repeated code.] -Used across the kernel code and using goto makes error handling uniform across the kernel. Sample code: [snip] int res; ... res = tty_register_driver (&serial_driver); if (res) { printk (KERN_ERR "serial.c: Failed to register serial driver\n"); goto out; } res = tty_register_driver (callout_driver); if (res) { printk(KERN_ERR "serial.c: Failed to register callout driver\n"); goto out_callout; } ..... out_callout: tty_unregister_driver (&serial_driver); out: return res; [snip] Your device driver should always return a status for a request it received. It is advocated you always use enumerated return codes as well as normal return codes. Returning 0 = pass 1 or -1 = failed is vague and could be misleading. All standard errors returned from driver code should use the standard errors provided by the Linux kernel header files in: linux/include/linux/errno.h linux/include/asm/errno.h linux/include/asm-generic/errno-base.h linux/include/asm-generic/errno.h Further, many drivers had the return statement as such: .... return EIO; /* I/O Error encountered */ and it should be: .... return -EIO; /* I/O Error encountered */ A device driver should do everything within it's capabilities to avoid calling panic(). The fact that a device (hardware) is broken, non-responsive, and/or faced an unresolved condition; doesn't mean the device driver should call panic(). Rather, the device driver should try to recover, re-initialize, or finally simply return an appropriate error code, release allocated resources, log a message and exit gracefully. Only in extreme cases where the OS and the system overall is un-recoverable should a device driver call panic(). As a general rule device drivers should not invoke the panic() function and should leave it to the kernel core sub-systems as the following examples show: Example: linux/drivers/char/watchdog/pcwd.c This is a real example from a real driver in the linux-2.5.52 kernel: [snip] ....line 272 if (revision == PCWD_REVISION_A) { if (cdat & WD_WDRST) rv |= WDIOF_CARDRESET; if (cdat & WD_T110) { rv |= WDIOF_OVERHEAT; if (temp_panic) panic("pcwd: Temperature overheat trip!\n"); } } [snip] In this case, the watchdog card is invoking a panic() when it detects that the CPU is over-heating. If you look at the panic() function in linux/kernel/panic.c you will find that unless the variable "int panic_timeout > 0" where the panic() function would call machine_restart after waiting a panic_timeout amount time; panic() would actually put the system in a dead-lock (infinite loop) which obviously in this case would harm the system more than help. A more appropriate way of handling such cases may be to simply shut the system dow n: [snip] if (revision == PCWD_REVISION_A) { if (cdat & WD_WDRST) rv |= WDIOF_CARDRESET; if (cdat & WD_T110) { rv |= WDIOF_OVERHEAT; if (temp_panic) { printk (KERN_EMERG "pcwd: Temperature overheat trip!\n"); machine_power_off(); } } } [snip] It's important _NOT_ to confuse "lazy" panics with real appropriate use of panic. What this document advocates against is the "lazy" panics where the developer did not have an understanding of the full picture and opted to panic() out for simplicity. 3.2 Up-to-date with kernel APIs/Interfaces: This document was written during the development of the 2.6 Linux kernel. During the development and various releases of linux-2.5.x, many concepts, interfaces and sub-systems were either introduced new, modified or totally removed. Your new or existing device driver needs to be aware of these changes in order for it to operate in the first place; further, to operate efficiently and avoid any potential side effects that may be cause of any instability or degredation of quality. This document addresses to the best of the author's ability all known areas of importance in accordance with the kernel-janitor project, the linux kernel mailing list, personal experience while contributing to linux kernel and gathered input from experienced developers in the open source community. 3.2.1 Module interface changes Prior to the 2.5 Kernel, a driver would declare: static int my_init_func (void) { ...... } static void my_cleanup_func (void) { ........ } module_init (my_init_func); module_exit (my_cleanup_func); The new way: static int __init my_init_func (void) { ...... } static void __exit my_cleanup_func (void) { ...... } module_init (my_init_func); module_exit (my_cleanup_func); MODULE_LICENSE ("license_type"); /* This is now required in most cases */ /* Could be GPL, BSD or whatever */ Reference: linux/include/linux/module.h Reference: ftp.us.kernel.org/pub/linux/kernel/people/rusty/modules/FAQ These macros are no longer supported in 2.5.x and in 2.6 Linux kernel. Instead set the .owner field as part of your file_operations struct. IN 2.4 kernel you used to have to adjust the module reference count to keep track usage in case the module dies or is unloaded while it was in use, usually in the "open" function: static int my_open (struct inode *inode, struct file *file) { ........ MOD_INC_USE_COUNT; ........ } You no longer need to adjust your own module reference count. ------------------------------------------------------------- -Reference counting from other modules: A Golden Rule: if you are calling through a function pointer into a (different) module, you must hold a reference to that module. Otherwise you risk sleeping in the module while it is unloaded. You can get a reference to a module by using: try_module_get(owner); and you don't have to check if OWNER != NULL, this is done by try_module_get. To put the reference back: module_put(owner); // Per FAQ, owner == NULL is OK Also, if you get an entry point to another module via symbol_get() or you got a function pointer or that module EXPORT_SYMBOLed a function, you need to try_module_get(owner) first and not call into the module if it fails. -Avoid using GET_USE_COUNT(module) especially if module unloading is disabled in the kernel. Then, there is no ref count value and nothing to assign to. -It is safe to call try_module_get and module_put from an interrupt/softirq. (Fixme:example) -Net drivers should call SET_MODULE_OWNER instead of MOD_*_USE_COUNT. -It is now required to have all module information in you code: MODULE_AUTHOR,MODULE_DESCRIPTION,SUPPORTED_DEVICE, PARM_DESC example: MODULE_AUTHOR("The Linux Kernel Team"); MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet driver"); MODULE_LICENSE("GPL"); Note: MODULE_PARM was replaced with module_param and is defined in linux/include/linux/moduleparam.h as follows: module_param(name, type, perm) which works if the driver is built-in or a module not requiring a #ifndef MODULE for parsing the kernel command line arguments. See 3.2.2 Sysfs and new driver model below. 3.2.2 Sysfs and new driver model Reference: linux-kernel-source/Documentation/driver-model/ Reference: linux-kernel-source/Documentation/filesystems/sysfs.txt Reference: linux-kernel-source/Documentation/kobject.txt Sysfs was designed with kernel space simplicity and unifrom implementation in mind. It's not simplifying the user space interface to kernel objects, however, it tries to maintain the same structure yet splitting the information from /proc which has become very confusing. Make sure you read the "kojbect.txt" file mentioned above first. Sysfs is tied inherently to kobjects. In this section I will try to show the simple fast ways to use Sysfs, and focus on the details that seem to be missed or not properly documented. Sysfs is always compiled in (kernel 2.5 and up) and can be accessed by: mount -t sysfs sysfs /sys where /sys is the mount point and is created by: mkdir /sys So the idea is to expose your device attributes (or driver bus and possibly a class -- a collection of similar objects or devices logically grouped under the same sysfs subdirectory such as "input". A mouse could be a PS/2 or USB device but it's still an input device and belongs to the "input" class.) via files created under /sys. These attributes correspond to specific functionality your device can perfom and user-space can access by writing values directly to these files. The most common task is probably porting existing driver to use sysfs or writing new drivers that take advantage of sysfs. Since these devices belong to a specific "bus" such as PCI or USB and probably also belong to a class of devices common in functionality but not "bus" -- you can have a PS/2 mouse and a USB mouse; both of class "input" but each attaches obviously to a different bus, the work to convert the buses, classes, etc. is either complete or near complete (network class not yet done as of this document time.) When you write a new driver for a PCI device and you allocate your struct pci_dev *pdev, for example, in your device's private data structure, it has a pointer to (struct pci_driver *driver) which has an embedded (struct device dev) in it. This is pulled by sysfs for the device driver. To access your device instance from within the driver, you point to xxx_driver->pdev->dev. struct xxx_driver { char *xxx_name; int chip_id; .... struct pci_dev *pdev; .... } Now, your device is discovered by the bus (pci in these examples during registration, the pci subsystem will automatically register your device driver with sysfs and create the appropriate files under the sysfs hierarchy under /sys/bus/pci/drivers/xxx_driver/ which is in reality a symbolic link to /sys/devices/pci0/device_address. Under these directories, your device driver attributes are exposed. There are default files created that might not be implemented features but every device gets such as name, power, class, vendor, irq, etc. and then there are device specific features that you will need to implement. These device-specific are exposed under the device instance's directory and not the driver directory. Simple Example 1: Here is an example of how you can create a new driver to an existing class. This is a dummy "character" device driver that I created under the "input" class as a system bus device: This driver has the usual struct file_operations fops: open, close, seek, etc. It registers a character device at /dev/mydriver with major 42 and minor 0. I register the driver as normal with register_chrdev(MAJOR, name, &fops); For sysfs: I had already declared a: struct device_driver mydriver = { .name = "mydriver", .bus = &system_bus_type, //From linux/device.h .devclass = &input_devclass //exported from the input subsystem } Note: a big effort in the kernel community has been to enforce the use of C99 style struct declaration. The example above shows how you do that and how you at the same time comform with the linux-coding-style. I wanted to be able to query the driver for the debug flag and also be able to set it by doing: echo "1" > /sys/class/input/drivers/mydriver/debug // writing 1 or 0 for on or off Sysfs provides two methods (show and store) for reading/writing attributes data. I implmented two functions: static ssize_t show_debug (struct device_driver *d, char * buf) { return sprintf(buf, "debug: %i\n", debug); } and static ssize_t store_debug (struct device_driver *d, const char * buf,ssize_t count) { int tmp; if ( (sscanf(buf, "%i", &tmp)) != 1) return -EINVAL; debug = tmp; return strnlen(buf, count); } Some important notes from linux/Documentation/filesystems/sysfs.txt -sysfs allocates a buffer of size (PAGE_SIZE) and passes it to the method. -The buffer will always be PAGE_SIZE bytes in length. On i386, this is 4096. -show() methods should return the number of bytes printed into the buffer. This is the return value of snprintf(). -store() should return the number of bytes used from the buffer. This can be done using strlen(). Note: The Sysfs implementation will not allow you to change a device driver name from user space, so don't implement an attribute to do so :) So now I have my (sysfs) device_driver structure and my methods. I need to register my attributes. Sysfs "linux/device.h" implementation provides MACROS to simplify this task: You simply declare: DRIVER_ATTR(debug, 0644, show_debug, store_debug); Now we have to create the files associated with the attributes: for drivers, we call: driver_create_file(&mydriver, &driver_attr_debug); where: mydriver is my device_driver struct. driver_attr is the prefix provided by the function. debug is the attribute name appended to the show_ and store_ method declarations. Of course there is also: driver_remove_file(&mydriver, &driver_attr_debug); for when you unload the driver. A common question is "Where do I put these functions in my overall code?". Answer is you don't want to register attributes or create files until the driver or module has finished initializing and completely loaded. In device instantiation usually you call these functions once the instance is registered with no errors. Same goes with removing the files. Example 2: adding attribute(s) to existing driver In this example I am using the "tulip" device driver implemented currently in the kernel. This example assumes the developer wants to implement a NEW attribute that is "device specific" to the tulip hardware and is not one of the common or default attributes already provided by pci-sysfs. The attribute name is "ctrl" and in reality is a dummy attribute that does not do anything. However, it should demonstrate how you implement an attribute for an existing device driver. In function prototypes, I declared three new functions: static ssize_t show_ctrl (struct device *d, char *ctrl_buf); // Show method static ssize_t store_ctrl (struct device *d, const char *ctrl_buf,size_t ctrl_count); //Store method static void attr_reg (struct tulip_private *device); // Called to create files Also, I declared a global variable: static int tulip_ctrl = 0; The functions are implemented as follows: a very simple show method: static ssize_t show_ctrl (struct device *d, char *ctrl_buf) { return snprintf(ctrl_buf,4096, "%i\n", tulip_ctrl); } and a simple store method: static ssize_t store_ctrl (struct device *d, const char *ctrl_buf, size_t ctrl_count) { int ret; if (sscanf(pm_buf, "%i", &ret) != 1) { printk (KERN_ALERT "Invalid flag for tulip ctrl\n"); return -EINVAL; } if (ret) { tulip_ctrl = 1; } else { tulip_ctrl = 0; } return strnlen(ctrl_buf, ctrl_count); } The catch is in attaching these attributes and the created files for them into the existing structure under sysfs for the tulip driver. The tulip device show up under: /sys/buc/pci/drivers/tulip/;/ I wanted my new "ctrl" attribute to show under the above-mentioned directory. Remember what I said above about the pci_dev structure having the "device" structure embedded in it? Now we can get to the sysfs device structure. Note the prototype of the attr_reg, a pointer to the tulip_private structure which looks like this: struct tulip_private { const char *product_name; struct net_device *next_module; struct tulip_rx_desc *rx_ring; struct tulip_tx_desc *tx_ring; ....... struct pci_dev *pdev; ....... }; Inside the tulip_private structure, you see a pointer to a pci_dev structure which looks like this: struct pci_dev { struct list_head global_list; /* node in list of all PCI devices */ struct list_head bus_list; /* node in per-bus list */ struct pci_bus *bus; /* bus this device is on */ struct pci_bus *subordinate; /* bus this device bridges to */ ........ struct device dev; /* Generic device interface */ ........ }; Now we have access to the device's entry into sysfs and can access it as such: static void attr_reg(struct tulip_private *device) { device_create_file(&device->pdev->dev, &dev_attr_ctrl); } But we're not done yet. We have not declared the macros for the attributes and did not place the calls for attr_reg and device_remove_file in the tulip code yet. I chose to place the DEVICE_ATTR macro right after the function prototypes. Remember all the macro does is declare your attribute structures for you and nothing takes place until you actually call device_create_file. Also remember you don't want to call attr_reg until the device instance has initialized and loaded completely. Code: ...... Top of the file after includes in prototype declaration..... static ssize_t show_ctrl (struct device *d, char *pm_buf); static ssize_t store_ctrl (struct device *d, const char *pm_buf,size_t pm_count); DEVICE_ATTR(ctrl,0644,show_ctrl,store_ctrl); static void attr_reg(struct tulip_private *device); In function tulip_init_one I added a call to attr_reg: .......[snippet from patch] @@ -1687,6 +1699,7 @@ /* No initialization necessary. */ break; } + attr_reg (tp); /* put the chip in snooze mode until opened */ tulip_set_power_state (tp, 0, 1); Of course let's not forget removing the files when the device instance is unloaded: static void __devexit tulip_remove_one (struct pci_dev *pdev) { @@ -1757,6 +1793,9 @@ return; tp = dev->priv; + + device_remove_file(&tp->pdev->dev, &dev_attr_ctrl); + pci_free_consistent (pdev, sizeof (struct tulip_rx_desc) * RX_RING_SIZE + sizeof (struct tulip_tx_desc) * TX_RING_SIZE, @@ -1774,7 +1813,6 @@ /* pci_power_off (pdev, -1); */ } Now as you compile the driver and load it, you will get a new entry for "ctrl" in: /sys/bus/pci/driver/tulip/0:11.0/ctrl Keep in mind the above example implements a device-specific attribute; meaning this attribute is not driver-wide and is only available (in this example) if the device itself provided functionality specific to it and is not implemented in the generic driver. 3.2.3 Cli() and Sti() Starting linux-2.5.x and targeted for linux-2.6.x, the following functions (macros) are absolete and have been deprecated: cli(), sti(), save_flags(), save_flags_cli and restore_flags(). This is especially true for SMP systems. Drivers need to use Linux provided locking primitives or use semaphores. See linux/Documentation/cli-sti-removal.txt One thing to keep in mind especially when converting exiting drivers to be SMP-safe when handling interrupts and lock synchronization: In most cases, it is not as simple as search and replace old calls with new ones. When dealing with interrupts and locks, you need to analyze the overall driver code and understand the objective of these calls. if you look at linux/include/linux/interrupt.h: The old UniProcessor (UP) macros are mapped until all code gets fixed. #if !CONFIG_SMP # define cli() local_irq_disable() # define sti() local_irq_enable() # define save_flags(x) local_save_flags(x) # define restore_flags(x) local_irq_restore(x) # define save_and_cli(x) local_irq_save(x) #endif These will be phased out by linux-2.6 release. Whether your driver is UP or SMP, it's more appropriate to use appropriate locking to protect critical code, handle interrupt within your driver, and synchronization. Spinlocks provide a performance advantage being faster to acquire. The spinlock functions (actually macros) are defined in: linux/include/linux/spinlock.h 3.3 Misc pointers to new API -proc_register is dead, use create_proc_entry() instead. -Look at the new PCI API: o pci_enable_device is required prior to reading device pdev->irq or pdev->resource[] since these values may not be correct until the call is made. o use pci_set_drvdata() to set dev->driver_data, likewise pci_get_drvdata() instead of reading directly. o PCI_CACHE_LINE_SIZE should be done during PCI initialization. -Replace check_region with request_region and check return code: o Example: [snip from patch sent to KJP] if ((state->type != PORT_UNKNOWN) && state->port) { - request_region(state->port,8,"serial(set)"); + if(!request_region(state->port,8,"serial(set)")) { + printk(KERN_WARNING "serial port request region %d failed.\n", state->port); + return -EBUSY; + } } -sleep_on() function will go away in 2.5/2.6 kernels. 3.3 Managing resources: In this section we will look at the various resource allocation methods, variable initialization, balancing function calls and proper string function for drivers. 3.3.1 String functions -strtok() is not thread and SMP safe and strsep() should be used instead: - for (opt = strtok(options, ","); - opt != NULL; - opt = strtok(NULL, ",")) { + while (opt = strsep(&options, ",")) { + if (!*opt) + continue; -strlen and sprintf should be avoided in drivers, especially since sysfs specifies in linux/Documentation/filesystems/sysfs.txt that the show() method should always use snprintf to return the number of bytes printed into the buffer. Same goes with the store() method. It should return the number of bytes used from the buffer using strnlen(). 3.3.2 Variable declaration and initialization. -For optimal assembly code output, declaring [const] char foo[] = "blah"; is better than [const] char *foo = "blah"; since the later would generate two variables in final assembly code, a static string and a char pointer to it. -"Unsigned int" is preferred to "int" because it generates better asm code on all platforms except sh5. However, if needed for negative error return codes, "int" is sufficient. -If you have a pointer variable to a struct and need to use "sizeof", it's better to use "sizeof(*var)" than "sizeof(type)". This is a maintenance issue. If the type is changed, you don't have to search for all instances of "sizeof(type)" and update them. 3.3.3 Balancing functions: It is important to make sure all your function calls for resource allocation/release are balanced and matched on the other end and during or after failures. The obvious memory allocation using kmalloc and kfree as well as kfree_skbs when freeing skbs. Common areas found needing attention are: -pci_alloc_consistent() and pci_free_consistent(). -ioremap() must be balanced by an iounmap(). This is a common memory leak. -Functions with *_lock_* naming should be balanced with their *_unlock_* partner to void dead-locks. 3.4 I/O operations: 3.4.1 All I/O space access should use the C I/O functions instead of assembly code. These functions are: Byte I/O read/writes: unsigned inb(unsigned port); //read void outb(unsigned char byte, unsigned port); //write Word I/O read/writes: unsigned inw(unsigned port); //read void outw(unsigned short word, unsigned port); //write Long I/O read/writes: unsigned inl(unsigned port); //read void outl(unsigned doubleword, unsigned port); //write String versions of the I/O space I/O Access: void insb(unsigned port,void *addr,unsigned long count); //read void outsb(unsigned port,void *addr,unsigned long count); //write void insw(unsigned port,void *addr,unsigned loing count); //read void outsw(unsigned port,void *addr,unsigned long count); //write void insl(unsigned port,void *addr,unsigned long count); //read void outsl(unsigned port,void *addr,unsigned long count); //write Example: __asm__( "mov $0, %al\n\t" "outb %al, $0x70"); should be: outb(0x00, 0x70); 3.4.2 All memory mapped I.O access should use read()/write() functions instead of de-referencing a pointer. These functions are: unsinged readb(address); unsigned readw(address); unsinged readl(address); void writeb(unsigned value, address); void writew(unsigned value, address); void writel(unsigned value, address); Example: *(u8 *) (IOBASE+IER) = 0x80; should be: writeb(0x80, IOBASE+IER); Note: you have to use the -O2 optimization flag while compiling for these functions (macros really) to expand. 3.5 Obvious DONTZ -Device drivers should never add a "syscall". system calls are decided by the linux maintainers and involve assembly code and mess with software interrup (int 0x80); something drivers should never do. -Ofcourse always avoid messing with BIOS unless you really know what you're doing. -Do not access any resources before you check and reserve them: -Do not add driver IOCTLs that duplicate existing ones. -Do not turn off compiler warnings in Makefiles. This will hide warning that may be really fatal. -Do not use __SMP__ anymore, it doesn't exist (or won't soon). -Do not mix formats or do type conversion in kernel space. -Do not invent new APIs if you're not sure one exists, do your research.