原來是直接用 /dev/sdN , 但發現這對以逅的專案會有影響, 所以就改用/dev/sgN 方式
而且習慣看 SCSI Sense Code, 除錯也比較容易.
過去十幾年來都在 Windows 和 NIOS-II 下對 Device直接下 SCSI Command
終於換到 Linux 平台來.
Some examples of storage device names of the supported operating systems
//===========================================
方式一:
一般都是利用 sg3-utils核心
# apt-get install sg3-utils
也就是開啟 "/dev/sgN" 方法
http://sg3-utils.sourcearchive.com/
sg3_utils-1.32 Example
參考下面這幾篇
T10 Working Drafts
這個 T10很多檔案都已經改為付費購買. 其實只要只知道檔案名稱, 還是在Google搜尋下載得到.
Seagate SCSI Commands Reference Manual
Seagate SCSI Commands Reference Manual April 2010
wiki SCSI command
http://www.tldp.org/HOWTO/SCSI-Generic-HOWTO/
Appendix C. Programming example
T10 Working Drafts
這個 T10很多檔案都已經改為付費購買. 其實只要只知道檔案名稱, 還是在Google搜尋下載得到.
Seagate SCSI Commands Reference Manual
Seagate SCSI Commands Reference Manual April 2010
wiki SCSI command
Appendix C. Programming example
ioctrl-using-scsi-pass-through
linux scsi generic howto學習筆記
typedef struct sg_io_hdr { int interface_id; /* [i] 'S' for SCSI generic (required) */ int dxfer_direction; /* [i] data transfer direction */ SG_DXFER_NONE /* e.g. a SCSI Test Unit Ready command */ SG_DXFER_TO_DEV /* e.g. a SCSI WRITE command */ SG_DXFER_FROM_DEV /* e.g. a SCSI READ command */ TestUnitReady => SG_DXFER_NONE ReadCapacity10 => SG_DXFER_FROM_DEV Read10 => SG_DXFER_FROM_DEV Write10 => SG_DXFER_TO_DEV unsigned char cmd_len; /* [i] SCSI command length ( <= 16 bytes) */ ReadCapacity10 => cmd_len = 10 ReadCapacity16 => cmd_len = 16 Read10 => cmd_len = 10 Write10 => cmd_len = 10 Write16 => cmd_len = 16 ... } sg_io_hdr_t;
//===========================================
使用 SG_IO 出現 ENOMEM
表示要求傳輸的記憶體太大
經測試只傳輸到 512byte * 9181sector
一呼叫 512byte * 9182sector 就會發生 ENOMEM
經查 Google 這是Linux的核心限制
scsi sg ioctl要求向 blk-mq 取得一塊和要求傳輸大小的記憶體區塊失敗.
對 Storage一次使用大區塊讀取, 並不一定會比較穩定快速
因為不同的 Storage, 其晶片的特性不同
讀取太大塊, 速度反而會變慢, 變錯.
這個數字是多少, 就依賴經驗值, 必竟Storage的種類範圍太廣.
下面只是參考, 別太認真.
void ReadWrite_Test() { int nI, nJ; TLBA nReadLba, nWriteLba; DWORD nSectorSize, nReadLen, nWriteLen; DWORD nReadCrc32, nWriteCrc32, nWriteSectorCrc32; DWORD nReadBufLen, nWriteBufLen; UCHAR *pReadBuf = new UCHAR[16*1024*1024]; UCHAR *pWriteBuf = new UCHAR[16*1024*1024]; nSectorSize = 512; nWriteLba = 0; nWriteLen = 8192; memset( pWriteBuf, 0, 16*1024*1024); /* 初始化 Writer Data Buffer */ for( nI =0; nI < nWriteLen; nI++) { /* 中間填入 序號Index */ for( nJ = 4; nJ < nSectorSize-sizeof(DWORD); nJ++) pWriteBuf[nI*nSectorSize+nJ] = nJ%255; /* 前4個Byte 是 Sector Index */ pWriteBuf[nI*nSectorSize] = (UCHAR)((nWriteLba+nI) >> 24); // MSB pWriteBuf[nI*nSectorSize+1] = (UCHAR)((nWriteLba+nI) >> 16); pWriteBuf[nI*nSectorSize+2] = (UCHAR)((nWriteLba+nI) >> 8); pWriteBuf[nI*nSectorSize+3] = (UCHAR)((nWriteLba+nI)); // LSB nWriteSectorCrc32 = Get_CRC32( pWriteBuf+(nI*nSectorSize), nSectorSize-sizeof(DWORD)); /* 後 2個Byte 是 Sector Index */ pWriteBuf[nI*nSectorSize+nSectorSize-sizeof(DWORD)] = (UCHAR)(nWriteSectorCrc32 >> 24); // MSB pWriteBuf[nI*nSectorSize+nSectorSize-sizeof(DWORD)+1] = (UCHAR)(nWriteSectorCrc32 >> 16); pWriteBuf[nI*nSectorSize+nSectorSize-sizeof(DWORD)+2] = (UCHAR)(nWriteSectorCrc32 >> 8); pWriteBuf[nI*nSectorSize+nSectorSize-sizeof(DWORD)+3] = (UCHAR)(nWriteSectorCrc32); // LSB } nWriteBufLen = nSectorSize*nWriteLen; /* 整個 Write Data Buffer 的 CRC-32 */ nWriteCrc32 = Get_CRC32( pWriteBuf, nWriteBufLen); if( Write16( nWriteLba, nSectorSize, nWriteLen, pWriteBuf) == true) { memset( pReadBuf, 0, 4096); memset( pReadBuf, 0, 16*1024*1024); nReadLen = nWriteLen; nReadLba = nWriteLba; nReadBufLen = nSectorSize * nReadLen; if( Read16( nReadLba, nSectorSize, nReadLen, pReadBuf)) { /* 驗證讀回來的資料是否正確 */ nReadCrc32 = Get_CRC32( pReadBuf, nReadBufLen); if( nReadCrc32 != nWriteCrc32) nprintf( "Read/Write Test CRAC-32 Failed!"); if( memcmp( pReadBuf, pWriteBuf, nReadBufLen) == 0) nprintf( "Read/Write Test Compare Ok!"); else nprintf( "Read/Write Test Compare Failed!"); } } // Write12( nWriteLba, nSectorSize, nWriteLen, pWriteBuf); // Write16( nWriteLba, nSectorSize, nWriteLen, pWriteBuf); if( pReadBuf) delete[] pReadBuf; if( pWriteBuf) delete[] pWriteBuf; }
//===========================================
# lsscsi
[0:0:0:0] disk SEAGATE ST3500414SS 0006 /dev/sdb
[0:0:1:0] disk SEAGATE ST3500414SS 0006 /dev/sdc
[1:0:0:0] disk ATA ST1000DM003-9YN1 CC4B /dev/sda
# lsscsi --transport
[6:0:0:0] disk sas:0x5000c500418fac39 /dev/sdb
[6:0:1:0] disk sas:0x5000c50041ea5265 /dev/sdc
/sys/class/scsi_generic/sg2/device/block/sdc
/sys/class/scsi_generic/sg2/device/scsi_device/6:0:1:0
/sys/class/scsi_generic/sg2/device/scsi_device/6:0:1:0/sas_address
可以交叉驗證, 就可以知道 /dev/sg3 是那一顆HDD
要注意 Hot-Plug 的狀態下, Target 會改變, /sys/class/scsi_generic/sgN的目錄會自動消失, 也會自動出現.
所以用udevadm 來監控 (但監控這幾個目錄也是可以, 而且比較簡單)
How To Compiler udev
$ apt-get install sg3-utils
$ sg_scan -i -x
//===========================================
sg3_utils-1.32 Example
如果是 SATA HDD交叉驗證, 請看另一篇取得 SATA PHY Port Index 的相關文章
//===========================================
關於超過 2TB 容量的 Device
請使用 ReadCapacity16, Read16, Write16 這幾組指令
ReadCapacity10 有的會直接失敗, 有的 Device會回傳四個 0xFF
這時就轉換為 ReadCapacity16指令ReadCapacity回傳的 Capacity是最後一個 LBA位置
所以實際容量為 Capacity + 1
還有一種情況是造假的 Capacity, 也就是回傳的 Capacity都是假的
這時要實際的去 Write & Read Last LBA, 進行資料驗證
記得有一次, 遇到一個Device, 一次 Read/Write 16MB, 回傳都是正常
讀寫的資料只有前面 4MB 資料是正確, 後面 12MB資料都是晶片亂編出來.
其實最常見的是, 寫入時, Device回傳 Write OK, 但確沒有真正的寫入.
//===========================================
方式二:
使用 /dev/bsg/1:0:0:0
http://sg.danny.cz/sg/p/libsgutils2-2_1.42-0.1_i386.deb
http://www.spinics.net/lists/linux-scsi/msg53920.html
sg_tst_context
http://sg.danny.cz/sg/p/libsgutils2-2_1.42-0.1_i386.deb
http://www.spinics.net/lists/linux-scsi/msg53920.html
sg_tst_context
//===========================================
方式三:
這方法只適用 LSI 控制卡上的 Device
Linux LSI SAS 9211-8i Utility
Source Code中一個 doScsiIo(), doMptCommand() 的函式
Source Code中一個 doScsiIo(), doMptCommand() 的函式
利用 LSI 驅動程式提供的 mpt_ioctl_command
直接對 LSI CHIP 下達 SCSI Command
LSI Util 原始程式是一個 SCSI Command就 Open Device Handle一次
速度會比較慢, 最好要改寫一些程式.
//===========================================
直接對 LSI CHIP 下達 SCSI Command
LSI Util 原始程式是一個 SCSI Command就 Open Device Handle一次
速度會比較慢, 最好要改寫一些程式.
//===========================================
關於 Linux 下直接使用 ATA Command
Linux ioctl command for bypassing ATA command(Identify) - Sample Code
這是直接使用 ioctl(device, HDIO_DRIVE_CMD, buf);
這是直接使用 ioctl(device, HDIO_DRIVE_CMD, buf);
真是方便, 以前在 NIOS 中, 程式寫了一大堆程式才做出來.
但還是建議使用 Hdparm 的 sgio方式
另一個是請查詢 hdparm 的原始程式
https://sourceforge.net/projects/hdparm/
hdparm.c / sgio.c / sgio.h
但還是建議使用 Hdparm 的 sgio方式
另一個是請查詢 hdparm 的原始程式
https://sourceforge.net/projects/hdparm/
hdparm.c / sgio.c / sgio.h
//===========================================
沒有留言:
張貼留言