SAS SMP PASS-THROUGH VIA BSG ---------------------------- Introduction ============ Serial Attached SCSI (SAS) has a Serial Management Protocol (SMP) which is mainly used to communicate with SAS expanders. SAS expanders typically have between 12 and 36 phys and permit many disks (e.g. more than 8) to be connected to a SAS HBA or RAID controller. In SAS interconnects that include one or more expanders, the kernel uses SMP to discover SAS and SATA devices during boot up. The kernel also responds to a BROADCAST(Change) received from an expander which indicates some change in the disposition of connected SAS and SATA devices. SAS expanders are powerful devices and user space programs may be required to access and manage them. New features such as phy-based zoning were added in SAS-2 and this increases the need for user space programs to access expanders. The SAS-2 standard outlines two ways to control expanders: a) via SMP from an SMP initiator which is found in most SAS HBAs and and RAID controllers b) an Out of Band communication method which SAS-2 does not define but nominates ethernet as an example This document outlines using method a) via the "block SCSI pass-through" (bsg) driver to access SAS expanders. The bsg driver ============== bsg is an acronym for Block SCSI Generic. The "block" used in the name of this driver is a little confusing since each bsg device node (e.g. /dev/bsg/0:0:0:0 corresponds to /dev/sda which is a SATA disk on this laptop) is actually a "char" device. The "block" term refers to the kernel layer which conveys both blocked data and messages to lower layers (such as the SCSI layer). bsg driver device nodes that can be used for SMP pass-through have names like /dev/bsg/expander-6:0 and /dev/bsg/sas_host6 . "expander-6:0" can be interpreted as the first expander (origin 0) found by (SAS) host number 6. There might be another SAS HBA in the machine (e.g. host 7) also connected to the same expander and that might lead to another bsg device node like /dev/bsg/expander-7:0 . There is also related information in sysfs. For example, /sys/class/bsg/expander-6:0/dev might contain a string like "254:2". Those are the major and minor device numbers of the corresponding bsg device node: # ls -l /dev/bsg/expander-6:0 crw------- 1 root root 254, 2 Jul 29 13:11 /dev/bsg/expander-6:0 Also the SAS address of the expander can be found with: # cat /sys/class/sas_device/expander-6:0/sas_address 0x5001517e85c3efff SMP Frames ========== SMP is a simple protocol, much simpler than the Serial SCSI protocol (SSP) and the Serial ATA Tunneled Protocol (STP) which are also defined in the SAS standards. That simplicity translates to more work for the user space programs using an SMP pass-through and a somewhat delicate division of work between the user space and the SAS HBA driver. SMP functions are sent to an SMP target (e.g. an expander) in an SMP request frame and the response is returned in an SMP response frame. Both request and response frames have a maximum size of 1028 bytes. Both request and response frames include a 4-byte header at the start of the frame and a 4-byte CRC at the end of the frame. The contents of an SMP REQUEST frame header (counting origin 0) are: - [byte 0]: SMP Frame type: 0x40 - [byte 1]: SMP function: 0x0 to 0xbf (see SAS); 0xc0 to 0xff vendor specific - [byte 2] [SAS-2]: allocated response length: expected response length in dwords (4-byte unit) excluding header and CRC - [byte 3] [SAS-2]: request length in dwords excluding this header and the CRC In SAS-1 (and SAS-1.1) bytes 2 and 3 are "reserved" which means they should be set to zero but are typically ignored by an SMP target. In this case the request and response lengths are implicit depending on the given SMP function (byte 1). The contents of an SMP RESPONSE frame header are: - [byte 0]: SMP Frame type: 0x41 - [byte 1]: SMP function: echoes byte 1 in the request. - [byte 2]: function result: 0x0 for success - [byte 3] [SAS-2]: response length in dwords excluding this header and the CRC In SAS-1 (and SAS-1.1) byte 3 is "reserved" which means it is typically set to zero by an SMP target. The response length is implicit depending on the associated SMP function (byte 1). Note that the largest request and response length is 255 (i.e. 0xff) which equates to 1020 bytes and when the 4-byte header and CRC are added, the maximum request and response frames are both 1028 bytes long. A user space program using the bsg SMP pass-through is responsible for setting up the request header and checking the returned response header. Existing bsg pass-through implementations generate and check the CRC in the HBA driver. This frees the user space program from that responsibility but leaves open the question of whether user space programs should pass through space for the CRC and what should be placed in that field. Current usage is to pass through the full frame (i.e. including the CRC) and place zero bytes in that field. The response frame allocation should also allow for the full frame (i.e. including the CRC) to be returned. Pass-through ============ Depending on the distribution the header file for the bsg driver might be found at one of these locations: /usr/include/linux/bsg.h /lib/modules//include/linux/bsg.h The first is preferred but requires a recent glibc version. The second one is in the kernel source and leads to a "don't use kernel source" warning during the C compilation that can safely be ignored. The general procedure is to open a bsg device node, use the resulting file descriptor to invoke an SG_IO ioctl and then close the file descriptor. The SG_IO ioctl sends the SMP request frame and then waits for the corresponding SMP response. The bsg device node should be opened O_RDWR with the open(2) system call. This will require root permissions (perhaps CAP_IO_RAW is sufficient). An instance of the sg_io_v4 structure needs to be built then a pointer to that instance passed as the SG_IO ioctl's third argument. Here is a small code fragment without error checking: int fd; struct sg_io_v4 req_resp; memset(&req_resp, 0, sizeof(req_resp); fd = open("/dev/nsg/expander-6:0", O_RDWR); // place data in the fields of a_req_resp ioctl(fd, SG_IO, &req_resp); // check and process the response close(fd); The sg_io_v4 structure was designed for SCSI commands but is general enough to be used with many protocols. SCSI has two types of transfers going from an HBA to a disk: SCSI commands (requests) and optionally data associated with commands like WRITE, so-called data_out. In return the SCSI initiator might get a single byte status, a sense buffer (response) and data associated with commands like READ, so-called "data_in". SMP only needs request and response frames and these are mapped to the sg_io_v4 structure's data_out and data_in buffers, respectively. Here is a code fragment to send an SMP REPORT MANUFACTURER INFORMATION function request and allow for its response: unsigned char req[] = {0x40, 0x1, 0xE, 0x0, 0, 0, 0, 0}; unsigned char resp[(0xE * 4) + 8]; /* 64 bytes */ /* Note allow for CRC in request and response */ unsigned char cmd[16]; /* unused fields have been zeroed (e.g. by a memset() shown above) */ req_resp.guard = 'Q'; req_resp.protocol = BSG_PROTOCOL_SCSI; req_resp.subprotocol = BSG_SUB_PROTOCOL_SCSI_TRANSPORT; req_resp.request_len = sizeof(cmd); /* unused */ req_resp.request = (uintptr_t) cmd; req_resp.dout_xfer_len = sizeof(req); req_resp.dout_xferp = (uintptr_t) req; req_resp.din_xfer_len = sizeof(resp); /* 64 */ req_resp.din_xferp = (uintptr_t) resp; hdr.timeout = 20000; /* i.e. 20 seconds in millisecond units */ In SAS-2 the SMP REPORT MANUFACTURER INFORMATION response is 64 bytes long including header and CRC. Processing the response starts by checking the value returned by the ioctl() system call. If that is -1 then the operation has failed and errno should be checked. If the ioctl succeeded then the following fields in the sg_io_v4 structure instance should be checked: driver_status, transport_status and device_status. If they are all zero then the number of valid bytes in the buffer pointed to by din_xferp should be (din_xfer_len - din_resid). In SAS-2 the first 4 bytes of the response should be: 0x41, 0x1, 0x0, 0xe . If the third byte is other than 0x0 then SMP function result is indicating an error. If the HBA passes back the CRC field in the response then din_resid will most likely be 0; if the HBA does not pass back the CRC field then din_resid will most likely be 4; either is okay. How the HBA reports errors such as a missing expander (e.g. cable unexpectedly removed), transport congestion (e.g. in SSP this can lead to aborted commands) and timeouts has not been agreed. Example code ============ The author has written a package of command line utilities called smp_utils (or smp-utils using Debian conventions). The source can be found in the Download section of this page: http://sg.danny.cz/sg/smp_utils.html The bsg SMP pass-through specific code is in the sgv4 directory. To allow the utilities to use other pass-throughs (e.g. proprietary Linux and other OSes such as FreeBSD's CAM extensions) an abstraction layer sits between each utility and the bsg SMP pass-through. The abstraction layer is defined in smp_utils include/smp_utils.h header (see struct smp_req_resp). Non-expander use of SMP ======================= In the section on "The bsg driver" above, /dev/bsg/sas_host6 was given as an example of a bsg SMP device node. That is a SAS HBA itself rather than an expander. The reason is that miniSAS 8087 cables carry sideband signals using the SGPIO protocol which is somewhat like I2C. These sideband signals may be controlled by the SMP READ GPIO and WRITE GPIO functions. Douglas Gilbert 20110802