124593e2cSAxel Dörfler /* 2*8b1d35bdSDavid Sebek * Copyright 2021 David Sebek, dasebek@gmail.com 3*8b1d35bdSDavid Sebek * Copyright 2004-2013 Haiku, Inc. 4*8b1d35bdSDavid Sebek * Copyright 2002-2003 Thomas Kurschel 5*8b1d35bdSDavid Sebek * All rights reserved. Distributed under the terms of the MIT License. 624593e2cSAxel Dörfler */ 724593e2cSAxel Dörfler 8a1e8da41SAxel Dörfler 9960c56aeSAxel Dörfler //! Handling of block device 1024593e2cSAxel Dörfler 1124593e2cSAxel Dörfler 1224593e2cSAxel Dörfler #include <string.h> 1324593e2cSAxel Dörfler 1499086aa3SAxel Dörfler #include <AutoDeleter.h> 1599086aa3SAxel Dörfler 1699086aa3SAxel Dörfler #include "scsi_periph_int.h" 1799086aa3SAxel Dörfler 1824593e2cSAxel Dörfler 19*8b1d35bdSDavid Sebek // UNMAP command limits 20*8b1d35bdSDavid Sebek #define UNMAP_MAX_LBA_VALUE UINT64_MAX 21*8b1d35bdSDavid Sebek #define UNMAP_MAX_BLOCK_COUNT_VALUE UINT32_MAX 22*8b1d35bdSDavid Sebek #define UNMAP_MAX_DESCRIPTORS 4095 23*8b1d35bdSDavid Sebek // Limit imposed by the UNMAP command structure 24*8b1d35bdSDavid Sebek #define UNMAP_DEFAULT_DESCRIPTORS 255 25*8b1d35bdSDavid Sebek // Reasonable default (?) when not specified by the device 26*8b1d35bdSDavid Sebek 27*8b1d35bdSDavid Sebek // WRITE SAME (16) command limits 28*8b1d35bdSDavid Sebek #define WS16_MAX_LBA_VALUE UINT64_MAX 29*8b1d35bdSDavid Sebek #define WS16_MAX_BLOCK_COUNT_VALUE UINT32_MAX 30*8b1d35bdSDavid Sebek 31*8b1d35bdSDavid Sebek // WRITE SAME (10) command limits 32*8b1d35bdSDavid Sebek #define WS10_MAX_LBA_VALUE UINT32_MAX 33*8b1d35bdSDavid Sebek #define WS10_MAX_BLOCK_COUNT_VALUE UINT16_MAX 34*8b1d35bdSDavid Sebek 35*8b1d35bdSDavid Sebek 36*8b1d35bdSDavid Sebek struct CapacityInfo { 37*8b1d35bdSDavid Sebek // Result of the READ CAPACITY command 38*8b1d35bdSDavid Sebek bool capacityFilled; 39*8b1d35bdSDavid Sebek uint64 lastLba; 40*8b1d35bdSDavid Sebek uint32 blockSize; 41*8b1d35bdSDavid Sebek 42*8b1d35bdSDavid Sebek // Provisioining info from READ CAPACITY 43*8b1d35bdSDavid Sebek bool provisioningFilled; 44*8b1d35bdSDavid Sebek bool lbpme; 45*8b1d35bdSDavid Sebek bool lbprz; 46*8b1d35bdSDavid Sebek }; 47*8b1d35bdSDavid Sebek 48*8b1d35bdSDavid Sebek 49*8b1d35bdSDavid Sebek struct UnmapSupport { 50*8b1d35bdSDavid Sebek // UNMAP commands supported by the device 51*8b1d35bdSDavid Sebek bool commandSupportFilled; 52*8b1d35bdSDavid Sebek bool unmapSupported; 53*8b1d35bdSDavid Sebek bool ws16Supported; 54*8b1d35bdSDavid Sebek bool ws10Supported; 55*8b1d35bdSDavid Sebek 56*8b1d35bdSDavid Sebek // Block limits for UNMAP commands 57*8b1d35bdSDavid Sebek bool blockLimitsFilled; 58*8b1d35bdSDavid Sebek uint32 maxUnmapLbaCount; 59*8b1d35bdSDavid Sebek uint32 maxUnmapDescriptorCount; 60*8b1d35bdSDavid Sebek uint64 maxWritesameLength; 61*8b1d35bdSDavid Sebek }; 62*8b1d35bdSDavid Sebek 63*8b1d35bdSDavid Sebek 64*8b1d35bdSDavid Sebek static bool 65*8b1d35bdSDavid Sebek prefer_read_capacity_16(scsi_periph_device_info* device) 66*8b1d35bdSDavid Sebek { 67*8b1d35bdSDavid Sebek const scsi_res_inquiry* inquiryData = NULL; 68*8b1d35bdSDavid Sebek size_t inquiryDataLength; 69*8b1d35bdSDavid Sebek 70*8b1d35bdSDavid Sebek if (gDeviceManager->get_attr_raw(device->node, SCSI_DEVICE_INQUIRY_ITEM, 71*8b1d35bdSDavid Sebek (const void**)&inquiryData, &inquiryDataLength, true) != B_OK 72*8b1d35bdSDavid Sebek || inquiryDataLength != sizeof(*inquiryData)) { 73*8b1d35bdSDavid Sebek return false; 74*8b1d35bdSDavid Sebek } 75*8b1d35bdSDavid Sebek 76*8b1d35bdSDavid Sebek if (inquiryData->protect) 77*8b1d35bdSDavid Sebek return true; 78*8b1d35bdSDavid Sebek 79*8b1d35bdSDavid Sebek if (inquiryData->ansi_version > 0x04 /* SPC-2 */) 80*8b1d35bdSDavid Sebek return true; 81*8b1d35bdSDavid Sebek 82*8b1d35bdSDavid Sebek return false; 83*8b1d35bdSDavid Sebek } 84*8b1d35bdSDavid Sebek 85*8b1d35bdSDavid Sebek 86*8b1d35bdSDavid Sebek static bool 87*8b1d35bdSDavid Sebek vpd_pages_supported(scsi_periph_device_info* device) 88*8b1d35bdSDavid Sebek { 89*8b1d35bdSDavid Sebek const scsi_res_inquiry* inquiryData = NULL; 90*8b1d35bdSDavid Sebek size_t inquiryDataLength; 91*8b1d35bdSDavid Sebek 92*8b1d35bdSDavid Sebek if (gDeviceManager->get_attr_raw(device->node, SCSI_DEVICE_INQUIRY_ITEM, 93*8b1d35bdSDavid Sebek (const void**)&inquiryData, &inquiryDataLength, true) != B_OK 94*8b1d35bdSDavid Sebek || inquiryDataLength != sizeof(*inquiryData)) { 95*8b1d35bdSDavid Sebek return false; 96*8b1d35bdSDavid Sebek } 97*8b1d35bdSDavid Sebek 98*8b1d35bdSDavid Sebek if (inquiryData->ansi_version >= 0x04 /* SPC-2 */) 99*8b1d35bdSDavid Sebek return true; 100*8b1d35bdSDavid Sebek 101*8b1d35bdSDavid Sebek return false; 102*8b1d35bdSDavid Sebek } 103*8b1d35bdSDavid Sebek 104*8b1d35bdSDavid Sebek 105*8b1d35bdSDavid Sebek static status_t 106*8b1d35bdSDavid Sebek read_capacity_10(scsi_periph_device_info* device, scsi_ccb* request, 107*8b1d35bdSDavid Sebek CapacityInfo* capacityInfo) 108*8b1d35bdSDavid Sebek { 109*8b1d35bdSDavid Sebek capacityInfo->capacityFilled = false; 110*8b1d35bdSDavid Sebek capacityInfo->provisioningFilled = false; 111*8b1d35bdSDavid Sebek 112*8b1d35bdSDavid Sebek scsi_res_read_capacity capacityResult; 113*8b1d35bdSDavid Sebek memset(&capacityResult, 0, sizeof(capacityResult)); 114*8b1d35bdSDavid Sebek 115*8b1d35bdSDavid Sebek scsi_cmd_read_capacity* cmd = (scsi_cmd_read_capacity*)request->cdb; 116*8b1d35bdSDavid Sebek memset(cmd, 0, sizeof(*cmd)); 117*8b1d35bdSDavid Sebek cmd->opcode = SCSI_OP_READ_CAPACITY; 118*8b1d35bdSDavid Sebek // we don't set PMI (partial medium indicator) as we want the whole capacity; 119*8b1d35bdSDavid Sebek // in this case, all other parameters must be zero 120*8b1d35bdSDavid Sebek 121*8b1d35bdSDavid Sebek request->flags = SCSI_DIR_IN; 122*8b1d35bdSDavid Sebek request->cdb_length = sizeof(*cmd); 123*8b1d35bdSDavid Sebek request->sort = -1; 124*8b1d35bdSDavid Sebek request->timeout = device->std_timeout; 125*8b1d35bdSDavid Sebek 126*8b1d35bdSDavid Sebek request->data = (uint8*)&capacityResult; 127*8b1d35bdSDavid Sebek request->data_length = sizeof(capacityResult); 128*8b1d35bdSDavid Sebek request->sg_list = NULL; 129*8b1d35bdSDavid Sebek 130*8b1d35bdSDavid Sebek status_t res = periph_safe_exec(device, request); 131*8b1d35bdSDavid Sebek 132*8b1d35bdSDavid Sebek if (res == B_OK && request->data_resid == 0) { 133*8b1d35bdSDavid Sebek capacityInfo->capacityFilled = true; 134*8b1d35bdSDavid Sebek capacityInfo->lastLba 135*8b1d35bdSDavid Sebek = (uint32)B_BENDIAN_TO_HOST_INT32(capacityResult.lba); 136*8b1d35bdSDavid Sebek capacityInfo->blockSize 137*8b1d35bdSDavid Sebek = B_BENDIAN_TO_HOST_INT32(capacityResult.block_size); 138*8b1d35bdSDavid Sebek } 139*8b1d35bdSDavid Sebek 140*8b1d35bdSDavid Sebek return res; 141*8b1d35bdSDavid Sebek } 142*8b1d35bdSDavid Sebek 143*8b1d35bdSDavid Sebek 144*8b1d35bdSDavid Sebek static status_t 145*8b1d35bdSDavid Sebek read_capacity_16(scsi_periph_device_info* device, scsi_ccb* request, 146*8b1d35bdSDavid Sebek CapacityInfo* capacityInfo) 147*8b1d35bdSDavid Sebek { 148*8b1d35bdSDavid Sebek capacityInfo->capacityFilled = false; 149*8b1d35bdSDavid Sebek capacityInfo->provisioningFilled = false; 150*8b1d35bdSDavid Sebek 151*8b1d35bdSDavid Sebek scsi_res_read_capacity_long capacityLongResult; 152*8b1d35bdSDavid Sebek memset(&capacityLongResult, 0, sizeof(capacityLongResult)); 153*8b1d35bdSDavid Sebek 154*8b1d35bdSDavid Sebek scsi_cmd_read_capacity_long* cmd 155*8b1d35bdSDavid Sebek = (scsi_cmd_read_capacity_long*)request->cdb; 156*8b1d35bdSDavid Sebek memset(cmd, 0, sizeof(*cmd)); 157*8b1d35bdSDavid Sebek cmd->opcode = SCSI_OP_SERVICE_ACTION_IN; 158*8b1d35bdSDavid Sebek cmd->service_action = SCSI_SAI_READ_CAPACITY_16; 159*8b1d35bdSDavid Sebek cmd->alloc_length = B_HOST_TO_BENDIAN_INT32(sizeof(capacityLongResult)); 160*8b1d35bdSDavid Sebek 161*8b1d35bdSDavid Sebek request->flags = SCSI_DIR_IN; 162*8b1d35bdSDavid Sebek request->cdb_length = sizeof(*cmd); 163*8b1d35bdSDavid Sebek request->sort = -1; 164*8b1d35bdSDavid Sebek request->timeout = device->std_timeout; 165*8b1d35bdSDavid Sebek 166*8b1d35bdSDavid Sebek request->data = (uint8*)&capacityLongResult; 167*8b1d35bdSDavid Sebek request->data_length = sizeof(capacityLongResult); 168*8b1d35bdSDavid Sebek request->sg_list = NULL; 169*8b1d35bdSDavid Sebek 170*8b1d35bdSDavid Sebek status_t res = periph_safe_exec(device, request); 171*8b1d35bdSDavid Sebek 172*8b1d35bdSDavid Sebek if (res == B_OK && request->data_resid 173*8b1d35bdSDavid Sebek <= (int32)sizeof(scsi_res_read_capacity_long) - 12) { 174*8b1d35bdSDavid Sebek // At least the last LBA and sector size have been transfered 175*8b1d35bdSDavid Sebek capacityInfo->capacityFilled = true; 176*8b1d35bdSDavid Sebek capacityInfo->lastLba 177*8b1d35bdSDavid Sebek = B_BENDIAN_TO_HOST_INT64(capacityLongResult.lba); 178*8b1d35bdSDavid Sebek capacityInfo->blockSize 179*8b1d35bdSDavid Sebek = B_BENDIAN_TO_HOST_INT32(capacityLongResult.block_size); 180*8b1d35bdSDavid Sebek } 181*8b1d35bdSDavid Sebek 182*8b1d35bdSDavid Sebek if (res == B_OK && request->data_resid 183*8b1d35bdSDavid Sebek <= (int32)sizeof(scsi_res_read_capacity_long) - 15) { 184*8b1d35bdSDavid Sebek // lbpme and lbprz bits were received too 185*8b1d35bdSDavid Sebek capacityInfo->provisioningFilled = true; 186*8b1d35bdSDavid Sebek capacityInfo->lbpme = capacityLongResult.lbpme; 187*8b1d35bdSDavid Sebek capacityInfo->lbprz = capacityLongResult.lbprz; 188*8b1d35bdSDavid Sebek } 189*8b1d35bdSDavid Sebek 190*8b1d35bdSDavid Sebek return res; 191*8b1d35bdSDavid Sebek } 192*8b1d35bdSDavid Sebek 193*8b1d35bdSDavid Sebek 194*8b1d35bdSDavid Sebek static status_t 195*8b1d35bdSDavid Sebek get_unmap_commands(scsi_periph_device_info* device, scsi_ccb* request, 196*8b1d35bdSDavid Sebek UnmapSupport* unmapSupport) 197*8b1d35bdSDavid Sebek { 198*8b1d35bdSDavid Sebek unmapSupport->commandSupportFilled = false; 199*8b1d35bdSDavid Sebek 200*8b1d35bdSDavid Sebek scsi_page_lb_provisioning vpdProvisioning; 201*8b1d35bdSDavid Sebek memset(&vpdProvisioning, 0, sizeof(vpdProvisioning)); 202*8b1d35bdSDavid Sebek status_t vpdStatus = vpd_page_get(device, request, 203*8b1d35bdSDavid Sebek SCSI_PAGE_LB_PROVISIONING, &vpdProvisioning, sizeof(vpdProvisioning)); 204*8b1d35bdSDavid Sebek 205*8b1d35bdSDavid Sebek if (vpdStatus == B_OK 206*8b1d35bdSDavid Sebek && request->data_resid <= (int32)sizeof(scsi_page_lb_provisioning) - 6 207*8b1d35bdSDavid Sebek && vpdProvisioning.page_code == SCSI_PAGE_LB_PROVISIONING 208*8b1d35bdSDavid Sebek && B_BENDIAN_TO_HOST_INT16(vpdProvisioning.page_length) >= 2) { 209*8b1d35bdSDavid Sebek unmapSupport->commandSupportFilled = true; 210*8b1d35bdSDavid Sebek unmapSupport->unmapSupported = vpdProvisioning.lbpu; 211*8b1d35bdSDavid Sebek unmapSupport->ws16Supported = vpdProvisioning.lbpws; 212*8b1d35bdSDavid Sebek unmapSupport->ws10Supported = vpdProvisioning.lbpws10; 213*8b1d35bdSDavid Sebek } 214*8b1d35bdSDavid Sebek 215*8b1d35bdSDavid Sebek if (vpdStatus == B_BAD_VALUE) 216*8b1d35bdSDavid Sebek return B_ERROR; 217*8b1d35bdSDavid Sebek 218*8b1d35bdSDavid Sebek return vpdStatus; 219*8b1d35bdSDavid Sebek } 220*8b1d35bdSDavid Sebek 221*8b1d35bdSDavid Sebek 222*8b1d35bdSDavid Sebek static status_t 223*8b1d35bdSDavid Sebek get_unmap_limits(scsi_periph_device_info* device, scsi_ccb* request, 224*8b1d35bdSDavid Sebek UnmapSupport* unmapSupport) 225*8b1d35bdSDavid Sebek { 226*8b1d35bdSDavid Sebek unmapSupport->blockLimitsFilled = false; 227*8b1d35bdSDavid Sebek 228*8b1d35bdSDavid Sebek scsi_page_block_limits vpdBlockLimits; 229*8b1d35bdSDavid Sebek memset(&vpdBlockLimits, 0, sizeof(vpdBlockLimits)); 230*8b1d35bdSDavid Sebek status_t vpdStatus = vpd_page_get(device, request, 231*8b1d35bdSDavid Sebek SCSI_PAGE_BLOCK_LIMITS, &vpdBlockLimits, sizeof(vpdBlockLimits)); 232*8b1d35bdSDavid Sebek 233*8b1d35bdSDavid Sebek if (vpdStatus == B_OK 234*8b1d35bdSDavid Sebek && request->data_resid <= (int32)sizeof(scsi_page_block_limits) - 44 235*8b1d35bdSDavid Sebek && vpdBlockLimits.page_code == SCSI_PAGE_BLOCK_LIMITS 236*8b1d35bdSDavid Sebek && B_BENDIAN_TO_HOST_INT16(vpdBlockLimits.page_length) == 0x3c) { 237*8b1d35bdSDavid Sebek unmapSupport->blockLimitsFilled = true; 238*8b1d35bdSDavid Sebek unmapSupport->maxUnmapLbaCount = B_BENDIAN_TO_HOST_INT32( 239*8b1d35bdSDavid Sebek vpdBlockLimits.max_unmap_lba_count); 240*8b1d35bdSDavid Sebek unmapSupport->maxUnmapDescriptorCount = B_BENDIAN_TO_HOST_INT32( 241*8b1d35bdSDavid Sebek vpdBlockLimits.max_unmap_blk_count); 242*8b1d35bdSDavid Sebek unmapSupport->maxWritesameLength = B_BENDIAN_TO_HOST_INT64( 243*8b1d35bdSDavid Sebek vpdBlockLimits.max_write_same_length); 244*8b1d35bdSDavid Sebek } 245*8b1d35bdSDavid Sebek 246*8b1d35bdSDavid Sebek if (vpdStatus == B_BAD_VALUE) 247*8b1d35bdSDavid Sebek return B_ERROR; 248*8b1d35bdSDavid Sebek 249*8b1d35bdSDavid Sebek return vpdStatus; 250*8b1d35bdSDavid Sebek } 251*8b1d35bdSDavid Sebek 252*8b1d35bdSDavid Sebek 253*8b1d35bdSDavid Sebek static void 254*8b1d35bdSDavid Sebek determine_unmap_support(const UnmapSupport* unmapSupport, 255*8b1d35bdSDavid Sebek enum trim_command* unmapCommand, uint32* maxLbaCount, 256*8b1d35bdSDavid Sebek uint32* maxDescriptorCount) 257*8b1d35bdSDavid Sebek { 258*8b1d35bdSDavid Sebek #ifdef DEBUG_TRIM 259*8b1d35bdSDavid Sebek if (unmapSupport->commandSupportFilled) 260*8b1d35bdSDavid Sebek dprintf("TRIM: device reports (LBP VPD): LBPU = %d, LBPWS = %d," 261*8b1d35bdSDavid Sebek " LBPWS10 = %d\n", unmapSupport->unmapSupported, 262*8b1d35bdSDavid Sebek unmapSupport->ws16Supported, unmapSupport->ws10Supported); 263*8b1d35bdSDavid Sebek else 264*8b1d35bdSDavid Sebek dprintf("TRIM: could not get the LBP VPD of the device\n"); 265*8b1d35bdSDavid Sebek if (unmapSupport->blockLimitsFilled) 266*8b1d35bdSDavid Sebek dprintf("TRIM: device reports (Block Limits VPD):" 267*8b1d35bdSDavid Sebek "\nTRIM: MAXIMUM UNMAP LBA COUNT = %" B_PRIu32 268*8b1d35bdSDavid Sebek "\nTRIM: MAXIMUM UNMAP BLOCK DESCRIPTOR COUNT = %" B_PRIu32 269*8b1d35bdSDavid Sebek "\nTRIM: MAXIMUM WRITESAME LENGTH = %" B_PRIu64 "\n", 270*8b1d35bdSDavid Sebek unmapSupport->maxUnmapLbaCount, 271*8b1d35bdSDavid Sebek unmapSupport->maxUnmapDescriptorCount, 272*8b1d35bdSDavid Sebek unmapSupport->maxWritesameLength); 273*8b1d35bdSDavid Sebek else 274*8b1d35bdSDavid Sebek dprintf("TRIM: could not get Block Limits VPD of the device\n"); 275*8b1d35bdSDavid Sebek #endif 276*8b1d35bdSDavid Sebek 277*8b1d35bdSDavid Sebek *unmapCommand = TRIM_NONE; 278*8b1d35bdSDavid Sebek *maxLbaCount = 0; 279*8b1d35bdSDavid Sebek *maxDescriptorCount = 0; 280*8b1d35bdSDavid Sebek 281*8b1d35bdSDavid Sebek if (!unmapSupport->commandSupportFilled 282*8b1d35bdSDavid Sebek || !unmapSupport->blockLimitsFilled) 283*8b1d35bdSDavid Sebek return; 284*8b1d35bdSDavid Sebek 285*8b1d35bdSDavid Sebek if (unmapSupport->unmapSupported 286*8b1d35bdSDavid Sebek && unmapSupport->maxUnmapLbaCount > 0 287*8b1d35bdSDavid Sebek && unmapSupport->maxUnmapDescriptorCount > 0) { 288*8b1d35bdSDavid Sebek *unmapCommand = TRIM_UNMAP; 289*8b1d35bdSDavid Sebek *maxLbaCount = unmapSupport->maxUnmapLbaCount; 290*8b1d35bdSDavid Sebek if (unmapSupport->maxUnmapDescriptorCount == UINT32_MAX 291*8b1d35bdSDavid Sebek || unmapSupport->maxUnmapDescriptorCount > UNMAP_MAX_DESCRIPTORS) { 292*8b1d35bdSDavid Sebek // Choose a reasonable value instead 293*8b1d35bdSDavid Sebek *maxDescriptorCount = UNMAP_DEFAULT_DESCRIPTORS; 294*8b1d35bdSDavid Sebek } else { 295*8b1d35bdSDavid Sebek *maxDescriptorCount = unmapSupport->maxUnmapDescriptorCount; 296*8b1d35bdSDavid Sebek } 297*8b1d35bdSDavid Sebek } 298*8b1d35bdSDavid Sebek 299*8b1d35bdSDavid Sebek if (*unmapCommand == TRIM_NONE && unmapSupport->ws16Supported) { 300*8b1d35bdSDavid Sebek uint64 maxLength = unmapSupport->maxWritesameLength; 301*8b1d35bdSDavid Sebek if (maxLength == 0) { 302*8b1d35bdSDavid Sebek // WRITE SAME limit not reported, try UNMAP limit instead 303*8b1d35bdSDavid Sebek if (unmapSupport->maxUnmapLbaCount > 0) 304*8b1d35bdSDavid Sebek maxLength = unmapSupport->maxUnmapLbaCount; 305*8b1d35bdSDavid Sebek else 306*8b1d35bdSDavid Sebek maxLength = WS16_MAX_BLOCK_COUNT_VALUE; 307*8b1d35bdSDavid Sebek } 308*8b1d35bdSDavid Sebek *unmapCommand = TRIM_WRITESAME16; 309*8b1d35bdSDavid Sebek *maxLbaCount = min_c(maxLength, WS16_MAX_BLOCK_COUNT_VALUE); 310*8b1d35bdSDavid Sebek *maxDescriptorCount = 1; 311*8b1d35bdSDavid Sebek } 312*8b1d35bdSDavid Sebek 313*8b1d35bdSDavid Sebek if (*unmapCommand == TRIM_NONE && unmapSupport->ws10Supported) { 314*8b1d35bdSDavid Sebek uint64 maxLength = unmapSupport->maxWritesameLength; 315*8b1d35bdSDavid Sebek if (maxLength == 0) { 316*8b1d35bdSDavid Sebek // WRITE SAME limit not reported, try UNMAP limit instead 317*8b1d35bdSDavid Sebek if (unmapSupport->maxUnmapLbaCount > 0) 318*8b1d35bdSDavid Sebek maxLength = unmapSupport->maxUnmapLbaCount; 319*8b1d35bdSDavid Sebek else 320*8b1d35bdSDavid Sebek maxLength = WS10_MAX_BLOCK_COUNT_VALUE; 321*8b1d35bdSDavid Sebek } 322*8b1d35bdSDavid Sebek *unmapCommand = TRIM_WRITESAME10; 323*8b1d35bdSDavid Sebek *maxLbaCount = min_c(maxLength, WS10_MAX_BLOCK_COUNT_VALUE); 324*8b1d35bdSDavid Sebek *maxDescriptorCount = 1; 325*8b1d35bdSDavid Sebek } 326*8b1d35bdSDavid Sebek } 327*8b1d35bdSDavid Sebek 328*8b1d35bdSDavid Sebek 32924593e2cSAxel Dörfler status_t 33024593e2cSAxel Dörfler periph_check_capacity(scsi_periph_device_info* device, scsi_ccb* request) 33124593e2cSAxel Dörfler { 332*8b1d35bdSDavid Sebek CapacityInfo capacityInfo = {0}; 33324593e2cSAxel Dörfler status_t res; 33424593e2cSAxel Dörfler 33524593e2cSAxel Dörfler SHOW_FLOW(3, "%p, %p", device, request); 33624593e2cSAxel Dörfler 33724593e2cSAxel Dörfler // driver doesn't support capacity callback - seems to be no block 33824593e2cSAxel Dörfler // device driver, so ignore 33924593e2cSAxel Dörfler if (device->callbacks->set_capacity == NULL) 34024593e2cSAxel Dörfler return B_OK; 34124593e2cSAxel Dörfler 342*8b1d35bdSDavid Sebek if (prefer_read_capacity_16(device)) { 343*8b1d35bdSDavid Sebek SHOW_FLOW0(3, "READ CAPACITY 16 tried first"); 344*8b1d35bdSDavid Sebek res = read_capacity_16(device, request, &capacityInfo); 34524593e2cSAxel Dörfler 346*8b1d35bdSDavid Sebek if (res == B_ERROR) { 347*8b1d35bdSDavid Sebek SHOW_FLOW0(3, "READ CAPACITY 16 failed, trying READ CAPACITY 10"); 348*8b1d35bdSDavid Sebek res = read_capacity_10(device, request, &capacityInfo); 349*8b1d35bdSDavid Sebek } 350*8b1d35bdSDavid Sebek } else { 351*8b1d35bdSDavid Sebek SHOW_FLOW0(3, "READ CAPACITY 10 tried first"); 352*8b1d35bdSDavid Sebek res = read_capacity_10(device, request, &capacityInfo); 35324593e2cSAxel Dörfler 354*8b1d35bdSDavid Sebek if (res == B_OK && capacityInfo.capacityFilled 355*8b1d35bdSDavid Sebek && capacityInfo.lastLba == UINT32_MAX) { 356*8b1d35bdSDavid Sebek SHOW_FLOW0(3, "Device is too large, trying READ CAPACITY 16"); 357*8b1d35bdSDavid Sebek res = read_capacity_16(device, request, &capacityInfo); 358*8b1d35bdSDavid Sebek } 359*8b1d35bdSDavid Sebek } 36024593e2cSAxel Dörfler 361*8b1d35bdSDavid Sebek uint64 capacity; 362*8b1d35bdSDavid Sebek uint32 blockSize; 363*8b1d35bdSDavid Sebek 364*8b1d35bdSDavid Sebek if (capacityInfo.capacityFilled) { 365*8b1d35bdSDavid Sebek capacity = capacityInfo.lastLba + 1; 366*8b1d35bdSDavid Sebek blockSize = capacityInfo.blockSize; 367*8b1d35bdSDavid Sebek } else { 368*8b1d35bdSDavid Sebek capacity = 0; 369*8b1d35bdSDavid Sebek blockSize = 0; 370*8b1d35bdSDavid Sebek } 371*8b1d35bdSDavid Sebek 372*8b1d35bdSDavid Sebek enum trim_command unmapCommand = TRIM_NONE; 373*8b1d35bdSDavid Sebek uint32 maxLbaCount = 0; 374*8b1d35bdSDavid Sebek uint32 maxDescriptorCount = 0; 375*8b1d35bdSDavid Sebek 376*8b1d35bdSDavid Sebek if (capacityInfo.provisioningFilled 377*8b1d35bdSDavid Sebek && capacityInfo.lbpme 378*8b1d35bdSDavid Sebek && vpd_pages_supported(device)) { 379*8b1d35bdSDavid Sebek UnmapSupport unmapSupport = {0}; 380*8b1d35bdSDavid Sebek 381*8b1d35bdSDavid Sebek // Don't fail if the device doesn't support the command 382*8b1d35bdSDavid Sebek // but fail if some other error happens 383*8b1d35bdSDavid Sebek if (res == B_OK) { 384*8b1d35bdSDavid Sebek status_t vpdStatus = get_unmap_commands(device, request, 385*8b1d35bdSDavid Sebek &unmapSupport); 386*8b1d35bdSDavid Sebek if (vpdStatus != B_OK && vpdStatus != B_ERROR) 387*8b1d35bdSDavid Sebek res = vpdStatus; 388*8b1d35bdSDavid Sebek } 389*8b1d35bdSDavid Sebek 390*8b1d35bdSDavid Sebek if (res == B_OK) { 391*8b1d35bdSDavid Sebek status_t vpdStatus = get_unmap_limits(device, request, 392*8b1d35bdSDavid Sebek &unmapSupport); 393*8b1d35bdSDavid Sebek if (vpdStatus != B_OK && vpdStatus != B_ERROR) 394*8b1d35bdSDavid Sebek res = vpdStatus; 395*8b1d35bdSDavid Sebek } 396*8b1d35bdSDavid Sebek 397*8b1d35bdSDavid Sebek determine_unmap_support(&unmapSupport, &unmapCommand, 398*8b1d35bdSDavid Sebek &maxLbaCount, &maxDescriptorCount); 399*8b1d35bdSDavid Sebek 400*8b1d35bdSDavid Sebek if (maxLbaCount == 0 || maxDescriptorCount == 0) 401*8b1d35bdSDavid Sebek unmapCommand = TRIM_NONE; 402*8b1d35bdSDavid Sebek } 40324593e2cSAxel Dörfler 40424593e2cSAxel Dörfler if (res == B_DEV_MEDIA_CHANGED) { 40524593e2cSAxel Dörfler // in this case, the error handler has already called check_capacity 40624593e2cSAxel Dörfler // recursively, so we ignore our (invalid) result 40724593e2cSAxel Dörfler SHOW_FLOW0(3, "ignore result because medium change"); 40824593e2cSAxel Dörfler return B_DEV_MEDIA_CHANGED; 40924593e2cSAxel Dörfler } 41024593e2cSAxel Dörfler 411*8b1d35bdSDavid Sebek if (res == B_OK && !capacityInfo.capacityFilled) 412*8b1d35bdSDavid Sebek // Although the capacity and block size will be set to 0 in this case, 413*8b1d35bdSDavid Sebek // it is also better to inform the caller that these values were not 414*8b1d35bdSDavid Sebek // reported by the device 415*8b1d35bdSDavid Sebek res = B_ERROR; 41624593e2cSAxel Dörfler 417*8b1d35bdSDavid Sebek SHOW_FLOW(3, "capacity = %" B_PRIu64 ", block_size = %" B_PRIu32 418*8b1d35bdSDavid Sebek " (%sreported)", capacity, blockSize, 419*8b1d35bdSDavid Sebek capacityInfo.capacityFilled ? "" : "not "); 420*8b1d35bdSDavid Sebek SHOW_INFO(1, "TRIM: Setting trim support to %s", 421*8b1d35bdSDavid Sebek unmapCommand == TRIM_NONE ? "disabled" 422*8b1d35bdSDavid Sebek : unmapCommand == TRIM_UNMAP ? "UNMAP" 423*8b1d35bdSDavid Sebek : unmapCommand == TRIM_WRITESAME16 ? "WRITE SAME (16)" 424*8b1d35bdSDavid Sebek : unmapCommand == TRIM_WRITESAME10 ? "WRITE SAME (10)" 425*8b1d35bdSDavid Sebek : "unknown"); 426*8b1d35bdSDavid Sebek SHOW_FLOW(3, "TRIM: Block limits: size = %" B_PRIu32 427*8b1d35bdSDavid Sebek ", descriptors = %" B_PRIu32, maxLbaCount, maxDescriptorCount); 428f10a55a6SJérôme Duval 429a1e8da41SAxel Dörfler mutex_lock(&device->mutex); 430*8b1d35bdSDavid Sebek // Was there a reason why this mutex 431*8b1d35bdSDavid Sebek // was previously locked much earlier? 432f10a55a6SJérôme Duval 433*8b1d35bdSDavid Sebek device->unmap_command = unmapCommand; 434*8b1d35bdSDavid Sebek device->max_unmap_lba_count = maxLbaCount; 435*8b1d35bdSDavid Sebek device->max_unmap_descriptor_count = maxDescriptorCount; 43624593e2cSAxel Dörfler 43724593e2cSAxel Dörfler device->block_size = blockSize; 43824593e2cSAxel Dörfler 43924593e2cSAxel Dörfler device->callbacks->set_capacity(device->periph_device, 44024593e2cSAxel Dörfler capacity, blockSize); 44124593e2cSAxel Dörfler 44224593e2cSAxel Dörfler /* device->byte2blk_shift = log2( device->block_size ); 44324593e2cSAxel Dörfler if( device->byte2blk_shift < 0 ) { 44424593e2cSAxel Dörfler // this may be too restrictive... 44524593e2cSAxel Dörfler device->capacity = -1; 44624593e2cSAxel Dörfler return ERR_DEV_GENERAL; 44724593e2cSAxel Dörfler }*/ 44824593e2cSAxel Dörfler 449a1e8da41SAxel Dörfler mutex_unlock(&device->mutex); 45024593e2cSAxel Dörfler 45124593e2cSAxel Dörfler SHOW_FLOW(3, "done (%s)", strerror(res)); 45224593e2cSAxel Dörfler 45324593e2cSAxel Dörfler return res; 45424593e2cSAxel Dörfler } 45524593e2cSAxel Dörfler 456960c56aeSAxel Dörfler 457*8b1d35bdSDavid Sebek static status_t 458*8b1d35bdSDavid Sebek trim_unmap(scsi_periph_device_info* device, scsi_ccb* request, 459*8b1d35bdSDavid Sebek scsi_block_range* ranges, uint32 rangeCount, uint64* trimmedBlocks) 460960c56aeSAxel Dörfler { 461*8b1d35bdSDavid Sebek uint64 maxLength = UNMAP_MAX_BLOCK_COUNT_VALUE; 462*8b1d35bdSDavid Sebek uint64 maxBlocksInRequest = device->max_unmap_lba_count; 463*8b1d35bdSDavid Sebek uint32 maxDescriptors = device->max_unmap_descriptor_count; 464*8b1d35bdSDavid Sebek 465*8b1d35bdSDavid Sebek *trimmedBlocks = 0; 466*8b1d35bdSDavid Sebek 467*8b1d35bdSDavid Sebek // Allocate a single buffer and re-use it between requests 468*8b1d35bdSDavid Sebek size_t expectedDescriptorCount = 0; 469*8b1d35bdSDavid Sebek for (uint32 i = 0; i < rangeCount; i++) { 470*8b1d35bdSDavid Sebek expectedDescriptorCount += ranges[i].size / maxLength; 471*8b1d35bdSDavid Sebek if (ranges[i].size % maxLength != 0) 472*8b1d35bdSDavid Sebek expectedDescriptorCount++; 473*8b1d35bdSDavid Sebek } 474*8b1d35bdSDavid Sebek expectedDescriptorCount = min_c(expectedDescriptorCount, maxDescriptors); 475*8b1d35bdSDavid Sebek 476*8b1d35bdSDavid Sebek size_t unmapListAllocatedSize = (expectedDescriptorCount - 1) 47799086aa3SAxel Dörfler * sizeof(scsi_unmap_block_descriptor) 47899086aa3SAxel Dörfler + sizeof(scsi_unmap_parameter_list); 47999086aa3SAxel Dörfler 480*8b1d35bdSDavid Sebek scsi_unmap_parameter_list* unmapList 481*8b1d35bdSDavid Sebek = (scsi_unmap_parameter_list*)malloc(unmapListAllocatedSize); 482*8b1d35bdSDavid Sebek if (unmapList == NULL) 48399086aa3SAxel Dörfler return B_NO_MEMORY; 48499086aa3SAxel Dörfler 485*8b1d35bdSDavid Sebek MemoryDeleter deleter(unmapList); 48699086aa3SAxel Dörfler 487*8b1d35bdSDavid Sebek status_t status = B_OK; 488*8b1d35bdSDavid Sebek uint32 descriptorIndex = 0; 489*8b1d35bdSDavid Sebek uint64 trimmedBlocksInRequest = 0; 490*8b1d35bdSDavid Sebek memset(unmapList, 0, unmapListAllocatedSize); 49199086aa3SAxel Dörfler for (uint32 i = 0; i < rangeCount; i++) { 492*8b1d35bdSDavid Sebek uint64 lba = ranges[i].lba; 493*8b1d35bdSDavid Sebek uint64 length = ranges[i].size; 494*8b1d35bdSDavid Sebek 495*8b1d35bdSDavid Sebek if (length == 0) 496*8b1d35bdSDavid Sebek continue; // Length of 0 would be ignored by the device anyway 497*8b1d35bdSDavid Sebek 498*8b1d35bdSDavid Sebek if (lba > UNMAP_MAX_LBA_VALUE) { 499*8b1d35bdSDavid Sebek SHOW_ERROR0(1, "LBA value is too large!" 500*8b1d35bdSDavid Sebek " This unmap range will be skipped."); 501*8b1d35bdSDavid Sebek continue; 50299086aa3SAxel Dörfler } 50399086aa3SAxel Dörfler 504*8b1d35bdSDavid Sebek // Split large ranges if needed. 505*8b1d35bdSDavid Sebek // Range length is limited by: 506*8b1d35bdSDavid Sebek // - the UNMAP_MAX_BLOCK_COUNT_VALUE constant 507*8b1d35bdSDavid Sebek // - the total number of LBAs in one UNMAP command is limited by 508*8b1d35bdSDavid Sebek // the MAX UNMAP LBA COUNT field in the Block Limits VPD page 509*8b1d35bdSDavid Sebek while (length > 0) { 510*8b1d35bdSDavid Sebek uint64 trimLength = min_c(length, maxLength); 511*8b1d35bdSDavid Sebek trimLength = min_c(trimLength, 512*8b1d35bdSDavid Sebek maxBlocksInRequest - trimmedBlocksInRequest); 513*8b1d35bdSDavid Sebek unmapList->blocks[descriptorIndex].lba 514*8b1d35bdSDavid Sebek = B_HOST_TO_BENDIAN_INT64(lba); 515*8b1d35bdSDavid Sebek unmapList->blocks[descriptorIndex].block_count 516*8b1d35bdSDavid Sebek = B_HOST_TO_BENDIAN_INT32(trimLength); 517*8b1d35bdSDavid Sebek descriptorIndex++; 518*8b1d35bdSDavid Sebek trimmedBlocksInRequest += trimLength; 519*8b1d35bdSDavid Sebek 520*8b1d35bdSDavid Sebek // Split into multiple requests if needed. 521*8b1d35bdSDavid Sebek // The number of UNMAP block descriptors is limited by: 522*8b1d35bdSDavid Sebek // - the number of block descriptors cannot exceed the 523*8b1d35bdSDavid Sebek // MAXIMUM UNMAP PARAMETER COUNT value in the Block Limits VPD 524*8b1d35bdSDavid Sebek // - the size of our buffer 525*8b1d35bdSDavid Sebek // - what fits in one UNMAP command 526*8b1d35bdSDavid Sebek // - the total number of LBAs in one UNMAP command is limited by 527*8b1d35bdSDavid Sebek // the MAX UNMAP LBA COUNT field in the Block Limits VPD page 528*8b1d35bdSDavid Sebek if (descriptorIndex >= maxDescriptors 529*8b1d35bdSDavid Sebek || descriptorIndex >= expectedDescriptorCount 530*8b1d35bdSDavid Sebek || descriptorIndex >= UNMAP_MAX_DESCRIPTORS 531*8b1d35bdSDavid Sebek || trimmedBlocksInRequest >= maxBlocksInRequest 532*8b1d35bdSDavid Sebek || (i == rangeCount - 1 && length <= maxLength)) 533*8b1d35bdSDavid Sebek { 534*8b1d35bdSDavid Sebek uint16 unmapListSize = (descriptorIndex - 1) 535*8b1d35bdSDavid Sebek * sizeof(scsi_unmap_block_descriptor) 536*8b1d35bdSDavid Sebek + sizeof(scsi_unmap_parameter_list); 537*8b1d35bdSDavid Sebek unmapList->data_length = B_HOST_TO_BENDIAN_INT16(unmapListSize 538*8b1d35bdSDavid Sebek - offsetof(scsi_unmap_parameter_list, block_data_length)); 539*8b1d35bdSDavid Sebek unmapList->block_data_length 540*8b1d35bdSDavid Sebek = B_HOST_TO_BENDIAN_INT16(unmapListSize 541*8b1d35bdSDavid Sebek - offsetof(scsi_unmap_parameter_list, blocks)); 542960c56aeSAxel Dörfler 54399086aa3SAxel Dörfler scsi_cmd_unmap* cmd = (scsi_cmd_unmap*)request->cdb; 544960c56aeSAxel Dörfler memset(cmd, 0, sizeof(*cmd)); 54599086aa3SAxel Dörfler cmd->opcode = SCSI_OP_UNMAP; 546*8b1d35bdSDavid Sebek cmd->length = B_HOST_TO_BENDIAN_INT16(unmapListSize); 54799086aa3SAxel Dörfler 548*8b1d35bdSDavid Sebek request->flags = SCSI_DIR_OUT; 549960c56aeSAxel Dörfler request->cdb_length = sizeof(*cmd); 550*8b1d35bdSDavid Sebek request->sort = B_BENDIAN_TO_HOST_INT64( 551*8b1d35bdSDavid Sebek unmapList->blocks[0].lba); 552*8b1d35bdSDavid Sebek request->timeout = device->std_timeout; 553960c56aeSAxel Dörfler 554*8b1d35bdSDavid Sebek request->data = (uint8*)unmapList; 555*8b1d35bdSDavid Sebek request->data_length = unmapListSize; 556*8b1d35bdSDavid Sebek request->sg_list = NULL; 557*8b1d35bdSDavid Sebek 558*8b1d35bdSDavid Sebek SHOW_FLOW(3, "UNMAP data used %" B_PRIu16 559*8b1d35bdSDavid Sebek " of %" B_PRIuSIZE " allocated bytes", 560*8b1d35bdSDavid Sebek unmapListSize, unmapListAllocatedSize); 561*8b1d35bdSDavid Sebek 562*8b1d35bdSDavid Sebek #ifdef DEBUG_TRIM 563*8b1d35bdSDavid Sebek uint16 scsiRangeCount = (uint16)B_BENDIAN_TO_HOST_INT16( 564*8b1d35bdSDavid Sebek unmapList->block_data_length) 565*8b1d35bdSDavid Sebek / sizeof(scsi_unmap_block_descriptor); 566*8b1d35bdSDavid Sebek uint64 count = 0; 567*8b1d35bdSDavid Sebek dprintf("TRIM: SCSI: sending an UNMAP command to" 568*8b1d35bdSDavid Sebek " the device (blocks):\n"); 569*8b1d35bdSDavid Sebek for (uint16 i = 0; i < scsiRangeCount; i++) { 570*8b1d35bdSDavid Sebek dprintf("[%3" B_PRIu16 "] %" B_PRIu64 " : %" B_PRIu32 "\n", 571*8b1d35bdSDavid Sebek i, (uint64)B_BENDIAN_TO_HOST_INT64( 572*8b1d35bdSDavid Sebek unmapList->blocks[i].lba), 573*8b1d35bdSDavid Sebek (uint32)B_BENDIAN_TO_HOST_INT32( 574*8b1d35bdSDavid Sebek unmapList->blocks[i].block_count)); 575*8b1d35bdSDavid Sebek count += (uint32)B_BENDIAN_TO_HOST_INT32( 576*8b1d35bdSDavid Sebek unmapList->blocks[i].block_count); 577*8b1d35bdSDavid Sebek } 578*8b1d35bdSDavid Sebek if (device->max_unmap_lba_count >= count) 579*8b1d35bdSDavid Sebek dprintf("TRIM: SCSI: Previous UNMAP command would fit %" 580*8b1d35bdSDavid Sebek B_PRIu64 " more LBAs\n", 581*8b1d35bdSDavid Sebek device->max_unmap_lba_count - count); 582*8b1d35bdSDavid Sebek else 583*8b1d35bdSDavid Sebek dprintf("TRIM: SCSI: Previous UNMAP ranges exceed the" 584*8b1d35bdSDavid Sebek " device limit!\n"); 585*8b1d35bdSDavid Sebek #endif /* DEBUG_TRIM */ 586*8b1d35bdSDavid Sebek 587*8b1d35bdSDavid Sebek status = periph_safe_exec(device, request); 588960c56aeSAxel Dörfler 589960c56aeSAxel Dörfler // peripheral layer only creates "read" error 590e6bde50aSAxel Dörfler if (status == B_DEV_READ_ERROR) 591960c56aeSAxel Dörfler return B_DEV_WRITE_ERROR; 592*8b1d35bdSDavid Sebek else if (status != B_OK) 593*8b1d35bdSDavid Sebek return status; 594*8b1d35bdSDavid Sebek 595*8b1d35bdSDavid Sebek *trimmedBlocks += trimmedBlocksInRequest; 596*8b1d35bdSDavid Sebek 597*8b1d35bdSDavid Sebek descriptorIndex = 0; 598*8b1d35bdSDavid Sebek trimmedBlocksInRequest = 0; 599*8b1d35bdSDavid Sebek memset(unmapList, 0, unmapListSize); 600*8b1d35bdSDavid Sebek } 601*8b1d35bdSDavid Sebek 602*8b1d35bdSDavid Sebek length -= trimLength; 603*8b1d35bdSDavid Sebek lba += trimLength; 604*8b1d35bdSDavid Sebek } 605*8b1d35bdSDavid Sebek } 606960c56aeSAxel Dörfler 607e6bde50aSAxel Dörfler return status; 608960c56aeSAxel Dörfler } 609960c56aeSAxel Dörfler 610*8b1d35bdSDavid Sebek 611*8b1d35bdSDavid Sebek static status_t 612*8b1d35bdSDavid Sebek trim_writesame16(scsi_periph_device_info* device, scsi_ccb* request, 613*8b1d35bdSDavid Sebek scsi_block_range* ranges, uint32 rangeCount, uint64* trimmedBlocks) 614*8b1d35bdSDavid Sebek { 615*8b1d35bdSDavid Sebek status_t status = B_OK; 616*8b1d35bdSDavid Sebek *trimmedBlocks = 0; 617*8b1d35bdSDavid Sebek 618*8b1d35bdSDavid Sebek for (uint32 i = 0; i < rangeCount; i++) { 619*8b1d35bdSDavid Sebek uint64 lba = ranges[i].lba; 620*8b1d35bdSDavid Sebek uint64 length = ranges[i].size; 621*8b1d35bdSDavid Sebek 622*8b1d35bdSDavid Sebek if (length == 0) 623*8b1d35bdSDavid Sebek continue; // length of 0 would mean the rest of the device! 624*8b1d35bdSDavid Sebek 625*8b1d35bdSDavid Sebek if (lba > WS16_MAX_LBA_VALUE) { 626*8b1d35bdSDavid Sebek SHOW_ERROR0(1, "LBA value is too large!" 627*8b1d35bdSDavid Sebek " This unmap range will be skipped."); 628*8b1d35bdSDavid Sebek continue; 629*8b1d35bdSDavid Sebek } 630*8b1d35bdSDavid Sebek 631*8b1d35bdSDavid Sebek // Split the range into multiple requests if needed 632*8b1d35bdSDavid Sebek uint64 maxLength = min_c(device->max_unmap_lba_count, 633*8b1d35bdSDavid Sebek WS16_MAX_BLOCK_COUNT_VALUE); 634*8b1d35bdSDavid Sebek while (length > 0) { 635*8b1d35bdSDavid Sebek uint64 trimLength = min_c(length, maxLength); 636*8b1d35bdSDavid Sebek if (trimLength == 0) { 637*8b1d35bdSDavid Sebek SHOW_ERROR0(1, 638*8b1d35bdSDavid Sebek "Error: Length of zero in WRITE SAME (16) detected"); 639*8b1d35bdSDavid Sebek break; 640*8b1d35bdSDavid Sebek } 641*8b1d35bdSDavid Sebek 642*8b1d35bdSDavid Sebek void* block = malloc(device->block_size); 643*8b1d35bdSDavid Sebek if (block == NULL) 644*8b1d35bdSDavid Sebek return B_NO_MEMORY; 645*8b1d35bdSDavid Sebek MemoryDeleter deleter(block); 646*8b1d35bdSDavid Sebek memset(block, 0, device->block_size); 647*8b1d35bdSDavid Sebek 648*8b1d35bdSDavid Sebek scsi_cmd_wsame_16* cmd = (scsi_cmd_wsame_16*)request->cdb; 649*8b1d35bdSDavid Sebek memset(cmd, 0, sizeof(*cmd)); 650*8b1d35bdSDavid Sebek cmd->opcode = SCSI_OP_WRITE_SAME_16; 651*8b1d35bdSDavid Sebek cmd->unmap = 1; 652*8b1d35bdSDavid Sebek cmd->lba = B_HOST_TO_BENDIAN_INT64(lba); 653*8b1d35bdSDavid Sebek cmd->length = B_HOST_TO_BENDIAN_INT32(trimLength); 654*8b1d35bdSDavid Sebek //cmd->ndob = 1; // no data is needed if this bit is enabled 655*8b1d35bdSDavid Sebek 656*8b1d35bdSDavid Sebek request->flags = SCSI_DIR_OUT; 657*8b1d35bdSDavid Sebek request->cdb_length = sizeof(*cmd); 658*8b1d35bdSDavid Sebek request->sort = lba; 659*8b1d35bdSDavid Sebek request->timeout = device->std_timeout; 660*8b1d35bdSDavid Sebek 661*8b1d35bdSDavid Sebek request->data = (uint8*)block; 662*8b1d35bdSDavid Sebek request->data_length = device->block_size; 663*8b1d35bdSDavid Sebek request->sg_list = NULL; 664*8b1d35bdSDavid Sebek 665*8b1d35bdSDavid Sebek #ifdef DEBUG_TRIM 666*8b1d35bdSDavid Sebek dprintf("TRIM: SCSI: sending a WRITE SAME (16) command to" 667*8b1d35bdSDavid Sebek " the device (blocks):\n"); 668*8b1d35bdSDavid Sebek dprintf("%" B_PRIu64 " : %" B_PRIu32 "\n", 669*8b1d35bdSDavid Sebek (uint64)B_BENDIAN_TO_HOST_INT64(cmd->lba), 670*8b1d35bdSDavid Sebek (uint32)B_BENDIAN_TO_HOST_INT32(cmd->length)); 671*8b1d35bdSDavid Sebek #endif 672*8b1d35bdSDavid Sebek 673*8b1d35bdSDavid Sebek status = periph_safe_exec(device, request); 674*8b1d35bdSDavid Sebek 675*8b1d35bdSDavid Sebek // peripheral layer only creates "read" error 676*8b1d35bdSDavid Sebek if (status == B_DEV_READ_ERROR) 677*8b1d35bdSDavid Sebek return B_DEV_WRITE_ERROR; 678*8b1d35bdSDavid Sebek else if (status != B_OK) 679*8b1d35bdSDavid Sebek return status; 680*8b1d35bdSDavid Sebek 681*8b1d35bdSDavid Sebek *trimmedBlocks += trimLength; 682*8b1d35bdSDavid Sebek length -= trimLength; 683*8b1d35bdSDavid Sebek lba += trimLength; 684*8b1d35bdSDavid Sebek } 685*8b1d35bdSDavid Sebek } 686*8b1d35bdSDavid Sebek 687*8b1d35bdSDavid Sebek return status; 688*8b1d35bdSDavid Sebek } 689*8b1d35bdSDavid Sebek 690*8b1d35bdSDavid Sebek 691*8b1d35bdSDavid Sebek static status_t 692*8b1d35bdSDavid Sebek trim_writesame10(scsi_periph_device_info* device, scsi_ccb* request, 693*8b1d35bdSDavid Sebek scsi_block_range* ranges, uint32 rangeCount, uint64* trimmedBlocks) 694*8b1d35bdSDavid Sebek { 695*8b1d35bdSDavid Sebek status_t status = B_OK; 696*8b1d35bdSDavid Sebek *trimmedBlocks = 0; 697*8b1d35bdSDavid Sebek 698*8b1d35bdSDavid Sebek for (uint32 i = 0; i < rangeCount; i++) { 699*8b1d35bdSDavid Sebek uint64 lba = ranges[i].lba; 700*8b1d35bdSDavid Sebek uint64 length = ranges[i].size; 701*8b1d35bdSDavid Sebek 702*8b1d35bdSDavid Sebek if (length == 0) 703*8b1d35bdSDavid Sebek continue; // length of 0 would mean the rest of the device! 704*8b1d35bdSDavid Sebek 705*8b1d35bdSDavid Sebek if (lba > WS10_MAX_LBA_VALUE) { 706*8b1d35bdSDavid Sebek SHOW_ERROR0(1, "LBA value is too large!" 707*8b1d35bdSDavid Sebek " This unmap range will be skipped."); 708*8b1d35bdSDavid Sebek continue; 709*8b1d35bdSDavid Sebek } 710*8b1d35bdSDavid Sebek 711*8b1d35bdSDavid Sebek // Split the range into multiple requests if needed 712*8b1d35bdSDavid Sebek uint64 maxLength = min_c(device->max_unmap_lba_count, 713*8b1d35bdSDavid Sebek WS10_MAX_BLOCK_COUNT_VALUE); 714*8b1d35bdSDavid Sebek while (length > 0) { 715*8b1d35bdSDavid Sebek uint64 trimLength = min_c(length, maxLength); 716*8b1d35bdSDavid Sebek if (trimLength == 0) { 717*8b1d35bdSDavid Sebek SHOW_ERROR0(1, 718*8b1d35bdSDavid Sebek "Error: Length of zero in WRITE SAME (10) detected"); 719*8b1d35bdSDavid Sebek break; 720*8b1d35bdSDavid Sebek } 721*8b1d35bdSDavid Sebek 722*8b1d35bdSDavid Sebek void* block = malloc(device->block_size); 723*8b1d35bdSDavid Sebek if (block == NULL) 724*8b1d35bdSDavid Sebek return B_NO_MEMORY; 725*8b1d35bdSDavid Sebek MemoryDeleter deleter(block); 726*8b1d35bdSDavid Sebek memset(block, 0, device->block_size); 727*8b1d35bdSDavid Sebek 728*8b1d35bdSDavid Sebek scsi_cmd_wsame_10* cmd = (scsi_cmd_wsame_10*)request->cdb; 729*8b1d35bdSDavid Sebek memset(cmd, 0, sizeof(*cmd)); 730*8b1d35bdSDavid Sebek cmd->opcode = SCSI_OP_WRITE_SAME_10; 731*8b1d35bdSDavid Sebek cmd->unmap = 1; 732*8b1d35bdSDavid Sebek cmd->lba = B_HOST_TO_BENDIAN_INT32(lba); 733*8b1d35bdSDavid Sebek cmd->length = B_HOST_TO_BENDIAN_INT16(trimLength); 734*8b1d35bdSDavid Sebek 735*8b1d35bdSDavid Sebek request->flags = SCSI_DIR_OUT; 736*8b1d35bdSDavid Sebek request->cdb_length = sizeof(*cmd); 737*8b1d35bdSDavid Sebek request->sort = lba; 738*8b1d35bdSDavid Sebek request->timeout = device->std_timeout; 739*8b1d35bdSDavid Sebek 740*8b1d35bdSDavid Sebek request->data = (uint8*)block; 741*8b1d35bdSDavid Sebek request->data_length = device->block_size; 742*8b1d35bdSDavid Sebek request->sg_list = NULL; 743*8b1d35bdSDavid Sebek 744*8b1d35bdSDavid Sebek #ifdef DEBUG_TRIM 745*8b1d35bdSDavid Sebek dprintf("TRIM: SCSI: sending a WRITE SAME (10) command to" 746*8b1d35bdSDavid Sebek " the device (blocks):\n"); 747*8b1d35bdSDavid Sebek dprintf("%" B_PRIu32 " : %" B_PRIu16 "\n", 748*8b1d35bdSDavid Sebek (uint32)B_BENDIAN_TO_HOST_INT32(cmd->lba), 749*8b1d35bdSDavid Sebek (uint16)B_BENDIAN_TO_HOST_INT16(cmd->length)); 750*8b1d35bdSDavid Sebek #endif 751*8b1d35bdSDavid Sebek 752*8b1d35bdSDavid Sebek status = periph_safe_exec(device, request); 753*8b1d35bdSDavid Sebek 754*8b1d35bdSDavid Sebek // peripheral layer only creates "read" error 755*8b1d35bdSDavid Sebek if (status == B_DEV_READ_ERROR) 756*8b1d35bdSDavid Sebek return B_DEV_WRITE_ERROR; 757*8b1d35bdSDavid Sebek else if (status != B_OK) 758*8b1d35bdSDavid Sebek return status; 759*8b1d35bdSDavid Sebek 760*8b1d35bdSDavid Sebek *trimmedBlocks += trimLength; 761*8b1d35bdSDavid Sebek length -= trimLength; 762*8b1d35bdSDavid Sebek lba += trimLength; 763*8b1d35bdSDavid Sebek } 764*8b1d35bdSDavid Sebek } 765*8b1d35bdSDavid Sebek 766*8b1d35bdSDavid Sebek return status; 767*8b1d35bdSDavid Sebek } 768*8b1d35bdSDavid Sebek 769*8b1d35bdSDavid Sebek 770*8b1d35bdSDavid Sebek status_t 771*8b1d35bdSDavid Sebek periph_trim_device(scsi_periph_device_info* device, scsi_ccb* request, 772*8b1d35bdSDavid Sebek scsi_block_range* ranges, uint32 rangeCount, uint64* trimmedBlocks) 773*8b1d35bdSDavid Sebek { 774*8b1d35bdSDavid Sebek *trimmedBlocks = 0; 775*8b1d35bdSDavid Sebek 776*8b1d35bdSDavid Sebek if (device->unmap_command == TRIM_NONE 777*8b1d35bdSDavid Sebek || device->max_unmap_lba_count == 0 778*8b1d35bdSDavid Sebek || device->max_unmap_descriptor_count == 0) 779*8b1d35bdSDavid Sebek return B_UNSUPPORTED; 780*8b1d35bdSDavid Sebek 781*8b1d35bdSDavid Sebek switch (device->unmap_command) { 782*8b1d35bdSDavid Sebek case TRIM_UNMAP: 783*8b1d35bdSDavid Sebek return trim_unmap(device, request, ranges, rangeCount, 784*8b1d35bdSDavid Sebek trimmedBlocks); 785*8b1d35bdSDavid Sebek case TRIM_WRITESAME16: 786*8b1d35bdSDavid Sebek return trim_writesame16(device, request, ranges, rangeCount, 787*8b1d35bdSDavid Sebek trimmedBlocks); 788*8b1d35bdSDavid Sebek case TRIM_WRITESAME10: 789*8b1d35bdSDavid Sebek return trim_writesame10(device, request, ranges, rangeCount, 790*8b1d35bdSDavid Sebek trimmedBlocks); 791*8b1d35bdSDavid Sebek default: 792*8b1d35bdSDavid Sebek return B_UNSUPPORTED; 793*8b1d35bdSDavid Sebek } 794*8b1d35bdSDavid Sebek } 795