The implementation of TSS has been designed to be modular to allow each module can be tested and debugged independently. The implementation not only supports a unified, integrated TSS device but also supports RAID1, RAID5, declustered RAID1 devices also. The control and configuration of the devices is done through ioctl calls.
Each device has a dev structure associated with it, which contains the information about the device including its personality type, personality specific information, information of the underlying devices, and the function pointers corresponding to the entry points specific to the device personality. Figure 2 depicts the details of dev structure and associated data structures.
When any entry point like read, write or close is called on a device, it first executes the generic implementation as exported by the device. The generic code in turn calls the personality specific function based on the type of the device. This design allows us to implement each personality separately and then integrate them together.
In the initialization module, a global array of pointers, to device structures is created. In the configuration ioctl, the generic device structure is created and its personality is specified, later the personality specific ioctl is used to configure the actual device parameters.
The I/O queue maintained by the driver is the same for all the devices controlled by it. When a request is in queue and request_fn() is called, it detaches the first request from the queue and passes this request structure to the personality specific strategy routine.
The repackaged I/O request is divided into stripe requests and all these are collected under first level stage I/O. Now the mapping from the logical to physical stripe is performed. For the declustered RAID1 and RAID5, the mappings are direct functions of the logical stripe number. For the integrated TSS device, maptable and compression table are used for this operation. Figure 3 gives the details of the way I/O staging is done at various levels in the data flow from the pseudo device to the actual device and the various abstractions involved.
We first take a look at how I/O is handled in Linux.