ahci: Finally fixed TRIM support.

* The maximum number of allowed ranges is now taken into account.
* Still untested on my end, however, I tested the array copying code with
  a number of test data.
This commit is contained in:
Axel Dörfler
2014-02-05 00:20:31 +01:00
parent 63a30a4744
commit a7119b44f2
@@ -1,5 +1,5 @@
/* /*
* Copyright 2008-2013 Haiku, Inc. All rights reserved. * Copyright 2008-2014 Haiku, Inc. All rights reserved.
* Copyright 2007-2009, Marcus Overhagen. All rights reserved. * Copyright 2007-2009, Marcus Overhagen. All rights reserved.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
@@ -654,7 +654,6 @@ AHCIPort::ScsiInquiry(scsi_ccb* request)
TRACE("model number: %s\n", modelNumber); TRACE("model number: %s\n", modelNumber);
TRACE("serial number: %s\n", serialNumber); TRACE("serial number: %s\n", serialNumber);
TRACE("firmware rev.: %s\n", firmwareRev); TRACE("firmware rev.: %s\n", firmwareRev);
TRACE("trim support: %s\n", fTrimSupported ? "yes" : "no");
// There's not enough space to fit all of the data in. ATA has 40 bytes for // There's not enough space to fit all of the data in. ATA has 40 bytes for
// the model number, 20 for the serial number and another 8 for the // the model number, 20 for the serial number and another 8 for the
@@ -813,15 +812,37 @@ AHCIPort::ScsiReadWrite(scsi_ccb* request, uint64 lba, size_t sectorCount,
void void
AHCIPort::ScsiUnmap(scsi_ccb* request, scsi_unmap_parameter_list* unmapBlocks) AHCIPort::ScsiUnmap(scsi_ccb* request, scsi_unmap_parameter_list* unmapBlocks)
{ {
// Determine how many ranges we'll need // Determine how many blocks are supposed to be trimmed in total
// We assume that the SCSI unmap ranges cannot be merged together
uint32 scsiRangeCount = B_BENDIAN_TO_HOST_INT16( uint32 scsiRangeCount = B_BENDIAN_TO_HOST_INT16(
unmapBlocks->block_data_length) / sizeof(scsi_unmap_block_descriptor); unmapBlocks->block_data_length) / sizeof(scsi_unmap_block_descriptor);
uint32 scsiIndex = 0;
uint32 scsiLastBlocks = 0;
uint32 maxLBARangeCount = fMaxTrimRangeBlocks * 512 / 8;
// 512 bytes per range block, 8 bytes per range
// Split the SCSI ranges into ATA ranges as large as allowed.
// We assume that the SCSI unmap ranges cannot be merged together
while (scsiIndex < scsiRangeCount) {
// Determine how many LBA ranges we need for the next chunk
uint32 lbaRangeCount = 0; uint32 lbaRangeCount = 0;
for (uint32 i = 0; i < scsiRangeCount; i++) { for (uint32 i = scsiIndex; i < scsiRangeCount; i++) {
lbaRangeCount += ((uint32)B_BENDIAN_TO_HOST_INT32( uint32 scsiBlocks = B_BENDIAN_TO_HOST_INT32(
unmapBlocks->blocks[i].block_count) + 65534) / 65535; unmapBlocks->blocks[i].block_count);
if (scsiBlocks == 0)
break;
if (i == scsiIndex)
scsiBlocks -= scsiLastBlocks;
lbaRangeCount += (scsiBlocks + 65534) / 65535;
if (lbaRangeCount >= maxLBARangeCount) {
lbaRangeCount = maxLBARangeCount;
break;
} }
}
if (lbaRangeCount == 0)
break;
uint32 lbaRangesSize = lbaRangeCount * sizeof(uint64); uint32 lbaRangesSize = lbaRangeCount * sizeof(uint64);
uint64* lbaRanges = (uint64*)malloc(lbaRangesSize); uint64* lbaRanges = (uint64*)malloc(lbaRangesSize);
@@ -835,17 +856,33 @@ AHCIPort::ScsiUnmap(scsi_ccb* request, scsi_unmap_parameter_list* unmapBlocks)
MemoryDeleter deleter(lbaRanges); MemoryDeleter deleter(lbaRanges);
for (uint32 i = 0, scsiIndex = 0; scsiIndex < scsiRangeCount; scsiIndex++) { for (uint32 lbaIndex = 0;
uint64 lba = (uint64)B_BENDIAN_TO_HOST_INT64( scsiIndex < scsiRangeCount && lbaIndex < lbaRangeCount;) {
unmapBlocks->blocks[scsiIndex].lba); uint64 scsiOffset = B_BENDIAN_TO_HOST_INT64(
uint64 blocksLeft = (uint32)B_BENDIAN_TO_HOST_INT32( unmapBlocks->blocks[scsiIndex].lba) + scsiLastBlocks;
unmapBlocks->blocks[scsiIndex].block_count); uint32 scsiBlocksLeft = B_BENDIAN_TO_HOST_INT32(
while (blocksLeft > 0) { unmapBlocks->blocks[scsiIndex].block_count) - scsiLastBlocks;
uint16 blocks = blocksLeft > 65535 ? 65535 : (uint16)blocksLeft;
lbaRanges[i++] = B_HOST_TO_LENDIAN_INT64( if (scsiBlocksLeft == 0) {
((uint64)blocks << 48) | lba); // Ignore the rest of the ranges (they are empty)
lba += blocks; scsiIndex = scsiRangeCount;
blocksLeft -= blocks; break;
}
while (scsiBlocksLeft > 0 && lbaIndex < lbaRangeCount) {
uint16 blocks = scsiBlocksLeft > 65535
? 65535 : (uint16)scsiBlocksLeft;
lbaRanges[lbaIndex++] = B_HOST_TO_LENDIAN_INT64(
((uint64)blocks << 48) | scsiOffset);
scsiOffset += blocks;
scsiLastBlocks += blocks;
scsiBlocksLeft -= blocks;
}
if (scsiBlocksLeft == 0) {
scsiLastBlocks = 0;
scsiIndex++;
} }
} }
@@ -863,6 +900,7 @@ AHCIPort::ScsiUnmap(scsi_ccb* request, scsi_unmap_parameter_list* unmapBlocks)
request->subsys_status = SCSI_REQ_CMP_ERR; request->subsys_status = SCSI_REQ_CMP_ERR;
} else } else
request->subsys_status = SCSI_REQ_CMP; request->subsys_status = SCSI_REQ_CMP;
}
request->data_resid = 0; request->data_resid = 0;
request->device_status = SCSI_STATUS_GOOD; request->device_status = SCSI_STATUS_GOOD;