I try to issue a scsi read(10) and write(10) to a SSD. I use this example code as a reference/basic code.
This is my scsi read:
#define READ_REPLY_LEN 32
#define READ_CMDLEN 10
void scsi_read()
{
unsigned char Readbuffer[ SCSI_OFF + READ_REPLY_LEN ];
unsigned char cmdblk [ READ_CMDLEN ] =
{ 0x28, /* command */
0, /* lun/reserved */
0, /* lba */
0, /* lba */
0, /* lba */
0, /* lba */
0, /* reserved */
0, /* transfer length */
READ_REPLY_LEN, /* transfer length */
0 };/* reserved/flag/link */
memset(Readbuffer,0,sizeof(Readbuffer));
memcpy( cmd + SCSI_OFF, cmdblk, sizeof(cmdblk) );
/*
* +------------------+
* | struct sg_header | <- cmd
* +------------------+
* | copy of cmdblk | <- cmd + SCSI_OFF
* +------------------+
*/
if (handle_scsi_cmd(sizeof(cmdblk), 0, cmd,
sizeof(Readbuffer) - SCSI_OFF, Readbuffer )) {
fprintf( stderr, "read failed\n" );
exit(2);
}
hex_dump(Readbuffer,sizeof(Readbuffer));
}
And this is my scsi write:
void scsi_write ( void )
{
unsigned char Writebuffer[SCSI_OFF];
unsigned char cmdblk [] =
{ 0x2A, /* 0: command */
0, /* 1: lun/reserved */
0, /* 2: LBA */
0, /* 3: LBA */
0, /* 4: LBA */
0, /* 5: LBA */
0, /* 6: reserved */
0, /* 7: transfer length */
0, /* 8: transfer length */
0 };/* 9: control */
memset(Writebuffer,0,sizeof(Writebuffer));
memcpy( cmd + SCSI_OFF, cmdblk, sizeof(cmdblk) );
cmd[SCSI_OFF+sizeof(cmdblk)+0] = 'A';
cmd[SCSI_OFF+sizeof(cmdblk)+1] = 'b';
cmd[SCSI_OFF+sizeof(cmdblk)+2] = 'c';
cmd[SCSI_OFF+sizeof(cmdblk)+3] = 'd';
cmd[SCSI_OFF+sizeof(cmdblk)+4] = 'e';
cmd[SCSI_OFF+sizeof(cmdblk)+5] = 'f';
cmd[SCSI_OFF+sizeof(cmdblk)+6] = 'g';
cmd[SCSI_OFF+sizeof(cmdblk)+7] = 0;
/*
* +------------------+
* | struct sg_header | <- cmd
* +------------------+
* | copy of cmdblk | <- cmd + SCSI_OFF
* +------------------+
* | data to write |
* +------------------+
*/
if (handle_scsi_cmd(sizeof(cmdblk), 8, cmd,
sizeof(Writebuffer) - SCSI_OFF, Writebuffer )) {
fprintf( stderr, "write failed\n" );
exit(2);
}
}
In the following example I do
And I print the hexdumps of the data which is written (scsi write) and what is read (scsi read)
Read(10)
[0000] 00 00 00 44 00 00 00 44 00 00 00 00 00 00 00 00 ...D...D ........
[0010] 00 2C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........
[0020] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........
[0030] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........
[0040] 00 00 00 00 ....
Write(10):
[0000] 00 00 00 00 00 00 00 24 00 00 00 00 00 00 00 00 ........ ........
[0010] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........
[0020] 00 00 00 00 2A 00 00 00 00 00 00 00 00 00 41 62 ........ ......Ab
[0030] 63 64 65 66 67 00 cdefg.
Read(10):
[0000] 00 00 00 44 00 00 00 44 00 00 00 00 00 00 00 00 ...D...D ........
[0010] 04 00 20 00 70 00 02 00 00 00 00 0A 00 00 00 00 ....p... ........
[0020] 04 00 00 00 41 62 63 64 65 66 67 00 00 00 00 00 ....Abcd efg.....
[0030] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........
[0040] 00 00 00 00 ....
fter running the three commands again, I should read Abcdefg
with the first read. Right? But running them again changes nothing. You could now assume, that the memory I use has still the data from previous funcions, but I get the same result even though I run memset(Readbuff,0,sizeof(Readbuff))
before the sys_read()
happens.
I assumed, that the LBA I try to write is maybe forbidden to write, and I read the cache. But interating over LBA Adresses from 0x00-0xFF changes nothing - That means, I read the same data (Abcdefg
).
Do you know an example implementation of scsi read or writes with the scsi generic interface?
WRITE SAME is basically a SCSI operation that tells the storage to write a certain pattern, in this case zeros. So instead of ESXi issuing possibly terabytes of zeros, ESXi just issues a few hundred or thousand small WRITE SAME I/Os and the array takes care of the zeroing.
Description. The normal action of the SCSI READ CAPACITY command is to fetch the number of blocks (and block size) from the DEVICE. The SCSI READ CAPACITY command (both 10 and 16 byte cdbs) actually yield the block address of the last block and the block size.
SCSI devices under Linux are often named to help the user identify the device. For example, the first SCSI CD-ROM is /dev/scd0. SCSI disks are labeled /dev/sda, /dev/sdb, /dev/sdc, etc. Once device initialization is complete, the Linux SCSI disk driver interfaces (sd) send only SCSI READ and WRITE commands.
The SCSI command architecture was originally defined for parallel SCSI buses but has been carried forward with minimal change for use with Fibre Channel, iSCSI, Serial Attached SCSI, and other transport layers. In the SCSI protocol, the initiator sends a SCSI command information unit to the target device.
In SCSI, the units of the LBA and the transfer length are in blocks, sometimes called sectors. This is almost always 512 bytes. So, you can't read or write just 32 bytes. At a minimum, you'll have to do 512 bytes == one block. This one point is most of what you need to fix.
Your transfer length is zero in your scsi_write implementation, so it's not actually going to write any data.
You should use different buffers for the CDB and the write/read data. I suspect that confusion about these buffers is leading your implementation to write past the end of one of your statically-allocated arrays and over your ReadBuffer. Run it under valgrind and see what shows up.
And lastly, a lot could go wrong in whatever is in handle_scsi_cmd. It can be tricky to set up the data transfer... in particular, make sure you're straight on which way the data is going in the I/O header's dxfer_direction: SG_DXFER_TO_DEV for write, SG_DXFER_FROM_DEV for read.
Check out this example of how to do a read(16). This is more along the lines of what you're trying to accomplish.
https://github.com/hreinecke/sg3_utils/blob/master/examples/sg_simple16.c
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With