scsi_cmnd data_buffer checksum
I am writing a checksum calculation of scsi_cmnd data buffer in the driver.
I calculate the checksum of the scsi_cmd data buffer(request_buffer) in driver queuecommand.
Now when the command is completed from the hardware and before driver sends it back to mid-layer, I calculate the checksum again of the same scsi_cmd data_buffer again.
Sometimes the checksums don't match. I mean somehow looks like OS changed the scsi_cmd data_buffer(request_buffer) in the meantime when driver is working on the command. I print the address of the scsi_cmd data_buffer (virtual address) and its same and the contents of the buffer is also same during both the calculations.
Can this happen?
Quoted 11 lines · Anil kumar
I am writing a checksum calculation of
scsi_cmnddata buffer in the driver.I calculate the checksum of the
scsi_cmddatabuffer(request_buffer)in driver queuecommand.Now when the command is completed from the hardware and before driver sends it back to mid-layer, I calculate the checksum again of the same
scsi_cmddata_bufferagain.Sometimes the checksums don't match. I mean somehow looks like OS changed the
scsi_cmddata_buffer(request_buffer)in the meantime when driver is working on the command. I print the address of thescsi_cmddata_buffer(virtual address) and its same and the contents of the buffer is also same during both the calculations.Can this happen?
Yes. While a write I/O is being processed in Linux or in flight, the data buffers can change. This causes problems for other checksums as well; i ran into this when looking at the DIF/DIX checksums for SCSI commands.
At the moment, this problem can be avoided e.g. by running only direct I/O on the xfs filesystem. There have been discussions about this, a short summary is here, look for "stable pages":
http://lwn.net/Articles/399148/
Christof
Hi Christof,
Thanks for the response.
I am running mkfs.ext3 command.
I am doing the following in the driver for write(10):
Queuecommand:
sg = scsi_sglist(cmd->scsi_cmd);cmd->write_buf = (u8 *)(kmap_atomic(sg->page, KM_IRQ0) + sg->offset); Calculate checksum for write_buf
Write Done:
Calculate checksum for cmd->write_buf
and checksums don't match. I am wondering how come OS changed the cmd->write_buf when I have not even unmapped the buffer. Is filesystem changing this cmd->write_buf pages when driver/HW is working on it?
Is there anyway I can avoid this. How about if we allocate a local buffer(kmalloc/pci_alloc_consistent) and memcpy kmap_atomic to that local buffer and then calculate checksum on that local buffer. Will this help?
--Anil
Quoted 45 lines · Christof Schmitt
From: Christof Schmitt <[email protected]>
Subject: Re:scsi_cmnddata_bufferchecksum
To: "Anil kumar" <[email protected]>
Cc: [email protected], [email protected]
Date: Thursday, September 9, 2010, 4:00 AMQuoted 2 lines · Anil kumar
I am writing a checksum calculation of
scsi_cmnddatabuffer in the driver.
Quoted 2 lines
I calculate the checksum of the
scsi_cmddata
buffer(request_buffer)in driver queuecommand.Quoted 2 lines
Now when the command is completed from the hardware
and before driver sends it back to mid-layer, I calculate the checksum again of the same
scsi_cmddata_bufferagain.Quoted 2 lines
Sometimes the checksums don't match. I mean somehow
looks like OS changed the
scsi_cmddata_buffer(request_buffer)in the meantime when driver is working on the command.Quoted 1 line
I print the address of the
scsi_cmddata_buffer(virtual address) and its same and the contents of the buffer is also same during both the calculations.
Quoted 2 lines
Can this happen?
Yes. While a write I/O is being processed in Linux or in flight, the
data buffers can change. This causes problems for other checksums as
well; i ran into this when looking at the DIF/DIX checksums for SCSI
commands.At the moment, this problem can be avoided e.g. by running only direct
I/O on the xfs filesystem. There have been discussions about this, a
short summary is here, look for "stable pages":
http://lwn.net/Articles/399148/Christof
Quoted 10 lines · Anil kumar
Hi Christof,
Thanks for the response.
I am running mkfs.ext3 command.
I am doing the following in the driver for write(10):
Queuecommand:
Quoted 8 lines
sg =
scsi_sglist(cmd->scsi_cmd);cmd->write_buf= (u8 *)(kmap_atomic(sg->page,KM_IRQ0) +sg->offset); Calculate checksum forwrite_bufWrite Done:
Calculate checksum forcmd->write_bufand checksums don't match. I am wondering how come OS changed the
cmd->write_bufwhen I have not even unmapped the buffer. Is filesystem changing thiscmd->write_bufpages when driver/HW is working on it?
Yes, the driver has direct access to the data. Usually, the data is not copied for I/O requests. The driver gets one sg list that points to the data pages of file system (or whatever the data source is). When the filesystem decides to change the data, this single data buffer is changed.
Quoted 1 line
Is there anyway I can avoid this. How about if we allocate a local
buffer(kmalloc/pci_alloc_consistent)and memcpykmap_atomicto that local buffer and then calculate checksum on that local buffer. Will this help?
Sure, you can create copies of data buffers in the driver, calculate the checksum of the copy and submit the data copy with the checksum to the hardware controller. This is usually not done for performance reasons, and you probably should keep a mempool to be able to issue I/Os when memory is low.
Christof
I quickly tried copying the data buffer to local buffers as follows:
In Queuecommand:
cmd->local_write_buf = pci_alloc_consistent(...); cmd->write_buf = (u8 *)(kmap_atomic(sg->page, KM_IRQ0) + sg->offset);
memcpy(cmd->local_write_buf, cmd->write_buf, scsibufflen(scsi_cmnd));
Now I calculate checksums of cmd->write_buf and my local cmd->local_write_buf
and the checksum fails. Am I doing something wrong here?
Quoted 57 lines · Christof Schmitt
From: Christof Schmitt <[email protected]>
Subject: Re:scsi_cmnddata_bufferchecksum
To: "Anil kumar" <[email protected]>
Cc: [email protected], [email protected]
Date: Thursday, September 9, 2010, 4:51 AMQuoted 4 lines · Anil kumar
Hi Christof,
Thanks for the response.
Quoted 8 lines
I am running mkfs.ext3 command.
I am doing the following in the driver for write(10):
Queuecommand:
sg =
scsi_sglist(cmd->scsi_cmd);cmd->write_buf= (u8 *)(kmap_atomic(sg->page,
KM_IRQ0) +sg->offset);Quoted 6 lines
Calculate checksum for
write_bufWrite Done:
Calculate checksum forcmd->write_bufand checksums don't match. I am wondering how come OS
changed the
cmd->write_bufwhen I have not even unmapped the buffer. Is filesystem changing thiscmd->write_bufpages when driver/HW is working on it?Yes, the driver has direct access to the data. Usually, the data is
not copied for I/O requests. The driver gets one sg list that points
to the data pages of file system (or whatever the data source is).
When the filesystem decides to change the data, this single data
buffer is changed.Quoted 1 line
Is there anyway I can avoid this. How about if we
allocate a local
buffer(kmalloc/pci_alloc_consistent)and memcpykmap_atomicto that local buffer and then calculate checksum on that local buffer. Will this help?Sure, you can create copies of data buffers in the driver, calculate
the checksum of the copy and submit the data copy with the checksum to
the hardware controller. This is usually not done for performance
reasons, and you probably should keep a mempool to be able to issue
I/Os when memory is low.Christof
Quoted 4 lines · Anil kumar
I quickly tried copying the data buffer to local buffers as follows:
In Queuecommand:
Quoted 8 lines
cmd->local_write_buf=pci_alloc_consistent(...);cmd->write_buf= (u8 *)(kmap_atomic(sg->page,KM_IRQ0) +sg->offset);
memcpy(cmd->local_write_buf, cmd->write_buf, scsibufflen(scsi_cmnd));Now I calculate checksums of
cmd->write_bufand my localcmd->local_write_bufand the checksum fails. Am I doing something wrong here?
Are you only copying one element of the sg list? Are you sure that there is only one element? If not, you have to use something like scsi_kmap_atomic_sg and create a copy of each element (maybe use scsi_for_each_sg).
Or maybe there is another problem... hard to guess from the distance.
Christof
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
"Anil" == Anil kumar <[email protected]> writes:
Anil> cmd-> write_buf = (u8 *)(kmap_atomic(sg->page, KM_IRQ0) + sg->offset); Anil> memcpy(cmd->local_write_buf, cmd->write_buf, cmd->scsibufflen(scsi_cmnd));
You need to walk the scatterlist. You can't assume that the pages are contiguous in memory.
But why are you doing this checksumming stuff manually when the existing DIX/DIF code can do it for you?
Signature · 1 line
Martin K. Petersen Oracle Linux Engineering
Martin,
Thanks. I will go thru the DIF document and code.
But I had a quick question, isn't that drive has to support "protection enable" also. Also, does Controller FW need to do anything extra to send DIF block down to drive. Ideally we will be sending 512 byte blocks down to drive.
-Anil
Quoted 26 lines · Martin K. Petersen
From: Martin K. Petersen <[email protected]>
Subject: Re:scsi_cmnddata_bufferchecksum
To: "Anil kumar" <[email protected]>
Cc: "Christof Schmitt" <[email protected]>, [email protected], [email protected]
Date: Thursday, September 9, 2010, 9:23 AMQuoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
"Anil" == Anil
kumar <[email protected]>
writes:Anil> cmd->
write_buf= (u8 *)(kmap_atomic(sg->page,KM_IRQ0) +sg->offset); Anil>memcpy(cmd->local_write_buf, cmd->write_buf, cmd->scsibufflen(scsi_cmnd));You need to walk the scatterlist. You can't assume that the pages are
contiguous in memory.But why are you doing this checksumming stuff manually when the existing
DIX/DIF code can do it for you?Signature · 2 lines
Martin K. Petersen Oracle Linux Engineering
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
"Anil" == Anil kumar <[email protected]> writes:
Anil> But I had a quick question, isn't that drive has to support Anil> "protection enable" also.
Just register DIX Type 0 in the host's protection mask. That'll enable protected transfer to the HBA only.
Anil> Also, does Controller FW need to do anything extra to send DIF Anil> block down to drive. Ideally we will be sending 512 byte blocks Anil> down to drive.
http://oss.oracle.com/~mkp/docs/dix-draft.pdf
Signature · 1 line
Martin K. Petersen Oracle Linux Engineering