Pawel Wojcik's Weblog Pawel Wojcik's Weblog

Wednesday Jan 17, 2007

SATA framework has to interface with Solaris scsi layer and sd target driver on one end (see the chart in the first part of the overview) and with SATA HBA driver on the other end. Let's first consider interface with SATA HBA driver. The interface between sata module and sata hba drivers is defined in usr/src/uts/common/sys/sata/sata_hba.h header file. SATA framework concerns itself with the generic operations, common for all SATA HBA. But it have to consider HBA capabilities, when it generate I/O and control requests. Also, it has to export some of these capabilities to the system, particularly DMA capabilities. SATA HBA driver exports its capabilities and entry points via sata_hba_tran structure, analogous to scsi_hba_tran structure. This structure contains:

  • Pointer to the SATA controller hardware instance node
  • Controller DMA attributes used for I/O data transfer (see man page for ddi_dma_attr)
  • Features supported by the controller, such as command queuing, native command queuing, ATAPI, Port Multipliers, etc. One-bit flags are used to indicate that specific feature is supported.
  • Number of sata device ports present/usable on the controller (SATA devices may be connected to those ports).
  • Queue depth supported by the controller
  • Pointers to the HBA driver functions used to:
    • Probe ports, to find the state of the port and presence/absence of a device.
    • Transport sata packet to the HBA driver, specifying some SATA device operation.
    • Abort sata packet (or packets) execution.
    • Reset sata device, device port or entire controller.
    • Initiate self-test.
    • Activate/deactivate sata device port.
Here is the complete structure definition (refer to usr/src/uts/common/sys/sata/sata_hba.h header file for details):
struct sata_hba_tran {
        int             sata_tran_hba_rev;      /* version */
        dev_info_t      *sata_tran_hba_dip;     /* Controler dev info */
        ddi_dma_attr_t  *sata_tran_hba_dma_attr; /* DMA attributes */
        int             sata_tran_hba_num_cports; /* Num of HBA device ports */
        uint16_t        sata_tran_hba_features_support; /* HBA features */
        uint16_t        sata_tran_hba_qdepth;   /* HBA-supported queue depth */

        int             (*sata_tran_probe_port)(dev_info_t *, sata_device_t *);
        int             (*sata_tran_start)(dev_info_t *, sata_pkt_t *);
        int             (*sata_tran_abort)(dev_info_t *, sata_pkt_t *, int);
        int             (*sata_tran_reset_dport)(dev_info_t *,
                                        sata_device_t *);
        int             (*sata_tran_selftest)(dev_info_t *, sata_device_t *);

                                                /* Hotplug vector */
        struct sata_tran_hotplug_ops *sata_tran_hotplug_ops;

                                                /* Power mgt vector */
        struct sata_tran_pwrmgt_ops *sata_tran_pwrmgt_ops;

        int             (*sata_tran_ioctl)(dev_info_t *, int, intptr_t);
};

When SATA framework calls one of the interface function supplied by the SATA HBA driver, it passes as one of the arguments the pointer to the device info that is specific for a given instance of the controller hardware. SATA HBA driver is using it to identify on which controller the operation is to be executed. It is important to note that by refering to the controller instance we refer to an individual hardware node in a device tree, created when the system is configured during the boot process. Such node does not necessarily corresponds to a physical controller chip, which may contain a number of independent controllers.
Specifying a controller instance is obvioulsy not enough - an additional information is needed to identify to which device or a device port apply an operation. This info is passed either directly as a pointer to sata_device addressing structure, or indirectly though the sata packet (sata_pkt) structure which embedds sata_device structure. Specifying sata port is simple - sata ports are numbered from 0 to max number of available data ports on the controller. But we also need to consider that a port multiplier may be connected to the controller's port, so in addition to the controller's port, the device port beyond the port multiplier has to be specified. To indicate when this secondary information is valid, the type of addressing (adress qualifier) is also included in the sata_address structure embedded in sata_device.

struct sata_address {
        uint8_t         cport;          /* Controller's SATA port number */
        uint8_t         pmport;         /* Port Multiplier SATA port number */
        uint8_t         qual;           /* Address Qualifier flags */
        uint8_t         pad;            /* Reserved */
};

