7.4. The ATA Layer To reduce the complexity of the individual disk drivers, much of the code common to supporting ATA drives has been abstracted out to a separate ATA layer that sits below the GEOM layer and above the device-driver level. The ATA layer handles the device-independent tasks of the tracking of request and notification between the controller and its clients. The ATA layer also handles the management of DMA mapping for the controllers that support it. Device-specific operations are left to the device driver. The device driver responds to a request from ATA by marshaling the device-independent parameters like IO request, virtual address to store the data, and transfer length into a firmware-specific format, and then executing the command. When the I/O completes, the driver returns the results back to the ATA layer. In addition to disks, the ATA layer manages any other storage device that might be connected to the system such as tape drives and CD-ROMs. The operation of the ATA layer is most easily understood by tracing an I/O request through it. The Path of an I/O Request Through the ATA Subsystem The path of a request through the ATA I/O subsystem is shown in Figure 7.8. As described in the previous section, the filesystem determines a set of blocks within its logical disk on which it wants to do I/O and passes this request down to the GEOM layer by calling the strategy() routine. Figure 7.8. The path of an I/O request through the ATA subsystem.
The GEOM layer takes the request and determines the disk to which the request should be sent. In this example the request is on an ad ATA disk. Sometimes a request may span several disks. When this happens, the GEOM layer breaks up the original request into a set of separate I/O requests for each of the disks on which the original request resides. Each of these new requests is passed down to the ATA layer by calling the appropriate strategy() routine for the disk (see the adstrategy( ) routine in Figure 7.8). The ATA adstrategy() routine gets the request and calls bioq_disksort(), which puts the request on the disk queue of the specified ATA disk. The adstrategy() routine finishes by calling the ata_start() function. The ata_start() routine is responsible for managing the channel queue. The channel queue is used to merge requests from the master and slave drives connected to the channel, since only one request can be active at a time. The ata_start() routine will alternate transfers between the two drives if both have outstanding requests. Once tag-queueing support is added, it will be possible to have outstanding requests on both drives. If the disk is not already busy, the ata_start() takes the next transfer request from the queue and invokes ata_transaction(). The ata_transaction() routine handles the low-level details of setting up a DMA mapping if the controller supports it. When the disk is ready to accept a new command, the ata_transaction() routine calls the drive-start routine set up for it by the adstrategy() routine (ad_start( ) in this example). The ad_start( ) routine takes the request off the channel queue and passes it to the driver firmware to be executed. Having completed its task, the ATA layer returns back to the caller of adstrategy ( ). The controller fulfills the request. When done, a completion interrupt arrives from the controller. The interrupt causes the ata_interrupt( ) routine to be run. The ata_interrupt( ) routine updates the request with the command completion status or sense information if there was an error. It then frees any previously allocated DMA resources, releases the channel for new work, and passes the completed request back to the ATA layer by calling ata_finish( ). The ata_finish( ) routine puts the completed I/O request on an ATA work queue to be run by the g_up thread and returns from the interrupt. When the g_up thread runs the ATA work queue, the completed I/O request is handled by ata_completed( ). The ata_completed( ) routine removes the request from its work queue, makes note of any errors that have occurred, and calls the specified completion function that maps to ad_done( ) in this example. The GEOM layer aggregates all the separate disk I/O requests together. When the last I/O operation finishes, it updates the original I/O request passed to it by the filesystem to reflect the result (either successful completion or details on any errors that occurred). The filesystem is then notified of the final result by calling the biodone( ) routine. |