We looked at device driver source code using static analysis tools to find out
what drivers actually do. We look at how modern research applies to them, how
do they interact with the kernel and device, and examine if there are any
opportunities to reduce driver code.
Here is what
we find (more results and figures in paper):
Instead of request handling, surprisingly,
contributors to driver code are initialization and cleanup, comprising almost
36% of driver code on average, error handling (5%), configuration (15%), power
management (7.4%) and ioctl handling (6.2%). On average, only 23.3% of the
code in a driver is dedicated to request handling and interrupts.
- As devices become increasingly virtualization aware, quick ways to
initialize or reset are critical for important virtualization features such as
re-assignment of devices and live migration.
- Drivers contain significant configuration code(15%), specifically in
network (33%) and video (28%) drivers. As devices continue to become more
complex, driver and OS research should look at efficient and organized ways of
managing device configuration
- Overall, driver code has increased by 185% over the last eight years
driven by net/wireless drivers, GPU, media, input devices and virtualization
- We find that most driver classes have substantial amounts of
device-specific functionality outside class definition i.e. standard interfaces.
Code supporting /proc and /sys is present in
16.3% of drivers and comprises over 5.3% of total driver code. Also, 36% of
drivers have load-time parameters to control their behavior and configure
options not available through the class interface. Additionally, ioctl code
comprises 6.2% of driver code, can also cause non-class behavior of drivers.
- Hence, for 44% of drivers, attempts to recover driver state based solely
on the class interface, like shadow drivers [OSDI04] or to synthesize drivers from common descriptions of
the class, like Termite [SOSP09], may not work for a substantial number of drivers. Thus, future
research should explicitly consider how to accommodate unique behaviors
- We find that 15% of drivers have at least one function that performs
processing, and processing occurs in 1% of all driver functions. An even
higher fraction (28%) of sound and network drivers do processing.
- Thus, efforts to generate driver code automatically must include
mechanisms for data processing, not just converting requests from the OS into
requests to the device. Furthermore, virtualized systems should account for
the CPU time spent processing data when this processing is performed on behalf
of a guest VM.
- We also find that 28% of drivers support more than one chipset and these
drivers support 83% of the total devices. Any system that generates unique
drivers for every chipset or requires per-chipset manual specification may
lead to a great expansion in driver code and complexity. Furthermore, there is
substantial complexity in supporting multiple chipsets, so better programming
methodologies, such as object-oriented programming and automatic interface
generation, similar to Devil, should be investigated.
- We find a variety of interaction styles between drivers and the kernel:
drivers with little supporting infrastructure demonstrate frequent
interactions with the kernel for access to kernel services but few calls to
device support code. Drivers with a high level of abstraction demonstrate few
calls to the kernel over all. Drivers with a support library demonstrate
frequent calls to kernel generic routines as well as calls to device support
routines. Furthermore, a large fraction of driver/kernel interactions are for
generic routines (memory, synchronization, libraries) that do not involve
other kernel services. Thus, they could be implemented by a runtime
environment local to the driver. For example, a driver executing in a separate
virtual machine or on the device itself can make use of its local OS for these
routines, and drivers in user space can similarly invoke user-space versions
of these routines, such as the UML environment in SUD.
- Interaction with devices: The number and type of device interactions vary
widely across devices. Thus, the cost of isolating drivers, or verifying that
their I/O requests are correct (as in Nexus) can vary widely across drivers.
Thus, any system that interposes or protects the driver/device interaction
must consider the variety of interaction styles. Similarly, symbolic
execution frameworks for drivers must generate appropriate symbolic data for
each interaction style.
- Interaction with bus: Our results demonstrate that the flexibility and
performance of PCI devices comes with a cost: increased driver complexity, and
less interface standardization. Thus, for devices that can live within the
performance limitations of USB or in a virtualized environment for XenBus,
these buses offer real architectural advantages to the drivers. With USB,
significant standardization enables less unique code per device, and
coarse-grained access allows efficient remote access to devices.
- Based on our code similarity study, we find that 8% of all driver
code is very similar to other driver code. The paper details the similarities
found across classes. We find most similarities based on device/kernel
wrappers and code within driver from same sub-classes. Finally, we see much of
this code can be reduced, by using three methods for achieving this reduction:
(i) procedural abstractions for driver sub-classes, (ii) better multiple
chipset support and (iii) table driven programming.
Driver study methods
Starts at the common step.