The sata_device structure (see sata_hba.h file for definition) is used not only to address specific sata device or sata port, but also to inform the SATA framework about the state of the port (including content of standard port registers) or device and about an attached device type. The state of the port has to be always returned to SATA module by SATA HBA driver, regardless of the type of the operation initiated by SATA module.

The forth imnportant (may be the most important) interface structure is sata packet structure (sata_pkt). Sata packet carries description of the operation that SATA framework is requesting from SATA HBA driver. When the operation is completed, the satus of the operation is returned to SATA module in the same structure.

struct sata_pkt {
        int             satapkt_rev;            /* version */
        struct sata_device satapkt_device;      /* Device address/type */

                                                /* HBA driver private data */
        void            *satapkt_hba_driver_private;

                                                /* SATA framework priv data */
        void            *satapkt_framework_private;

                                                /* Rqsted mode of operation */
        uint32_t        satapkt_op_mode;

        struct sata_cmd satapkt_cmd;            /* composite sata command */
        int             satapkt_time;           /* time allotted to command */
        void            (*satapkt_comp)(struct sata_pkt *); /* callback */
        int             satapkt_reason;         /* completion reason */
};
The sata packet contains embedded sata_device addressing structure, mode of operation, SATA command to be passed to a sata device, a time allowed for the execution, and a status of the execution.
Two pointers are embedded in sata packet - one (satapkt_framework_private) is used to link the packet with SATA module internal structures and original scsi packet (if any). This pointer should not be ever modified by the SATA HBA driver.
The other embedded pointer could be used by the SATA HBA driver to link the packet with its own internal data structures related to the addressed device and the packet execution.
The pointer to a callback function allows queuing the sata packet in SATA HBA driver for delayed execution and an immediate return to the SATA module. When the operation specified by the sata packet completes, the callback function is called, with the sata packet passed as the argument.
The status of the operation is returned two ways. The completion reason specifies general reason for the completion, i.e. success, rejection due to a full packet queue, unsupported operation or unspecified driver busy condition, or a failure due to some serial link problems or error returned by the device (see sata_hba.h for details). Some additional information about a failure reason are returned either in embedded sata_device structure (port or serial link problem) or in sata command structure (satapkt_cmd) when an error was reported by the actual sata device.

The last, but not the least important structure is sata_cmd structure, embedded in the sata packet. Sata_cmd is set-up by the SATA module and specifies the content of the task file registers. Task file registers compose a set of registers used to transfer information to a sata device. In old parallel ATA word these registers were directly accessible by the IDE controller. In SATA, their content is transfered to/from the sata device via packets send through a serial link. SATA specification retained the concept of the task file registers for a software compatibility reason. It uses the ATA/ATAPI command formats, and those commands are based on task file registers interface
So, when SATA module wants to execute specific SATA command, it sets up the content of every relevant register (for a given command) in the task file register. The SATA HBA driver does not have to interpret anything, just takes prepared register content and passes it, in a controller-specific fashion, to a sata device (sometimes stuffing it into "shadow" task file registers, sometimes building complete Frame Information Structure - a packet being transfered via serial link).

