|
|
Subscribe / Log in / New account

I/O Hook

By Jonathan Corbet
July 30, 2013
Writing device drivers can be a painful process; hardware has a tendency to behave in ways other than those described in the documentation. The job can be even harder, though, in the absence of the hardware itself. Developing a complete driver without the hardware can require a simulator built into a tool like QEMU — a development project in its own right. For simpler situations, though, it may be enough to fool the driver about the contents of a few device registers. Rui Wang's recently posted I/O hook patch set aims to make that functionality available.

The I/O hook module works by overriding the normal functions used to access I/O memory, I/O ports, and the PCI configuration space. When kernel code calls one of those functions, the new version will check to see whether an override has been configured for the address/port of interest; if so, the operation will be redirected. In the absence of an explicit override, the I/O operation will proceed as usual. Needless to say, adding this kind of overhead to every I/O operation executed by the kernel could slow things down significantly. In an attempt to minimize the impact, the static key mechanism is used to patch the kernel at run time. So the I/O hooks will not run unless they are in active use at the time.

There is an in-kernel interface that can be used to set up register overrides; it is a simple matter of calling:

    void hook_add_ovrd(int spaceid, u64 address, u64 value, u64 mask,
		       u32 length, u8 attrib);

Here, spaceid is one of OVRD_SPACE_MEM for regular I/O memory, OVRD_SPACE_IO for an I/O port, or OVRD_SPACE_PCICONF for the PCI configuration space. The combination of address, mask, and length describe the range of addresses to be overridden, while value is the initial value to be set in the overridden space. By using the mask value it is possible to override a space as narrow as a single bit. The attrib parameter describes how the space is to behave: OVRD_RW for a normal read/write register, OVRD_RO for read-only, OVRD_RC for a register whose bits are cleared on being read, or OVRD_WC to clear bits on a write.

There are two functions, hook_start_ovrd() and hook_stop_ovrd(), that are used to turn the mechanism on and off. Any number of overrides can be set up prior to turning the whole thing on, so a complicated set of virtual registers can be configured. It's worth noting, though, that the overrides are stored internally in a simple linked list, suggesting that the number of overrides is expected to be relatively small.

While the in-kernel interface may be useful, it will probably be more common to control this facility through the debugfs interface. The module provides a set of files through which overrides can be set up; see the documentation file for details on the syntax. The debugfs interface also provides a mechanism by which a simulated interrupt can be delivered to the driver; if an interrupt number is given to the system (by writing it to the appropriate debugfs file), that interrupt will be triggered once the overrides are enabled.

A system like this clearly cannot be used to emulate anything other than the simplest of devices. A real device has a long list of registers and, importantly, the contents of those registers will change as the device performs the operations requested of it. One could imagine enhancing this module with an interface by which a user-space process could supply register values on demand, but there comes a point where it is probably better just to add a virtual device to an emulator like QEMU.

So where, then, does a tool like this fit in? The use cases provided with the patch posting mostly have to do with the testing of hotplug operations on hardware without hotplug support. A hotplug event typically involves an interrupt and a relatively small number of registers; by overriding just those registers, the I/O hook mechanism can convince a driver that its hardware just went away (or came back). That allows testing the hotplug paths without needing to have suitably capable hardware.

Similarly, overrides can be used to test error paths by injecting various types of errors into the system. Error paths are often difficult to exercise; there are almost certainly large numbers of error paths in the kernel that have never been executed. Code that has never run has a higher-than-average chance of containing bugs. The fault injection framework can be used to test a wide range of error paths, but it is not comprehensive; the I/O hook module could be useful to fill in the gaps.

But, then, anecdotal evidence suggests that relatively few developers even use the fault injection facilities, so uptake of a more complex mechanism may be limited. But, for those who use it, the I/O hook subsystem might well prove to be a useful addition to the debugging toolbox.

Index entries for this article
KernelDevelopment tools


to post comments

I/O Hook

Posted Aug 2, 2013 22:01 UTC (Fri) by nix (subscriber, #2304) [Link] (2 responses)

Are the functions really called 'hook_*()'? That's a... disturbingly useful set of names to squat on.

Might I suggest 'iohook_*()' instead?

I/O Hook

Posted Aug 5, 2013 2:55 UTC (Mon) by ruiwang (guest, #92216) [Link] (1 responses)

Sure I'll change the names in the next version. I posted a v2 RFC on Aug 1, with some changes according to Bjorn. If you have more suggestions your comments are welcome on LKML.

Thanks
Rui Wang

I/O Hook

Posted Aug 7, 2013 13:42 UTC (Wed) by nix (subscriber, #2304) [Link]

Yeah, but whenever I try to do anything on lkml I am washed away by the message torrent (and half the time when I report bugs there, nothing happens, and just when I start to get annoyed I realise I've not put the subsystem list on there too so the people who could see the bug report missed it in the torrent).


Copyright © 2013, Eklektix, Inc.
This article may be redistributed under the terms of the Creative Commons CC BY-SA 4.0 license
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds