momo zone

调核人的blog

Monthly Archives: 七月 2012

WD1500AHFD

在tb上搞了一块当年的猛物:西数猛禽3代透明版

评测在这里。

http://www.tomshardware.com/reviews/wd1500ad-raptor-xtends-performance-lead,1215.html
由于是06年的产物,那个时候sata才刚面世,所以即使是万转硬盘,也完全比不上现在的7200硬盘快。但寻道还是不错的,平均8毫秒。用了几天发现最大的缺点是发热量太大,不在机箱风道环境下温度能达到70度左右。

smartctl/hdparm对usb硬盘的支持

最近使用smartctl对usb硬盘获取smart信息识别。所以就仔细查了看原因。同时又记起一起使用hdparm对硬盘休眠时,usb硬盘也是有问题,使用了什么pass through方法?那时没有深入的去了解,这次正好仔细看了看代码和相关问题。

1:为什么smartctl对usb设备会失败?能否有解决的方法?

2:hdparm对usb失败后,采取的方法能否对smartctl是否有效?

查找思路

因为是用smartctl工具对usb硬盘失效的,所以就使用strace调试smartctl到底使用哪些ioctl。

ioctl(3, SG_IO, {‘S’, SG_DXFER_FROM_DEV, cmd[16]=[85, 08, 0e, 00, 00, 00

查找内核代码发现,0x85是ATA_16是个ata pass through命令。

sata硬盘将scsi命令转换成ata命令的函数是:ata_scsi_queuecmd—>__ata_scsi_queuecmd

该函数遵循的标准是SAT(scsi to ata translate spec),即如何将scsi命令转成ata命令。

在这个函数中发现:真正转成ata命令后,发送到sata控制器是使用:ata_scsi_translate;

而使用已有的数据来模拟scsi命令的是ata_scsi_simulate。

     这里必须说明一点的是,这两个函数都是SAT标准的一部分,只是一些scsi命令的结果不需要再向sata 控制器发送,只要根据已知数据就可以填充返回值。

ata_scsi_translate

    其功能就是根据scsi命令找到合适的转换函数。即ata_get_xlat_func根据scsi命令返回合适的函数。

static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, u8 cmd)
{
switch (cmd) {
case READ_6:
case READ_10:
case READ_16:

 case WRITE_6:
case WRITE_10:
case WRITE_16:
return ata_scsi_rw_xlat;

case SYNCHRONIZE_CACHE:
if (ata_try_flush_cache(dev))
return ata_scsi_flush_xlat;
break;

case VERIFY:
case VERIFY_16:
return ata_scsi_verify_xlat;

case ATA_12:
case ATA_16:
return ata_scsi_pass_thru;

case START_STOP:/*power management*/
return ata_scsi_start_stop_xlat;
}

return NULL;
}

通过这个函数可以看出,真正装换ata命令的基本都是读写函数。

但是ata command set命令很多,比如:smart相关、power management、hpa相关。但是上面的函数均没有处理。那么这些函数该如何处理的?这就迁出了SAT spec中最重要的两个命令:ATA_12、ATA_16.

    这两个命令就是告诉SATL,scsi命令集cdb中包含不是scsi command,而是真正的ata 命令,SATL只需要将其完整的发送ATA 设备即可。转换的函数是ata_scsi_pass_thru。

     以上就是一个简单的sata硬盘接收命令的过程。

     这里又有一个问题:如果用户程序例如smartctl发送的scsi命令不是ATA_12/16,而是直接的smart相关ata命令,那么是不是SATL就无法处理?是的如果仍然使用SG_IO,发送的是标准ATA command这是错误。使用SG_IO,只能发送SAT spec支持的命令.否则就会被丢弃。

    但是我使用strace hdparm -y /dev/sda时发现hdparm使用的不是SG_IO,命令是标准的ATA命令。那么它们是如何实现的?
ioctl(3, 0x31f, 0x7fffadc97e10)         = 0

查内核代码0x31f=HDIO_DRIVE_CMD。ioctl的执行情况:sd_ioctl–》scsi_cmd_ioctl—》scsi_ioctl–》sdev->host->hostt->ioctl

而scsi host的ioctl是ata_scsi_ioctl—》ata_cmd_ioctl。该函数的功能是将标准ATA命令,按照SAT SPEC转换成ATA_16命令后,生成request插入request 队列,回到了ata_scsi_queuecmd。
这样虽然两个工具的ioctl不同,但是在内核中最终走到一起。

我们这里先解决问题2,即hdparm对usb设备失败的原因:usb存储的scsi host template是:struct scsi_host_template usb_stor_host_template。这个数据没有ioctl,所以0x31f命令是没有办法执行的,所以在scsi_ioctl函数中找不到任何可以执行的函数,就只能return -EINVAL;

如果我们将hdparm发送的命令改成SG_IO方式。就不会出错了。下面是改后的代码:
int dm_start_hdardisk_standby(const char * disk_name)
{
int fd = 0;
struct sg_io_hdr io_hdr;
unsigned char ssuBlk[START_STOP_CMDLEN] = {START_STOP_CMD, 0, 0, 0, 0, 0}; /*cdb*/
unsigned char sense_b[SENSE_BUFF_LEN];
int time_secs;

if (disk_name == NULL){
printf(“%s(%d):parameter error:%s/n”, __FUNCTION__, __LINE__, disk_name);
return DM_ERR_PARAM;
}

fd = open(disk_name, O_RDONLY);

if (fd < 0){
printf(“%s(%d): open %s error(%s)l/n”, __FUNCTION__, __LINE__, disk_name, strerror(errno));
return DM_EOPEN;
}

if ( system(“sync”) != 0 )
{
printf(“%s(%d): system(sync): fail(%s)/n”, __FUNCTION__, __LINE__,strerror(errno));
}

ssuBlk[4] = 0;/*from active to standby*/

memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
io_hdr.interface_id = ‘S’;
io_hdr.dxfer_direction = SG_DXFER_NONE;
io_hdr.cmdp = ssuBlk;
io_hdr.cmd_len = START_STOP_CMDLEN;

memset(sense_b, 0, SENSE_BUFF_LEN);
io_hdr.sbp = sense_b;
io_hdr.mx_sb_len = SENSE_BUFF_LEN;
io_hdr.timeout = ((time_secs > 0) ? (time_secs * 1000) :  DEF_TIMEOUT);

if (ioctl(fd, SG_IO, &io_hdr) < 0){
printf(“ioctl error(%s)/n”, strerror(errno));
close(fd);
return DM_EIOCTL;
}

使用SG_IO命令,command是标准的scsi命令,在ata_get_xlat_func中有对START_STOP的处理。

但是这仅仅是对正常的sata硬盘而言,那么对usb硬盘会这样吗?

 

usb硬盘

usb storage spec中说明了usb存储中的command set。

static int get_protocol(struct us_data *us)
{
switch (us->subclass) {
case US_SC_RBC:
us->protocol_name = “Reduced Block Commands (RBC)”;
us->proto_handler = usb_stor_transparent_scsi_command;
break;

case US_SC_8020:
us->protocol_name = “8020i”;
us->proto_handler = usb_stor_ATAPI_command;
us->max_lun = 0;
break;

case US_SC_QIC:
us->protocol_name = “QIC-157”;
us->proto_handler = usb_stor_qic157_command;
us->max_lun = 0;
break;

case US_SC_8070:
us->protocol_name = “8070i”;
us->proto_handler = usb_stor_ATAPI_command;
us->max_lun = 0;
break;

case US_SC_SCSI:
us->protocol_name = “Transparent SCSI”;
us->proto_handler = usb_stor_transparent_scsi_command;
break;

case US_SC_UFI:
us->protocol_name = “Uniform Floppy Interface (UFI)”;
us->proto_handler = usb_stor_ufi_command;
break;

这是根据usb 控制器芯片的subclass来选择合适的命令集,我们一般见到的都是US_SC_SCSI。至少我没有见过其他类型的。这说明命令集是scsi,即在驱动中并不转成ata command,这个转换是在usb芯片中完成的。

但是我们使用smartctl时是失败,那么这是什么原因?查看smartctl发现有下面的内容:

USB devices and smartmontools

To access USB storage devices, the operating system sends SCSI commands through the USB transport to the device. If the USB device is actually a PATA or SATA disk in an USB enclosure, the firmware of its USB bridge chip translates these commands into the corresponding ATA commands. This works straightforward for read and write commands, but not for SMART commands.

To access SMART functionality, smartmontools must be able to send ATA commands directly to the disk. For USB devices, at least the following conditions must be met:

  • The USB bridge provides an ATA pass-through command.
  • This command is supported by smartmontools.
  • The operating system provides a SCSI pass-through I/O-control which works through its USB-layer.
  • SCSI support is implemented in the operating system interface of smartmontools.

Some recent USB bridges already support the vendor independent SAT (SCSI/ATA Translation, ANSI INCITS 431-2007) standard. Other USB bridges provide vendor specific ATA pass-through commands. The current version of smartmontools supports the following pass-through commands and USB bridges:

 

Command USB bridges smartctl option 48-bit ATAsupport Comment
SAT ATApass-through 12 and 16 various (Initio, Oxford, …) -d sat[,16]; -d sat,12 requires ‘-d sat[,16]’ Older Linux kernels may require ‘-d sat,12’
Cypress ATACB Cypress CY7C68300B/C (AT2LP), CY7C68310 (ISD-300LP) -d usbcypress[,CMD] No CY7C68300A (AT2) may not work.
JMicron ATApass-through JMicron JM20329, JM20335-39 -d usbjmicron[,x][,PORT] No:JM20327, Yes:JM20336/9 ‘-d usbjmicron,x’ enables 48-bit support
Sunplus ATApass-through Sunplus SPIF215/6, SPIF225/6 -d usbsunplus Yes

 

Smartmontools was successfully tested with many USB devices on several Platforms. This will never work on MacOS X unless someone adds SCSI pass-through support to this OS. If the USB ID can be obtained from the operating system, smartmontools also supports auto-detection of (the already tested) USB devices. Then it is not necessary to specify the ‘-d’ option. See the following table for details:

 

Platform …has SCSIpass-through smartmontools supports SCSI … supports USB … auto-detection … in smartd DEVICESCAN Comment
Linux Yes Yes YES YES YES See comment about SAT in above table
MacOS X No No No No No USB works with e.g. XP in a VM
FreeBSD Yes Yes YES YES YES
NetBSD Yes Yes YES No No
OpenBSD Yes Yes YES No No
Solaris Yes Yes YES No No
QNX ? No No No No
OS/2 ? No No No No
Windows NT/2K/XP/Vista Yes Yes YES YES No Auto-detection is slow due to the use of ‘wmic.exe’
Windows 9x/ME Yes Yes YES No No Requires ASPI driver

 

目前usb存储芯片:
1:支持标准SAT,使用ATA_12/16
2:支持自己的特殊命令如ATACB,类似于ATA_12/16,比如cypress公司的芯片。
在linux-2.6.28中的cypress-atacb.c中就是第二中方案:Support for emulating SAT (ata pass through) on devices based  on the Cypress USB/ATA bridge supporting ATACB.
  完成该功能的函数是cypress_atacb_passthrough。
  目前可以说hdparm和smartctl对usb硬盘不工作的原因是不同的。如果使用sdparm usb就会可以。但是smartctl是需要芯片支持的。所以在最新的smartctl中使用-d后面加参数。就是根据各自的芯片,填写标准ATA_16或者vendor spec comand。
  可以在smartctl的主页中查找支持哪些usb芯片。
usb芯片是否支持SAT或者特殊的如ATACB,需要看datasheet