struct sata_cmd {
        int             satacmd_rev;            /* version */
        struct buf      *satacmd_bp;            /* ptr to buffer structure */
        struct sata_cmd_flags  satacmd_flags;
        uint8_t         satacmd_addr_type;      /* addr type: LBA28, LBA48 */
        uint8_t         satacmd_features_reg_ext; /* features reg extended */
        uint8_t         satacmd_sec_count_msb;  /* sector count MSB (LBA48) */
        uint8_t         satacmd_lba_low_msb;    /* LBA Low MSB (LBA48) */
        uint8_t         satacmd_lba_mid_msb;    /* LBA Mid MSB (LBA48) */
        uint8_t         satacmd_lba_high_msb;   /* LBA High MSB (LBA48) */
        uint8_t         satacmd_sec_count_lsb;  /* sector count LSB */
        uint8_t         satacmd_lba_low_lsb;    /* LBA Low LSB */
        uint8_t         satacmd_lba_mid_lsb;    /* LBA Mid LSB */
        uint8_t         satacmd_lba_high_lsb;   /* LBA High LSB */
        uint8_t         satacmd_device_reg;     /* ATA dev reg & LBA28 MSB */
        uint8_t         satacmd_cmd_reg;        /* ata command code */
        uint8_t         satacmd_features_reg;   /* ATA features register */
        uint8_t         satacmd_status_reg;     /* ATA status register */
        uint8_t         satacmd_error_reg;      /* ATA error register  */
        uint8_t         satacmd_acdb_len;       /* ATAPI cdb length */
              ..........
        int             satacmd_num_dma_cookies; /* number of dma cookies */
                                                /* ptr to dma cookie list */
        ddi_dma_cookie_t *satacmd_dma_cookie_list;
};
Other interesting part of the sata_cmd structure is dma cookie list. This list contains adresses and lengths of the regions in the system physical memory, and it is used to set-up DMA controller's scatter-gather list. SATA module makes sure that each specified "chunk" of memory would match the capability of the HBA's DMA controller with respect to an address range and a transfer size (Remember DMA attributes that SATA HBA passed in sata_hab_tran structure? They are used for this transfer "limiting" purpose). Once a sata device executes the command, it returns the status of the operation in the status "register" of task file registes set . If the operation was successfull, SATA HBA driver just have to return a general 'completion reason'. If the operation failed, SATA device returns error information (command specific) in various registers. SATA HBA driver has to collect this information from the device (from "shadow" registers or from register FIS) and store it back in the sata_cmd structure. When the sata_pkt is returned to SATA module, the 'completion reason' in conjunction with additional information in sata_cmd let SATA module to determine the cause of the failure and the information that may need to be passed to other system software.

TO BE CONTINUED

Friday Jan 12, 2007

In previous part I have shown where SATA Framework and SATA HBA drivers are placed in Solaris and what are SATA Framework features/functionality. Let's see what SATA HBA driver has to offer:

SATA HBA driver role is to take care of the SATA controller hardware and its operation. SATA HBA driver is not aware of SCSA interface requirements nor SCSI-to-SATA translations.

