Smart or virtual hardware drivers

Published on May 04, 2016

Smart or virtual hardware drivers

    The first article about drivers was absolutely introductory, and I thought that it could not be supplemented with a story about how the drivers of more modern devices are arranged.

    First, we introduce the definition of bus master: a device that can be not only a slave, but also a master on the computer bus. That is, not only to respond to I / O transactions initiated by the processor, but also to initiate them independently - on their own initiative to "go" into memory.

    The history of such devices is rooted in the concept of DMA: back in the days of the progenitor of microprocessors, the 8080 microprocessor (KR5080IK80), it became clear that it would be nice to unload the processor from the routine operation of dragging bytes between I / O devices and memory.

    The DMA (Direct Memory Access) controller was an external subsystem with respect to the input / output device that had to be explicitly programmed - set the type of operation (write to memory, read from memory, copy memory-memory), memory address (s), etc. Actually, I am completely unfairly writing about this in the past tense - all this quite exists now, for example, in microcontrollers.

    Already in DMA mode, the driver’s operation looks significantly different - the driver is required not to perform I / O, but to prepare the settings for the device, activate it, wait for the interrupt at the end of the I / O, and verify the success of the operation. Everything said in the previous article is also true for DMA devices, but in addition to the above, the driver must understand the interaction between the device and the DMA controller, and sometimes explicitly allocate and configure the controller: if in old devices the binding of the I / O port to the DMA controller was fixed , now in many cases full routing or DMA channel selection from 2-4 options is possible.

    Separately, it should be noted that the initiation of the next I / O transaction can be automatic (DMA pounds at the highest possible speed), automatic with a pace setting (so as not to eat up the entire bus bandwidth), or by event.

    At the same time, an event in developed systems can be an interruption, just a change in the state of the microcontroller leg, or another device can be the source of the event. For example, a timer. This allows you to combine the DAC, DMA engine and timer together so that the next byte is fed to the DAC at the specified (timer) frequency. There are other options for device aggregation, for example, the inclusion of one DMA channel at the end of another. Without attracting the attention of the processor.

    It is also appropriate to say that DMA controllers sometimes can explicitly pair a pair of channels to ensure the continuity of the data stream when one channel finishes, the second channel starts and an interrupt is generated, by which the processor loads the first channel again - for the same DAC, this can be vital.

    We will return from the world of controllers to "adult" machines. Most modern I / O subsystems are no longer based on external DMA, but have its analog directly inside.

    These are devices with the "master bus" mode, bus master.

    It’s easiest to imagine them as devices with a built-in, personal and optimally designed DMA controller.

    Typically, such devices are controlled through a tree of descriptors in memory: the device has a special register in which the processor places the address of the structure in memory that contains the job for the controller. Or, more often, an array or list of structures with such tasks. The controller independently reads the tasks from the memory and performs them step by step. The task, as a rule, consists of the identifier of the operation, the address in memory, where to get data and additional parameters necessary for the operation. For example: {write to disk, disk address, buffer address in memory}. This is how modern controllers of everything are arranged: disk, USB, network interface.

    In addition to the descriptor structure, such a device also requires tools for exchanging events: the processor must be able to report that it has changed or supplemented the descriptors, and the device should be able to communicate partially or completely. The second is, of course, executed through interrupts, and for the first, a register (doorbell) is often used, in which the processor “knocks” to draw the device’s attention to changes.

    At the same time, there is a danger of changing exactly what the device is currently processing, which imposes additional restrictions on the structure of the driver.

    Separately in this row are virtio devices. They appeared as a result of a victorious procession of hypervisors around the world. Traditionally, the hypervisor offered the guest OS a certain set of virtual devices that copied one or another popular physical device. A known network card or disk controller. But emulating an “iron” device is difficult, inconvenient and dreary - often you have to maintain properties that are completely unnecessary in the virtual world, and for virtualization purposes, the structure of a real iron device is usually not optimal.

    This led the authors to design deliberately virtual devices that will never (never say never © 007) be implemented in hardware and are needed exclusively for communication between the hypervisor and the guest OS. They are designed so that for a large number of heterogeneous devices it is possible to implement a common uniform infrastructure, both in the core of the guest OS and in the hypervisor.

    ( View implementation )

    In fact, the virtio driver is a packet transport with requests and responses between the guest OS and the hypervisor. The contents of the package are specific to the type of driver and its mode. For example, for a network card, this is the address of the ethernet packet, and for a disk, a scatter-gather handle with the type of disk operation and address on the disk.

    In virtio drivers, the kernel fills the package and asks the generic vitio driver to "pass" it to the device. Now, until the device “sends” the packet back, you cannot touch it. And vice versa, if the device is able to make input on an external initiative, it needs to transfer several empty (prepared for reading) packets to it - as data arrives (for example, incoming network messages), the packets will be filled and sent back to the guest OS kernel.

    In addition, the virtio standard supports the ability of the kernel and device to agree on a mode of operation and supported functions in a standard way. For example, the virtio network driver may or may not be able to read and insert a checksum into the sent IP packets.

    It is easy to see that the virtio standard describes a rather typical generalized bus master device driver: we send a request to the “device” with an address in memory and I / O request parameters, the rest happens asynchronously.

    Against the background of the foregoing, talking about DPC is no longer so relevant, but since the discussion arose in the comments, I will give a brief description.

    In some operating systems (the Phantom “copied” this with NT, where they copied from - I don’t know) there is regular support for running the code inside the “light” threads - Deferred Procedure Call. This allows you to reduce the time spent by the driver in the interrupt: the interrupt handler only captures the event and, as a maximum, reads the status from the device - one register. The rest is done in the DPC, which is quickly activated and completes the job.

    Frankly, there is not much point in this - it’s easier to run its own high-priority thread in the driver and make input-output from it. However, there may be options. You can select a DPC priority group and guarantee them priority always higher than that of the thread. It is possible to provide ultra-fast sheduling and transfer the processor directly to these threads right at the exit of exactly the interrupt that this DPC requested, reducing latency.

    Separately, we note that from the DPC, much of what is impossible in the interrupt is possible. What exactly is OS dependent. In the Phantom inside the DPC, you can do everything, including falling asleep for a month. Sin, but - it is possible. NT, EMNIP, nevertheless, somehow restricts the rights of DPC (that is, these are not ordinary threads), but I don’t remember the details.

    On this, I feel that I have fulfilled my duty with respect to drivers. :)

    Since May 1, you, albeit belatedly. :)