The SATA HBA driver compatible with SATA framework compatible has to take care of:
  • Any hardware specific mapping of the SATA controller registers and resources.
  • Controller-related DMA setup and control (controller's registers and controller-specific data structures accessed via DMA).
  • PHY link control and device detection.
  • Interrupt registration and handling.
  • Detection of hotplugging events.
  • Hardware specific setup and transfer of ATA/ATAPI commands, data and error information via serial link.
  • Perform power management operations requested by the system.
Interacting with SATA HBA framework, SATA HBA driver has to:
  • Register itself with the SATA HBA framework.
  • Expose its capability and configuration.
  • Report state and changes of state of the SATA device ports.
  • Execute operation requests specified by SATA framework in sata packets.
  • Return status of the operations to the SATA framework.
  • All access to the SATA HBA driver (and SATA HBA) is via SATA framework. SATA HBA framework presents to the system each registered instance of the SATA HBA as the SCSI HBA instance. It means, that the rest of the system really see only the scsi HBA and is not aware of the type if the interconnect (serial ATA in this case). The basic functionality of a scsi HBA driver is provided by the combined SATA framework and SATA HBA driver functionality. Attached SATA devices are treated by the system as SCSI devices and are controlled by the sd target driver via scsi transport layer.

    Devices

    SATA devices, similarly to scsi devices, are represented in the device tree by the target nodes. Unlike scsi devices, which are connected to a shared scsi bus, SATA devices are connected point-to-point to SATA controller device ports. The classical mechanism of scsi device discovery and configuration is therefore not needed, because there is no need to scan any bus. SATA controller knows that a device is connected to given SATA device port and the link with a device is established automatically. With this understanding we can consider SATA target devices as self-identifying devices, that do not depend on driver.conf entries to be recognized by the system (see Solaris man page for 'scsi').
    So the target nodes (corresponding to attached and configured devices) and their properties are created dynamically (using standard nexus mechanisms) by SATA framework. This operation is performed during SATA HBA instance attachment process and when a connected device is configured for use by the system via cfgadm operation (more about it later). Target nodes are also destroyed by SATA framework when a device is disconnected/removed from a SATA device port. From the application point of view, SATA device is referred to like a scsi device, using the same naming convention and device path, e.g. /dev/dsk/cXtYd0.

    I/O Operations

    Operations for scsi device are specified by the target driver using scsi packet (see man page for 'scsi_pkt'). Since SATA framwork/SATA HBA pair registers itself as scsi HBA, it receives operational directives from the target driver in scsi packets and, optionally, as IOCTL request.
    Operations specified by scsi packets are translated by SATA HBA framework into equivalent SATA device operations (in SATA packet envelope) or are emulated within the SATA framework, or are rejected if none of the above could be done. The translation may be quite involved (even multi-phased), particularly for scsi commands that do not have direct equivalent is SATA command set.

    Any data transfer to/from the SATA device involves data buffer. Some data may be transfered via PIO transfer, but majority of the data transfer is performed using DMA. SATA framework assumes that all data is transfered via DMA. To relieve SATA HBA driver from data transfer DMA setup operations, SATA framework performs data buffer DMA mapping and DMA resources allocation, based on the information in the scsi packet. Even when the operation involving data transfer originates in the SATA framework (no scsi packet involved), the DMA setup is performed by the framework. What is involved here: DMA handle allocation and binding, partial DMA mapping management, DMA windows and cookies management, and occasionally an intermediate buffer allocation. While allocating DMA resources, SATA HBA framework takes into consideration SATA HBA capabilities and attached device capabilities - this needs to be done, because SATA devices use different command set and have different I/O capabilities than scsi devices and scsi-type requests needs to be adjusted such a way that they could be performed by SATA device.

    SATA framework sends to a SATA HBA driver (via SATA HBA Interface) a sata packet, one packet per single SATA device operation. Sata packet contains (among other data) a fully formed SATA device command, including standard registers (ATA task file registers) setup. SATA HBA driver uses this data in the sata packet without any further translation to set-up, in the controller-specific way, an I/O operation.
    The status of the SATA operation, including potential error information, is translated into SCSI format (as needed for scsi packet) by SATA HBA framework and send back to the scsi target driver.

    Control

    SATA HBA framework creates also control node for each SATA controller. This is an equivalent of the control node for scsi controller. All IOCTLs for a given controller are addressed to its control node. Device control IOCTLs are processed by the SATA HBA framework and are translated into SATA operations, if necessary. IOCTLs that are not handled by SATA HBA framework are passed either to standard nexus IOCTL handler (ndi_devctl_ioctl()) or to the SATA HBA driver, if possible (i.e. if HBA driver registered entry point for IOCTL requests).

    Hotplugging

    As I mentioned before, SATA controllers detects device physical (electrical) connection and disconnection. Device electrical connect/disconnect - hotplugging events - detected by the SATA HBA driver are passed to the SATA framework, which in response performs device detection, internal configuration/reconfiguration and error handling operations and informs the system about hotplugging events via sysevents. Hot-plugged devices are not immediately usable by the system - an explicit operator intervention is needed to configure such device and make it accessible by the system (more about this later).


    The brief descriptions above will be expanded in following sections, where we will talk about interfaces, driver attach operations, device configuration, scsi operation setup/translation, sample read/write operations, events handling, internal structures, cfgadm operations and details of various operations.

Thursday Jan 11, 2007

As many of you noticed, Solaris now supports SATA controllers and devices. To simplify writing SATA HBA drivers the new module and a set of interfaces was created, referred to as either SATA Framework or SATA module. I was a principal architect of SATA framework, but several other Sun engineers were participating in the conceptual design and the shaping of the interfaces.
It is not small piece of software - the source, sata.c, is over 300k in size. Reading this code, with associated header files may be a little confusing. So, I created an overview of the sata module, explaining what it is, how it fits in Solaris kernel, what it does, what are the interfaces and how sample operations are performed. Hopefully, it will be useful for all that want to improve and expand SATA support in Solaris Similar overview was presented about a year ago at Silicon Valley Open Solaris User Group meeting in Santa Clara and on various occasions internally in Sun organization. The overview that I plan to present here will have several parts. Here is the first one...

Up to the time of introduction of SATA Framework, Solaris support for SATA controllers was limited to controllers operating in a compatibility mode, emulating generic, parallel ATA controllers. Only generic ATA interfaces are used, i.e. task files registers and bus master control registers. This was clearly inadequate for SATA controllers not implementing standard ATA interfaces and for providing fine-control access to serial interfaces and SATA-native features. Hotplugging feature in particular was not supported in existing pci-ide/ata framework. Queuing, particularly SATA NCQ could not be supported without extensive changes to pci-ide and ata driver.

The goals of the SATA framework project:
  • Facilitate usage of existing Solaris scsi layer and common scsi target driver.
  • Provide common processing and algorithms that all SATA HBA drivers using scsa and scsi target driver would otherwise have to implement.
  • Provide native SATA support (as opposed to emulating legacy IDE devices),
  • Provide configuration management and hotplugging support for SATA HBA drivers.
  • Minimize SATA HBA driver complexity.
  • Provide platform neutrality.
  • Provide stable interface suitable for a third-party driver developers.
Why SCSI? Because Solaris scsi layer and target driver (sd) are a mature drivers (however imperfect) with already tried and tested capabilities and features. Furthermore, device naming and handling would appear to other system entities/application layers the same as for scsi devices. And lastly, T10 standard body and SATA manufacturers fairly early started an effort to define common definitions and and requirements for emulating SCSI device behavior.
SATA HBA Framework was intended as the support module interfacing SATA HBA driver (hardware specific) with existing Solaris SCSI framework. The goal was to isolate hardware-specific SATA HBA driver from SCSI related operations and interfaces and to implement generic, SATA-specific operations for use by SATA HBA driver. SATA HBA driver could be then concerned only with hardware initialization and interface to attached SATA devices, while SATA HBA Framework would implement generic SATA operations and interface with generic SCSA framework and SCSI target drivers.
SATA HBA driver in conjunction with SATA HBA Framework would be treated by SCSI framework as an SCSI HBA driver. SATA HBA Framework and SATA drivers are intended to be platform-neutral - common code would be used on x86, x64 and sparc based platforms (existing ATA support in Solaris uses different drivers on x86/x64 and sparc platforms). Additional goal was to make SATA HBA Framework interface public, thus allowing third-party developers to write simple SATA HBA drivers without Sun engineers involvement.

Basic Architecture Model:
                    +-----------------+
                    |     cfgadm      | existing
                    +-------^---------+
                            |
                            v
                    +=================+
               new  | SATA Hot Plug   | SATA specific
                    | library         | plug-in
                    +=======^=========+ library
                            |                            Solaris I/O
                            |                                ^
                            |              USER              |
      ======================|================================|================
                            |             KERNEL             |
                            |                        +-------v---------+
             cfgadm plug-in |                        |   SD driver     | existing
                     IOCTLs |                        | (target driver) |
                            |                        +-------^---------+
                            |                                ^
                            |             SCSA               |
                  +=========v==========+  Interface  +-------v---------+
             new  | SATA HBA Framework |<----------->| SCSI Framework  | existing
                  +=========^==========+             +-----------------+
                            |
                            | SATA HBA
                            | Interface
                            |
                  +=========v==========+
              new | SATA HBA Driver    |
                  | (eg. AHCI driver)  |
                  +====================+
                            |
                            V
           ( SATA devices: SATA disks, SATA ATAPI CDROMs, ...)


SATA Framework features:
  • Implemented as a loadable kernel module.
  • Implements and exports generic SCSI HBA interface.
  • Performs initial SATA devices configuration and setup.
  • Emulates SCSI operations that have no equivalent in SATA.
  • Translates SCSI operations into appropriate SATA operations.
  • Controls SATA operation sequences needed for SCSI operations.
  • Provides data buffer DMA resources setup.
  • Translates SATA operation status and error information into SCSI.
  • format.
  • Provides devctl interface for cfgadm.
  • Controls hotplugging and operator-directed operations.
  • Performs dynamic reconfiguration based on asynchronous hotplugging events.
  • Generates sysevents that may be consumed/monitored by other system entities.


TO BE CONTINUED....