124593e2cSAxel Dörfler /* 28b1d35bdSDavid Sebek * Copyright 2021 David Sebek, dasebek@gmail.com 38b1d35bdSDavid Sebek * Copyright 2004-2013 Haiku, Inc. 48b1d35bdSDavid Sebek * Copyright 2002-2003 Thomas Kurschel 58b1d35bdSDavid 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 198b1d35bdSDavid Sebek // UNMAP command limits 208b1d35bdSDavid Sebek #define UNMAP_MAX_LBA_VALUE UINT64_MAX 218b1d35bdSDavid Sebek #define UNMAP_MAX_BLOCK_COUNT_VALUE UINT32_MAX 228b1d35bdSDavid Sebek #define UNMAP_MAX_DESCRIPTORS 4095 238b1d35bdSDavid Sebek // Limit imposed by the UNMAP command structure 248b1d35bdSDavid Sebek #define UNMAP_DEFAULT_DESCRIPTORS 255 258b1d35bdSDavid Sebek // Reasonable default (?) when not specified by the device 268b1d35bdSDavid Sebek 278b1d35bdSDavid Sebek // WRITE SAME (16) command limits 288b1d35bdSDavid Sebek #define WS16_MAX_LBA_VALUE UINT64_MAX 298b1d35bdSDavid Sebek #define WS16_MAX_BLOCK_COUNT_VALUE UINT32_MAX 308b1d35bdSDavid Sebek 318b1d35bdSDavid Sebek // WRITE SAME (10) command limits 328b1d35bdSDavid Sebek #define WS10_MAX_LBA_VALUE UINT32_MAX 338b1d35bdSDavid Sebek #define WS10_MAX_BLOCK_COUNT_VALUE UINT16_MAX 348b1d35bdSDavid Sebek 358b1d35bdSDavid Sebek 368b1d35bdSDavid Sebek struct CapacityInfo { 378b1d35bdSDavid Sebek // Result of the READ CAPACITY command 388b1d35bdSDavid Sebek bool capacityFilled; 398b1d35bdSDavid Sebek uint64 lastLba; 408b1d35bdSDavid Sebek uint32 blockSize; 41*688acf41SJérôme Duval uint32 physicalBlockSize; 428b1d35bdSDavid Sebek 43*688acf41SJérôme Duval // Provisioning info from READ CAPACITY 448b1d35bdSDavid Sebek bool provisioningFilled; 458b1d35bdSDavid Sebek bool lbpme; 468b1d35bdSDavid Sebek bool lbprz; 478b1d35bdSDavid Sebek }; 488b1d35bdSDavid Sebek 498b1d35bdSDavid Sebek 508b1d35bdSDavid Sebek struct UnmapSupport { 518b1d35bdSDavid Sebek // UNMAP commands supported by the device 528b1d35bdSDavid Sebek bool commandSupportFilled; 538b1d35bdSDavid Sebek bool unmapSupported; 548b1d35bdSDavid Sebek bool ws16Supported; 558b1d35bdSDavid Sebek bool ws10Supported; 568b1d35bdSDavid Sebek 578b1d35bdSDavid Sebek // Block limits for UNMAP commands 588b1d35bdSDavid Sebek bool blockLimitsFilled; 598b1d35bdSDavid Sebek uint32 maxUnmapLbaCount; 608b1d35bdSDavid Sebek uint32 maxUnmapDescriptorCount; 618b1d35bdSDavid Sebek uint64 maxWritesameLength; 628b1d35bdSDavid Sebek }; 638b1d35bdSDavid Sebek 648b1d35bdSDavid Sebek 658b1d35bdSDavid Sebek static bool 668b1d35bdSDavid Sebek prefer_read_capacity_16(scsi_periph_device_info* device) 678b1d35bdSDavid Sebek { 688b1d35bdSDavid Sebek const scsi_res_inquiry* inquiryData = NULL; 698b1d35bdSDavid Sebek size_t inquiryDataLength; 708b1d35bdSDavid Sebek 718b1d35bdSDavid Sebek if (gDeviceManager->get_attr_raw(device->node, SCSI_DEVICE_INQUIRY_ITEM, 728b1d35bdSDavid Sebek (const void**)&inquiryData, &inquiryDataLength, true) != B_OK 738b1d35bdSDavid Sebek || inquiryDataLength != sizeof(*inquiryData)) { 748b1d35bdSDavid Sebek return false; 758b1d35bdSDavid Sebek } 768b1d35bdSDavid Sebek 778b1d35bdSDavid Sebek if (inquiryData->protect) 788b1d35bdSDavid Sebek return true; 798b1d35bdSDavid Sebek 808b1d35bdSDavid Sebek if (inquiryData->ansi_version > 0x04 /* SPC-2 */) 818b1d35bdSDavid Sebek return true; 828b1d35bdSDavid Sebek 838b1d35bdSDavid Sebek return false; 848b1d35bdSDavid Sebek } 858b1d35bdSDavid Sebek 868b1d35bdSDavid Sebek 878b1d35bdSDavid Sebek static bool 888b1d35bdSDavid Sebek vpd_pages_supported(scsi_periph_device_info* device) 898b1d35bdSDavid Sebek { 908b1d35bdSDavid Sebek const scsi_res_inquiry* inquiryData = NULL; 918b1d35bdSDavid Sebek size_t inquiryDataLength; 928b1d35bdSDavid Sebek 938b1d35bdSDavid Sebek if (gDeviceManager->get_attr_raw(device->node, SCSI_DEVICE_INQUIRY_ITEM, 948b1d35bdSDavid Sebek (const void**)&inquiryData, &inquiryDataLength, true) != B_OK 958b1d35bdSDavid Sebek || inquiryDataLength != sizeof(*inquiryData)) { 968b1d35bdSDavid Sebek return false; 978b1d35bdSDavid Sebek } 988b1d35bdSDavid Sebek 998b1d35bdSDavid Sebek if (inquiryData->ansi_version >= 0x04 /* SPC-2 */) 1008b1d35bdSDavid Sebek return true; 1018b1d35bdSDavid Sebek 1028b1d35bdSDavid Sebek return false; 1038b1d35bdSDavid Sebek } 1048b1d35bdSDavid Sebek 1058b1d35bdSDavid Sebek 1068b1d35bdSDavid Sebek static status_t 1078b1d35bdSDavid Sebek read_capacity_10(scsi_periph_device_info* device, scsi_ccb* request, 1088b1d35bdSDavid Sebek CapacityInfo* capacityInfo) 1098b1d35bdSDavid Sebek { 1108b1d35bdSDavid Sebek capacityInfo->capacityFilled = false; 1118b1d35bdSDavid Sebek capacityInfo->provisioningFilled = false; 1128b1d35bdSDavid Sebek 1138b1d35bdSDavid Sebek scsi_res_read_capacity capacityResult; 1148b1d35bdSDavid Sebek memset(&capacityResult, 0, sizeof(capacityResult)); 1158b1d35bdSDavid Sebek 1168b1d35bdSDavid Sebek scsi_cmd_read_capacity* cmd = (scsi_cmd_read_capacity*)request->cdb; 1178b1d35bdSDavid Sebek memset(cmd, 0, sizeof(*cmd)); 1188b1d35bdSDavid Sebek cmd->opcode = SCSI_OP_READ_CAPACITY; 1198b1d35bdSDavid Sebek // we don't set PMI (partial medium indicator) as we want the whole capacity; 1208b1d35bdSDavid Sebek // in this case, all other parameters must be zero 1218b1d35bdSDavid Sebek 1228b1d35bdSDavid Sebek request->flags = SCSI_DIR_IN; 1238b1d35bdSDavid Sebek request->cdb_length = sizeof(*cmd); 1248b1d35bdSDavid Sebek request->sort = -1; 1258b1d35bdSDavid Sebek request->timeout = device->std_timeout; 1268b1d35bdSDavid Sebek 1278b1d35bdSDavid Sebek request->data = (uint8*)&capacityResult; 1288b1d35bdSDavid Sebek request->data_length = sizeof(capacityResult); 1298b1d35bdSDavid Sebek request->sg_list = NULL; 1308b1d35bdSDavid Sebek 1318b1d35bdSDavid Sebek status_t res = periph_safe_exec(device, request); 1328b1d35bdSDavid Sebek 1338b1d35bdSDavid Sebek if (res == B_OK && request->data_resid == 0) { 1348b1d35bdSDavid Sebek capacityInfo->capacityFilled = true; 1358b1d35bdSDavid Sebek capacityInfo->lastLba 1368b1d35bdSDavid Sebek = (uint32)B_BENDIAN_TO_HOST_INT32(capacityResult.lba); 1378b1d35bdSDavid Sebek capacityInfo->blockSize 1388b1d35bdSDavid Sebek = B_BENDIAN_TO_HOST_INT32(capacityResult.block_size); 139*688acf41SJérôme Duval capacityInfo->physicalBlockSize = capacityInfo->blockSize; 1408b1d35bdSDavid Sebek } 1418b1d35bdSDavid Sebek 1428b1d35bdSDavid Sebek return res; 1438b1d35bdSDavid Sebek } 1448b1d35bdSDavid Sebek 1458b1d35bdSDavid Sebek 1468b1d35bdSDavid Sebek static status_t 1478b1d35bdSDavid Sebek read_capacity_16(scsi_periph_device_info* device, scsi_ccb* request, 1488b1d35bdSDavid Sebek CapacityInfo* capacityInfo) 1498b1d35bdSDavid Sebek { 1508b1d35bdSDavid Sebek capacityInfo->capacityFilled = false; 1518b1d35bdSDavid Sebek capacityInfo->provisioningFilled = false; 1528b1d35bdSDavid Sebek 1538b1d35bdSDavid Sebek scsi_res_read_capacity_long capacityLongResult; 1548b1d35bdSDavid Sebek memset(&capacityLongResult, 0, sizeof(capacityLongResult)); 1558b1d35bdSDavid Sebek 1568b1d35bdSDavid Sebek scsi_cmd_read_capacity_long* cmd 1578b1d35bdSDavid Sebek = (scsi_cmd_read_capacity_long*)request->cdb; 1588b1d35bdSDavid Sebek memset(cmd, 0, sizeof(*cmd)); 1598b1d35bdSDavid Sebek cmd->opcode = SCSI_OP_SERVICE_ACTION_IN; 1608b1d35bdSDavid Sebek cmd->service_action = SCSI_SAI_READ_CAPACITY_16; 1618b1d35bdSDavid Sebek cmd->alloc_length = B_HOST_TO_BENDIAN_INT32(sizeof(capacityLongResult)); 1628b1d35bdSDavid Sebek 1638b1d35bdSDavid Sebek request->flags = SCSI_DIR_IN; 1648b1d35bdSDavid Sebek request->cdb_length = sizeof(*cmd); 1658b1d35bdSDavid Sebek request->sort = -1; 1668b1d35bdSDavid Sebek request->timeout = device->std_timeout; 1678b1d35bdSDavid Sebek 1688b1d35bdSDavid Sebek request->data = (uint8*)&capacityLongResult; 1698b1d35bdSDavid Sebek request->data_length = sizeof(capacityLongResult); 1708b1d35bdSDavid Sebek request->sg_list = NULL; 1718b1d35bdSDavid Sebek 1728b1d35bdSDavid Sebek status_t res = periph_safe_exec(device, request); 1738b1d35bdSDavid Sebek 1748b1d35bdSDavid Sebek if (res == B_OK && request->data_resid 1758b1d35bdSDavid Sebek <= (int32)sizeof(scsi_res_read_capacity_long) - 12) { 1768b1d35bdSDavid Sebek // At least the last LBA and sector size have been transfered 1778b1d35bdSDavid Sebek capacityInfo->capacityFilled = true; 1788b1d35bdSDavid Sebek capacityInfo->lastLba 1798b1d35bdSDavid Sebek = B_BENDIAN_TO_HOST_INT64(capacityLongResult.lba); 1808b1d35bdSDavid Sebek capacityInfo->blockSize 1818b1d35bdSDavid Sebek = B_BENDIAN_TO_HOST_INT32(capacityLongResult.block_size); 182*688acf41SJérôme Duval capacityInfo->physicalBlockSize = capacityInfo->blockSize 183*688acf41SJérôme Duval * (1 << capacityLongResult.logical_blocks_per_physical_block_exponent); 1848b1d35bdSDavid Sebek } 1858b1d35bdSDavid Sebek 1868b1d35bdSDavid Sebek if (res == B_OK && request->data_resid 1878b1d35bdSDavid Sebek <= (int32)sizeof(scsi_res_read_capacity_long) - 15) { 1888b1d35bdSDavid Sebek // lbpme and lbprz bits were received too 1898b1d35bdSDavid Sebek capacityInfo->provisioningFilled = true; 1908b1d35bdSDavid Sebek capacityInfo->lbpme = capacityLongResult.lbpme; 1918b1d35bdSDavid Sebek capacityInfo->lbprz = capacityLongResult.lbprz; 1928b1d35bdSDavid Sebek } 1938b1d35bdSDavid Sebek 1948b1d35bdSDavid Sebek return res; 1958b1d35bdSDavid Sebek } 1968b1d35bdSDavid Sebek 1978b1d35bdSDavid Sebek 1988b1d35bdSDavid Sebek static status_t 1998b1d35bdSDavid Sebek get_unmap_commands(scsi_periph_device_info* device, scsi_ccb* request, 2008b1d35bdSDavid Sebek UnmapSupport* unmapSupport) 2018b1d35bdSDavid Sebek { 2028b1d35bdSDavid Sebek unmapSupport->commandSupportFilled = false; 2038b1d35bdSDavid Sebek 2048b1d35bdSDavid Sebek scsi_page_lb_provisioning vpdProvisioning; 2058b1d35bdSDavid Sebek memset(&vpdProvisioning, 0, sizeof(vpdProvisioning)); 2068b1d35bdSDavid Sebek status_t vpdStatus = vpd_page_get(device, request, 2078b1d35bdSDavid Sebek SCSI_PAGE_LB_PROVISIONING, &vpdProvisioning, sizeof(vpdProvisioning)); 2088b1d35bdSDavid Sebek 2098b1d35bdSDavid Sebek if (vpdStatus == B_OK 2108b1d35bdSDavid Sebek && request->data_resid <= (int32)sizeof(scsi_page_lb_provisioning) - 6 2118b1d35bdSDavid Sebek && vpdProvisioning.page_code == SCSI_PAGE_LB_PROVISIONING 2128b1d35bdSDavid Sebek && B_BENDIAN_TO_HOST_INT16(vpdProvisioning.page_length) >= 2) { 2138b1d35bdSDavid Sebek unmapSupport->commandSupportFilled = true; 2148b1d35bdSDavid Sebek unmapSupport->unmapSupported = vpdProvisioning.lbpu; 2158b1d35bdSDavid Sebek unmapSupport->ws16Supported = vpdProvisioning.lbpws; 2168b1d35bdSDavid Sebek unmapSupport->ws10Supported = vpdProvisioning.lbpws10; 2178b1d35bdSDavid Sebek } 2188b1d35bdSDavid Sebek 2198b1d35bdSDavid Sebek if (vpdStatus == B_BAD_VALUE) 2208b1d35bdSDavid Sebek return B_ERROR; 2218b1d35bdSDavid Sebek 2228b1d35bdSDavid Sebek return vpdStatus; 2238b1d35bdSDavid Sebek } 2248b1d35bdSDavid Sebek 2258b1d35bdSDavid Sebek 2268b1d35bdSDavid Sebek static status_t 2278b1d35bdSDavid Sebek get_unmap_limits(scsi_periph_device_info* device, scsi_ccb* request, 2288b1d35bdSDavid Sebek UnmapSupport* unmapSupport) 2298b1d35bdSDavid Sebek { 2308b1d35bdSDavid Sebek unmapSupport->blockLimitsFilled = false; 2318b1d35bdSDavid Sebek 2328b1d35bdSDavid Sebek scsi_page_block_limits vpdBlockLimits; 2338b1d35bdSDavid Sebek memset(&vpdBlockLimits, 0, sizeof(vpdBlockLimits)); 2348b1d35bdSDavid Sebek status_t vpdStatus = vpd_page_get(device, request, 2358b1d35bdSDavid Sebek SCSI_PAGE_BLOCK_LIMITS, &vpdBlockLimits, sizeof(vpdBlockLimits)); 2368b1d35bdSDavid Sebek 2378b1d35bdSDavid Sebek if (vpdStatus == B_OK 2388b1d35bdSDavid Sebek && request->data_resid <= (int32)sizeof(scsi_page_block_limits) - 44 2398b1d35bdSDavid Sebek && vpdBlockLimits.page_code == SCSI_PAGE_BLOCK_LIMITS 2408b1d35bdSDavid Sebek && B_BENDIAN_TO_HOST_INT16(vpdBlockLimits.page_length) == 0x3c) { 2418b1d35bdSDavid Sebek unmapSupport->blockLimitsFilled = true; 2428b1d35bdSDavid Sebek unmapSupport->maxUnmapLbaCount = B_BENDIAN_TO_HOST_INT32( 2438b1d35bdSDavid Sebek vpdBlockLimits.max_unmap_lba_count); 2448b1d35bdSDavid Sebek unmapSupport->maxUnmapDescriptorCount = B_BENDIAN_TO_HOST_INT32( 2458b1d35bdSDavid Sebek vpdBlockLimits.max_unmap_blk_count); 2468b1d35bdSDavid Sebek unmapSupport->maxWritesameLength = B_BENDIAN_TO_HOST_INT64( 2478b1d35bdSDavid Sebek vpdBlockLimits.max_write_same_length); 2488b1d35bdSDavid Sebek } 2498b1d35bdSDavid Sebek 2508b1d35bdSDavid Sebek if (vpdStatus == B_BAD_VALUE) 2518b1d35bdSDavid Sebek return B_ERROR; 2528b1d35bdSDavid Sebek 2538b1d35bdSDavid Sebek return vpdStatus; 2548b1d35bdSDavid Sebek } 2558b1d35bdSDavid Sebek 2568b1d35bdSDavid Sebek 2578b1d35bdSDavid Sebek static void 2588b1d35bdSDavid Sebek determine_unmap_support(const UnmapSupport* unmapSupport, 2598b1d35bdSDavid Sebek enum trim_command* unmapCommand, uint32* maxLbaCount, 2608b1d35bdSDavid Sebek uint32* maxDescriptorCount) 2618b1d35bdSDavid Sebek { 2628b1d35bdSDavid Sebek #ifdef DEBUG_TRIM 2638b1d35bdSDavid Sebek if (unmapSupport->commandSupportFilled) 2648b1d35bdSDavid Sebek dprintf("TRIM: device reports (LBP VPD): LBPU = %d, LBPWS = %d," 2658b1d35bdSDavid Sebek " LBPWS10 = %d\n", unmapSupport->unmapSupported, 2668b1d35bdSDavid Sebek unmapSupport->ws16Supported, unmapSupport->ws10Supported); 2678b1d35bdSDavid Sebek else 2688b1d35bdSDavid Sebek dprintf("TRIM: could not get the LBP VPD of the device\n"); 2698b1d35bdSDavid Sebek if (unmapSupport->blockLimitsFilled) 2708b1d35bdSDavid Sebek dprintf("TRIM: device reports (Block Limits VPD):" 2718b1d35bdSDavid Sebek "\nTRIM: MAXIMUM UNMAP LBA COUNT = %" B_PRIu32 2728b1d35bdSDavid Sebek "\nTRIM: MAXIMUM UNMAP BLOCK DESCRIPTOR COUNT = %" B_PRIu32 2738b1d35bdSDavid Sebek "\nTRIM: MAXIMUM WRITESAME LENGTH = %" B_PRIu64 "\n", 2748b1d35bdSDavid Sebek unmapSupport->maxUnmapLbaCount, 2758b1d35bdSDavid Sebek unmapSupport->maxUnmapDescriptorCount, 2768b1d35bdSDavid Sebek unmapSupport->maxWritesameLength); 2778b1d35bdSDavid Sebek else 2788b1d35bdSDavid Sebek dprintf("TRIM: could not get Block Limits VPD of the device\n"); 2798b1d35bdSDavid Sebek #endif 2808b1d35bdSDavid Sebek 2818b1d35bdSDavid Sebek *unmapCommand = TRIM_NONE; 2828b1d35bdSDavid Sebek *maxLbaCount = 0; 2838b1d35bdSDavid Sebek *maxDescriptorCount = 0; 2848b1d35bdSDavid Sebek 2858b1d35bdSDavid Sebek if (!unmapSupport->commandSupportFilled 2868b1d35bdSDavid Sebek || !unmapSupport->blockLimitsFilled) 2878b1d35bdSDavid Sebek return; 2888b1d35bdSDavid Sebek 2898b1d35bdSDavid Sebek if (unmapSupport->unmapSupported 2908b1d35bdSDavid Sebek && unmapSupport->maxUnmapLbaCount > 0 2918b1d35bdSDavid Sebek && unmapSupport->maxUnmapDescriptorCount > 0) { 2928b1d35bdSDavid Sebek *unmapCommand = TRIM_UNMAP; 2938b1d35bdSDavid Sebek *maxLbaCount = unmapSupport->maxUnmapLbaCount; 2948b1d35bdSDavid Sebek if (unmapSupport->maxUnmapDescriptorCount == UINT32_MAX 2958b1d35bdSDavid Sebek || unmapSupport->maxUnmapDescriptorCount > UNMAP_MAX_DESCRIPTORS) { 2968b1d35bdSDavid Sebek // Choose a reasonable value instead 2978b1d35bdSDavid Sebek *maxDescriptorCount = UNMAP_DEFAULT_DESCRIPTORS; 2988b1d35bdSDavid Sebek } else { 2998b1d35bdSDavid Sebek *maxDescriptorCount = unmapSupport->maxUnmapDescriptorCount; 3008b1d35bdSDavid Sebek } 3018b1d35bdSDavid Sebek } 3028b1d35bdSDavid Sebek 3038b1d35bdSDavid Sebek if (*unmapCommand == TRIM_NONE && unmapSupport->ws16Supported) { 3048b1d35bdSDavid Sebek uint64 maxLength = unmapSupport->maxWritesameLength; 3058b1d35bdSDavid Sebek if (maxLength == 0) { 3068b1d35bdSDavid Sebek // WRITE SAME limit not reported, try UNMAP limit instead 3078b1d35bdSDavid Sebek if (unmapSupport->maxUnmapLbaCount > 0) 3088b1d35bdSDavid Sebek maxLength = unmapSupport->maxUnmapLbaCount; 3098b1d35bdSDavid Sebek else 3108b1d35bdSDavid Sebek maxLength = WS16_MAX_BLOCK_COUNT_VALUE; 3118b1d35bdSDavid Sebek } 3128b1d35bdSDavid Sebek *unmapCommand = TRIM_WRITESAME16; 3138b1d35bdSDavid Sebek *maxLbaCount = min_c(maxLength, WS16_MAX_BLOCK_COUNT_VALUE); 3148b1d35bdSDavid Sebek *maxDescriptorCount = 1; 3158b1d35bdSDavid Sebek } 3168b1d35bdSDavid Sebek 3178b1d35bdSDavid Sebek if (*unmapCommand == TRIM_NONE && unmapSupport->ws10Supported) { 3188b1d35bdSDavid Sebek uint64 maxLength = unmapSupport->maxWritesameLength; 3198b1d35bdSDavid Sebek if (maxLength == 0) { 3208b1d35bdSDavid Sebek // WRITE SAME limit not reported, try UNMAP limit instead 3218b1d35bdSDavid Sebek if (unmapSupport->maxUnmapLbaCount > 0) 3228b1d35bdSDavid Sebek maxLength = unmapSupport->maxUnmapLbaCount; 3238b1d35bdSDavid Sebek else 3248b1d35bdSDavid Sebek maxLength = WS10_MAX_BLOCK_COUNT_VALUE; 3258b1d35bdSDavid Sebek } 3268b1d35bdSDavid Sebek *unmapCommand = TRIM_WRITESAME10; 3278b1d35bdSDavid Sebek *maxLbaCount = min_c(maxLength, WS10_MAX_BLOCK_COUNT_VALUE); 3288b1d35bdSDavid Sebek *maxDescriptorCount = 1; 3298b1d35bdSDavid Sebek } 3308b1d35bdSDavid Sebek } 3318b1d35bdSDavid Sebek 3328b1d35bdSDavid Sebek 33324593e2cSAxel Dörfler status_t 33424593e2cSAxel Dörfler periph_check_capacity(scsi_periph_device_info* device, scsi_ccb* request) 33524593e2cSAxel Dörfler { 3368b1d35bdSDavid Sebek CapacityInfo capacityInfo = {0}; 33724593e2cSAxel Dörfler status_t res; 33824593e2cSAxel Dörfler 33924593e2cSAxel Dörfler SHOW_FLOW(3, "%p, %p", device, request); 34024593e2cSAxel Dörfler 34124593e2cSAxel Dörfler // driver doesn't support capacity callback - seems to be no block 34224593e2cSAxel Dörfler // device driver, so ignore 34324593e2cSAxel Dörfler if (device->callbacks->set_capacity == NULL) 34424593e2cSAxel Dörfler return B_OK; 34524593e2cSAxel Dörfler 3468b1d35bdSDavid Sebek if (prefer_read_capacity_16(device)) { 3478b1d35bdSDavid Sebek SHOW_FLOW0(3, "READ CAPACITY 16 tried first"); 3488b1d35bdSDavid Sebek res = read_capacity_16(device, request, &capacityInfo); 34924593e2cSAxel Dörfler 3508b1d35bdSDavid Sebek if (res == B_ERROR) { 3518b1d35bdSDavid Sebek SHOW_FLOW0(3, "READ CAPACITY 16 failed, trying READ CAPACITY 10"); 3528b1d35bdSDavid Sebek res = read_capacity_10(device, request, &capacityInfo); 3538b1d35bdSDavid Sebek } 3548b1d35bdSDavid Sebek } else { 3558b1d35bdSDavid Sebek SHOW_FLOW0(3, "READ CAPACITY 10 tried first"); 3568b1d35bdSDavid Sebek res = read_capacity_10(device, request, &capacityInfo); 35724593e2cSAxel Dörfler 3588b1d35bdSDavid Sebek if (res == B_OK && capacityInfo.capacityFilled 3598b1d35bdSDavid Sebek && capacityInfo.lastLba == UINT32_MAX) { 3608b1d35bdSDavid Sebek SHOW_FLOW0(3, "Device is too large, trying READ CAPACITY 16"); 3618b1d35bdSDavid Sebek res = read_capacity_16(device, request, &capacityInfo); 3628b1d35bdSDavid Sebek } 3638b1d35bdSDavid Sebek } 36424593e2cSAxel Dörfler 3658b1d35bdSDavid Sebek uint64 capacity; 366*688acf41SJérôme Duval uint32 blockSize, physicalBlockSize; 3678b1d35bdSDavid Sebek 3688b1d35bdSDavid Sebek if (capacityInfo.capacityFilled) { 3698b1d35bdSDavid Sebek capacity = capacityInfo.lastLba + 1; 3708b1d35bdSDavid Sebek blockSize = capacityInfo.blockSize; 371*688acf41SJérôme Duval physicalBlockSize = capacityInfo.physicalBlockSize; 3728b1d35bdSDavid Sebek } else { 3738b1d35bdSDavid Sebek capacity = 0; 3748b1d35bdSDavid Sebek blockSize = 0; 375*688acf41SJérôme Duval physicalBlockSize = 0; 3768b1d35bdSDavid Sebek } 3778b1d35bdSDavid Sebek 3788b1d35bdSDavid Sebek enum trim_command unmapCommand = TRIM_NONE; 3798b1d35bdSDavid Sebek uint32 maxLbaCount = 0; 3808b1d35bdSDavid Sebek uint32 maxDescriptorCount = 0; 3818b1d35bdSDavid Sebek 3828b1d35bdSDavid Sebek if (capacityInfo.provisioningFilled 3838b1d35bdSDavid Sebek && capacityInfo.lbpme 3848b1d35bdSDavid Sebek && vpd_pages_supported(device)) { 3858b1d35bdSDavid Sebek UnmapSupport unmapSupport = {0}; 3868b1d35bdSDavid Sebek 3878b1d35bdSDavid Sebek // Don't fail if the device doesn't support the command 3888b1d35bdSDavid Sebek // but fail if some other error happens 3898b1d35bdSDavid Sebek if (res == B_OK) { 3908b1d35bdSDavid Sebek status_t vpdStatus = get_unmap_commands(device, request, 3918b1d35bdSDavid Sebek &unmapSupport); 3928b1d35bdSDavid Sebek if (vpdStatus != B_OK && vpdStatus != B_ERROR) 3938b1d35bdSDavid Sebek res = vpdStatus; 3948b1d35bdSDavid Sebek } 3958b1d35bdSDavid Sebek 3968b1d35bdSDavid Sebek if (res == B_OK) { 3978b1d35bdSDavid Sebek status_t vpdStatus = get_unmap_limits(device, request, 3988b1d35bdSDavid Sebek &unmapSupport); 3998b1d35bdSDavid Sebek if (vpdStatus != B_OK && vpdStatus != B_ERROR) 4008b1d35bdSDavid Sebek res = vpdStatus; 4018b1d35bdSDavid Sebek } 4028b1d35bdSDavid Sebek 4038b1d35bdSDavid Sebek determine_unmap_support(&unmapSupport, &unmapCommand, 4048b1d35bdSDavid Sebek &maxLbaCount, &maxDescriptorCount); 4058b1d35bdSDavid Sebek 4068b1d35bdSDavid Sebek if (maxLbaCount == 0 || maxDescriptorCount == 0) 4078b1d35bdSDavid Sebek unmapCommand = TRIM_NONE; 4088b1d35bdSDavid Sebek } 40924593e2cSAxel Dörfler 41024593e2cSAxel Dörfler if (res == B_DEV_MEDIA_CHANGED) { 41124593e2cSAxel Dörfler // in this case, the error handler has already called check_capacity 41224593e2cSAxel Dörfler // recursively, so we ignore our (invalid) result 41324593e2cSAxel Dörfler SHOW_FLOW0(3, "ignore result because medium change"); 41424593e2cSAxel Dörfler return B_DEV_MEDIA_CHANGED; 41524593e2cSAxel Dörfler } 41624593e2cSAxel Dörfler 4178b1d35bdSDavid Sebek if (res == B_OK && !capacityInfo.capacityFilled) 4188b1d35bdSDavid Sebek // Although the capacity and block size will be set to 0 in this case, 4198b1d35bdSDavid Sebek // it is also better to inform the caller that these values were not 4208b1d35bdSDavid Sebek // reported by the device 4218b1d35bdSDavid Sebek res = B_ERROR; 42224593e2cSAxel Dörfler 4238b1d35bdSDavid Sebek SHOW_FLOW(3, "capacity = %" B_PRIu64 ", block_size = %" B_PRIu32 4248b1d35bdSDavid Sebek " (%sreported)", capacity, blockSize, 4258b1d35bdSDavid Sebek capacityInfo.capacityFilled ? "" : "not "); 4268b1d35bdSDavid Sebek SHOW_INFO(1, "TRIM: Setting trim support to %s", 4278b1d35bdSDavid Sebek unmapCommand == TRIM_NONE ? "disabled" 4288b1d35bdSDavid Sebek : unmapCommand == TRIM_UNMAP ? "UNMAP" 4298b1d35bdSDavid Sebek : unmapCommand == TRIM_WRITESAME16 ? "WRITE SAME (16)" 4308b1d35bdSDavid Sebek : unmapCommand == TRIM_WRITESAME10 ? "WRITE SAME (10)" 4318b1d35bdSDavid Sebek : "unknown"); 4328b1d35bdSDavid Sebek SHOW_FLOW(3, "TRIM: Block limits: size = %" B_PRIu32 4338b1d35bdSDavid Sebek ", descriptors = %" B_PRIu32, maxLbaCount, maxDescriptorCount); 434f10a55a6SJérôme Duval 435a1e8da41SAxel Dörfler mutex_lock(&device->mutex); 4368b1d35bdSDavid Sebek // Was there a reason why this mutex 4378b1d35bdSDavid Sebek // was previously locked much earlier? 438f10a55a6SJérôme Duval 4398b1d35bdSDavid Sebek device->unmap_command = unmapCommand; 4408b1d35bdSDavid Sebek device->max_unmap_lba_count = maxLbaCount; 4418b1d35bdSDavid Sebek device->max_unmap_descriptor_count = maxDescriptorCount; 44224593e2cSAxel Dörfler 44324593e2cSAxel Dörfler device->block_size = blockSize; 444*688acf41SJérôme Duval device->physical_block_size = physicalBlockSize; 44524593e2cSAxel Dörfler 44624593e2cSAxel Dörfler device->callbacks->set_capacity(device->periph_device, 447*688acf41SJérôme Duval capacity, blockSize, physicalBlockSize); 44824593e2cSAxel Dörfler 44924593e2cSAxel Dörfler /* device->byte2blk_shift = log2( device->block_size ); 45024593e2cSAxel Dörfler if( device->byte2blk_shift < 0 ) { 45124593e2cSAxel Dörfler // this may be too restrictive... 45224593e2cSAxel Dörfler device->capacity = -1; 45324593e2cSAxel Dörfler return ERR_DEV_GENERAL; 45424593e2cSAxel Dörfler }*/ 45524593e2cSAxel Dörfler 456a1e8da41SAxel Dörfler mutex_unlock(&device->mutex); 45724593e2cSAxel Dörfler 45824593e2cSAxel Dörfler SHOW_FLOW(3, "done (%s)", strerror(res)); 45924593e2cSAxel Dörfler 46024593e2cSAxel Dörfler return res; 46124593e2cSAxel Dörfler } 46224593e2cSAxel Dörfler 463960c56aeSAxel Dörfler 4648b1d35bdSDavid Sebek static status_t 4658b1d35bdSDavid Sebek trim_unmap(scsi_periph_device_info* device, scsi_ccb* request, 4668b1d35bdSDavid Sebek scsi_block_range* ranges, uint32 rangeCount, uint64* trimmedBlocks) 467960c56aeSAxel Dörfler { 4688b1d35bdSDavid Sebek uint64 maxLength = UNMAP_MAX_BLOCK_COUNT_VALUE; 4698b1d35bdSDavid Sebek uint64 maxBlocksInRequest = device->max_unmap_lba_count; 4708b1d35bdSDavid Sebek uint32 maxDescriptors = device->max_unmap_descriptor_count; 4718b1d35bdSDavid Sebek 4728b1d35bdSDavid Sebek *trimmedBlocks = 0; 4738b1d35bdSDavid Sebek 4748b1d35bdSDavid Sebek // Allocate a single buffer and re-use it between requests 4758b1d35bdSDavid Sebek size_t expectedDescriptorCount = 0; 4768b1d35bdSDavid Sebek for (uint32 i = 0; i < rangeCount; i++) { 4778b1d35bdSDavid Sebek expectedDescriptorCount += ranges[i].size / maxLength; 4788b1d35bdSDavid Sebek if (ranges[i].size % maxLength != 0) 4798b1d35bdSDavid Sebek expectedDescriptorCount++; 4808b1d35bdSDavid Sebek } 4818b1d35bdSDavid Sebek expectedDescriptorCount = min_c(expectedDescriptorCount, maxDescriptors); 4828b1d35bdSDavid Sebek 4838b1d35bdSDavid Sebek size_t unmapListAllocatedSize = (expectedDescriptorCount - 1) 48499086aa3SAxel Dörfler * sizeof(scsi_unmap_block_descriptor) 48599086aa3SAxel Dörfler + sizeof(scsi_unmap_parameter_list); 48699086aa3SAxel Dörfler 4878b1d35bdSDavid Sebek scsi_unmap_parameter_list* unmapList 4888b1d35bdSDavid Sebek = (scsi_unmap_parameter_list*)malloc(unmapListAllocatedSize); 4898b1d35bdSDavid Sebek if (unmapList == NULL) 49099086aa3SAxel Dörfler return B_NO_MEMORY; 49199086aa3SAxel Dörfler 4928b1d35bdSDavid Sebek MemoryDeleter deleter(unmapList); 49399086aa3SAxel Dörfler 4948b1d35bdSDavid Sebek status_t status = B_OK; 4958b1d35bdSDavid Sebek uint32 descriptorIndex = 0; 4968b1d35bdSDavid Sebek uint64 trimmedBlocksInRequest = 0; 4978b1d35bdSDavid Sebek memset(unmapList, 0, unmapListAllocatedSize); 49899086aa3SAxel Dörfler for (uint32 i = 0; i < rangeCount; i++) { 4998b1d35bdSDavid Sebek uint64 lba = ranges[i].lba; 5008b1d35bdSDavid Sebek uint64 length = ranges[i].size; 5018b1d35bdSDavid Sebek 5028b1d35bdSDavid Sebek if (length == 0) 5038b1d35bdSDavid Sebek continue; // Length of 0 would be ignored by the device anyway 5048b1d35bdSDavid Sebek 5058b1d35bdSDavid Sebek if (lba > UNMAP_MAX_LBA_VALUE) { 5068b1d35bdSDavid Sebek SHOW_ERROR0(1, "LBA value is too large!" 5078b1d35bdSDavid Sebek " This unmap range will be skipped."); 5088b1d35bdSDavid Sebek continue; 50999086aa3SAxel Dörfler } 51099086aa3SAxel Dörfler 5118b1d35bdSDavid Sebek // Split large ranges if needed. 5128b1d35bdSDavid Sebek // Range length is limited by: 5138b1d35bdSDavid Sebek // - the UNMAP_MAX_BLOCK_COUNT_VALUE constant 5148b1d35bdSDavid Sebek // - the total number of LBAs in one UNMAP command is limited by 5158b1d35bdSDavid Sebek // the MAX UNMAP LBA COUNT field in the Block Limits VPD page 5168b1d35bdSDavid Sebek while (length > 0) { 5178b1d35bdSDavid Sebek uint64 trimLength = min_c(length, maxLength); 5188b1d35bdSDavid Sebek trimLength = min_c(trimLength, 5198b1d35bdSDavid Sebek maxBlocksInRequest - trimmedBlocksInRequest); 5208b1d35bdSDavid Sebek unmapList->blocks[descriptorIndex].lba 5218b1d35bdSDavid Sebek = B_HOST_TO_BENDIAN_INT64(lba); 5228b1d35bdSDavid Sebek unmapList->blocks[descriptorIndex].block_count 5238b1d35bdSDavid Sebek = B_HOST_TO_BENDIAN_INT32(trimLength); 5248b1d35bdSDavid Sebek descriptorIndex++; 5258b1d35bdSDavid Sebek trimmedBlocksInRequest += trimLength; 5268b1d35bdSDavid Sebek 5278b1d35bdSDavid Sebek // Split into multiple requests if needed. 5288b1d35bdSDavid Sebek // The number of UNMAP block descriptors is limited by: 5298b1d35bdSDavid Sebek // - the number of block descriptors cannot exceed the 5308b1d35bdSDavid Sebek // MAXIMUM UNMAP PARAMETER COUNT value in the Block Limits VPD 5318b1d35bdSDavid Sebek // - the size of our buffer 5328b1d35bdSDavid Sebek // - what fits in one UNMAP command 5338b1d35bdSDavid Sebek // - the total number of LBAs in one UNMAP command is limited by 5348b1d35bdSDavid Sebek // the MAX UNMAP LBA COUNT field in the Block Limits VPD page 5358b1d35bdSDavid Sebek if (descriptorIndex >= maxDescriptors 5368b1d35bdSDavid Sebek || descriptorIndex >= expectedDescriptorCount 5378b1d35bdSDavid Sebek || descriptorIndex >= UNMAP_MAX_DESCRIPTORS 5388b1d35bdSDavid Sebek || trimmedBlocksInRequest >= maxBlocksInRequest 5398b1d35bdSDavid Sebek || (i == rangeCount - 1 && length <= maxLength)) 5408b1d35bdSDavid Sebek { 5418b1d35bdSDavid Sebek uint16 unmapListSize = (descriptorIndex - 1) 5428b1d35bdSDavid Sebek * sizeof(scsi_unmap_block_descriptor) 5438b1d35bdSDavid Sebek + sizeof(scsi_unmap_parameter_list); 5448b1d35bdSDavid Sebek unmapList->data_length = B_HOST_TO_BENDIAN_INT16(unmapListSize 5458b1d35bdSDavid Sebek - offsetof(scsi_unmap_parameter_list, block_data_length)); 5468b1d35bdSDavid Sebek unmapList->block_data_length 5478b1d35bdSDavid Sebek = B_HOST_TO_BENDIAN_INT16(unmapListSize 5488b1d35bdSDavid Sebek - offsetof(scsi_unmap_parameter_list, blocks)); 549960c56aeSAxel Dörfler 55099086aa3SAxel Dörfler scsi_cmd_unmap* cmd = (scsi_cmd_unmap*)request->cdb; 551960c56aeSAxel Dörfler memset(cmd, 0, sizeof(*cmd)); 55299086aa3SAxel Dörfler cmd->opcode = SCSI_OP_UNMAP; 5538b1d35bdSDavid Sebek cmd->length = B_HOST_TO_BENDIAN_INT16(unmapListSize); 55499086aa3SAxel Dörfler 5558b1d35bdSDavid Sebek request->flags = SCSI_DIR_OUT; 556960c56aeSAxel Dörfler request->cdb_length = sizeof(*cmd); 5578b1d35bdSDavid Sebek request->sort = B_BENDIAN_TO_HOST_INT64( 5588b1d35bdSDavid Sebek unmapList->blocks[0].lba); 5598b1d35bdSDavid Sebek request->timeout = device->std_timeout; 560960c56aeSAxel Dörfler 5618b1d35bdSDavid Sebek request->data = (uint8*)unmapList; 5628b1d35bdSDavid Sebek request->data_length = unmapListSize; 5638b1d35bdSDavid Sebek request->sg_list = NULL; 5648b1d35bdSDavid Sebek 5658b1d35bdSDavid Sebek SHOW_FLOW(3, "UNMAP data used %" B_PRIu16 5668b1d35bdSDavid Sebek " of %" B_PRIuSIZE " allocated bytes", 5678b1d35bdSDavid Sebek unmapListSize, unmapListAllocatedSize); 5688b1d35bdSDavid Sebek 5698b1d35bdSDavid Sebek #ifdef DEBUG_TRIM 5708b1d35bdSDavid Sebek uint16 scsiRangeCount = (uint16)B_BENDIAN_TO_HOST_INT16( 5718b1d35bdSDavid Sebek unmapList->block_data_length) 5728b1d35bdSDavid Sebek / sizeof(scsi_unmap_block_descriptor); 5738b1d35bdSDavid Sebek uint64 count = 0; 5748b1d35bdSDavid Sebek dprintf("TRIM: SCSI: sending an UNMAP command to" 5758b1d35bdSDavid Sebek " the device (blocks):\n"); 5768b1d35bdSDavid Sebek for (uint16 i = 0; i < scsiRangeCount; i++) { 5778b1d35bdSDavid Sebek dprintf("[%3" B_PRIu16 "] %" B_PRIu64 " : %" B_PRIu32 "\n", 5788b1d35bdSDavid Sebek i, (uint64)B_BENDIAN_TO_HOST_INT64( 5798b1d35bdSDavid Sebek unmapList->blocks[i].lba), 5808b1d35bdSDavid Sebek (uint32)B_BENDIAN_TO_HOST_INT32( 5818b1d35bdSDavid Sebek unmapList->blocks[i].block_count)); 5828b1d35bdSDavid Sebek count += (uint32)B_BENDIAN_TO_HOST_INT32( 5838b1d35bdSDavid Sebek unmapList->blocks[i].block_count); 5848b1d35bdSDavid Sebek } 5858b1d35bdSDavid Sebek if (device->max_unmap_lba_count >= count) 5868b1d35bdSDavid Sebek dprintf("TRIM: SCSI: Previous UNMAP command would fit %" 5878b1d35bdSDavid Sebek B_PRIu64 " more LBAs\n", 5888b1d35bdSDavid Sebek device->max_unmap_lba_count - count); 5898b1d35bdSDavid Sebek else 5908b1d35bdSDavid Sebek dprintf("TRIM: SCSI: Previous UNMAP ranges exceed the" 5918b1d35bdSDavid Sebek " device limit!\n"); 5928b1d35bdSDavid Sebek #endif /* DEBUG_TRIM */ 5938b1d35bdSDavid Sebek 5948b1d35bdSDavid Sebek status = periph_safe_exec(device, request); 595960c56aeSAxel Dörfler 596960c56aeSAxel Dörfler // peripheral layer only creates "read" error 597e6bde50aSAxel Dörfler if (status == B_DEV_READ_ERROR) 598960c56aeSAxel Dörfler return B_DEV_WRITE_ERROR; 5998b1d35bdSDavid Sebek else if (status != B_OK) 6008b1d35bdSDavid Sebek return status; 6018b1d35bdSDavid Sebek 6028b1d35bdSDavid Sebek *trimmedBlocks += trimmedBlocksInRequest; 6038b1d35bdSDavid Sebek 6048b1d35bdSDavid Sebek descriptorIndex = 0; 6058b1d35bdSDavid Sebek trimmedBlocksInRequest = 0; 6068b1d35bdSDavid Sebek memset(unmapList, 0, unmapListSize); 6078b1d35bdSDavid Sebek } 6088b1d35bdSDavid Sebek 6098b1d35bdSDavid Sebek length -= trimLength; 6108b1d35bdSDavid Sebek lba += trimLength; 6118b1d35bdSDavid Sebek } 6128b1d35bdSDavid Sebek } 613960c56aeSAxel Dörfler 614e6bde50aSAxel Dörfler return status; 615960c56aeSAxel Dörfler } 616960c56aeSAxel Dörfler 6178b1d35bdSDavid Sebek 6188b1d35bdSDavid Sebek static status_t 6198b1d35bdSDavid Sebek trim_writesame16(scsi_periph_device_info* device, scsi_ccb* request, 6208b1d35bdSDavid Sebek scsi_block_range* ranges, uint32 rangeCount, uint64* trimmedBlocks) 6218b1d35bdSDavid Sebek { 6228b1d35bdSDavid Sebek status_t status = B_OK; 6238b1d35bdSDavid Sebek *trimmedBlocks = 0; 6248b1d35bdSDavid Sebek 6258b1d35bdSDavid Sebek for (uint32 i = 0; i < rangeCount; i++) { 6268b1d35bdSDavid Sebek uint64 lba = ranges[i].lba; 6278b1d35bdSDavid Sebek uint64 length = ranges[i].size; 6288b1d35bdSDavid Sebek 6298b1d35bdSDavid Sebek if (length == 0) 6308b1d35bdSDavid Sebek continue; // length of 0 would mean the rest of the device! 6318b1d35bdSDavid Sebek 6328b1d35bdSDavid Sebek if (lba > WS16_MAX_LBA_VALUE) { 6338b1d35bdSDavid Sebek SHOW_ERROR0(1, "LBA value is too large!" 6348b1d35bdSDavid Sebek " This unmap range will be skipped."); 6358b1d35bdSDavid Sebek continue; 6368b1d35bdSDavid Sebek } 6378b1d35bdSDavid Sebek 6388b1d35bdSDavid Sebek // Split the range into multiple requests if needed 6398b1d35bdSDavid Sebek uint64 maxLength = min_c(device->max_unmap_lba_count, 6408b1d35bdSDavid Sebek WS16_MAX_BLOCK_COUNT_VALUE); 6418b1d35bdSDavid Sebek while (length > 0) { 6428b1d35bdSDavid Sebek uint64 trimLength = min_c(length, maxLength); 6438b1d35bdSDavid Sebek if (trimLength == 0) { 6448b1d35bdSDavid Sebek SHOW_ERROR0(1, 6458b1d35bdSDavid Sebek "Error: Length of zero in WRITE SAME (16) detected"); 6468b1d35bdSDavid Sebek break; 6478b1d35bdSDavid Sebek } 6488b1d35bdSDavid Sebek 6498b1d35bdSDavid Sebek void* block = malloc(device->block_size); 6508b1d35bdSDavid Sebek if (block == NULL) 6518b1d35bdSDavid Sebek return B_NO_MEMORY; 6528b1d35bdSDavid Sebek MemoryDeleter deleter(block); 6538b1d35bdSDavid Sebek memset(block, 0, device->block_size); 6548b1d35bdSDavid Sebek 6558b1d35bdSDavid Sebek scsi_cmd_wsame_16* cmd = (scsi_cmd_wsame_16*)request->cdb; 6568b1d35bdSDavid Sebek memset(cmd, 0, sizeof(*cmd)); 6578b1d35bdSDavid Sebek cmd->opcode = SCSI_OP_WRITE_SAME_16; 6588b1d35bdSDavid Sebek cmd->unmap = 1; 6598b1d35bdSDavid Sebek cmd->lba = B_HOST_TO_BENDIAN_INT64(lba); 6608b1d35bdSDavid Sebek cmd->length = B_HOST_TO_BENDIAN_INT32(trimLength); 6618b1d35bdSDavid Sebek //cmd->ndob = 1; // no data is needed if this bit is enabled 6628b1d35bdSDavid Sebek 6638b1d35bdSDavid Sebek request->flags = SCSI_DIR_OUT; 6648b1d35bdSDavid Sebek request->cdb_length = sizeof(*cmd); 6658b1d35bdSDavid Sebek request->sort = lba; 6668b1d35bdSDavid Sebek request->timeout = device->std_timeout; 6678b1d35bdSDavid Sebek 6688b1d35bdSDavid Sebek request->data = (uint8*)block; 6698b1d35bdSDavid Sebek request->data_length = device->block_size; 6708b1d35bdSDavid Sebek request->sg_list = NULL; 6718b1d35bdSDavid Sebek 6728b1d35bdSDavid Sebek #ifdef DEBUG_TRIM 6738b1d35bdSDavid Sebek dprintf("TRIM: SCSI: sending a WRITE SAME (16) command to" 6748b1d35bdSDavid Sebek " the device (blocks):\n"); 6758b1d35bdSDavid Sebek dprintf("%" B_PRIu64 " : %" B_PRIu32 "\n", 6768b1d35bdSDavid Sebek (uint64)B_BENDIAN_TO_HOST_INT64(cmd->lba), 6778b1d35bdSDavid Sebek (uint32)B_BENDIAN_TO_HOST_INT32(cmd->length)); 6788b1d35bdSDavid Sebek #endif 6798b1d35bdSDavid Sebek 6808b1d35bdSDavid Sebek status = periph_safe_exec(device, request); 6818b1d35bdSDavid Sebek 6828b1d35bdSDavid Sebek // peripheral layer only creates "read" error 6838b1d35bdSDavid Sebek if (status == B_DEV_READ_ERROR) 6848b1d35bdSDavid Sebek return B_DEV_WRITE_ERROR; 6858b1d35bdSDavid Sebek else if (status != B_OK) 6868b1d35bdSDavid Sebek return status; 6878b1d35bdSDavid Sebek 6888b1d35bdSDavid Sebek *trimmedBlocks += trimLength; 6898b1d35bdSDavid Sebek length -= trimLength; 6908b1d35bdSDavid Sebek lba += trimLength; 6918b1d35bdSDavid Sebek } 6928b1d35bdSDavid Sebek } 6938b1d35bdSDavid Sebek 6948b1d35bdSDavid Sebek return status; 6958b1d35bdSDavid Sebek } 6968b1d35bdSDavid Sebek 6978b1d35bdSDavid Sebek 6988b1d35bdSDavid Sebek static status_t 6998b1d35bdSDavid Sebek trim_writesame10(scsi_periph_device_info* device, scsi_ccb* request, 7008b1d35bdSDavid Sebek scsi_block_range* ranges, uint32 rangeCount, uint64* trimmedBlocks) 7018b1d35bdSDavid Sebek { 7028b1d35bdSDavid Sebek status_t status = B_OK; 7038b1d35bdSDavid Sebek *trimmedBlocks = 0; 7048b1d35bdSDavid Sebek 7058b1d35bdSDavid Sebek for (uint32 i = 0; i < rangeCount; i++) { 7068b1d35bdSDavid Sebek uint64 lba = ranges[i].lba; 7078b1d35bdSDavid Sebek uint64 length = ranges[i].size; 7088b1d35bdSDavid Sebek 7098b1d35bdSDavid Sebek if (length == 0) 7108b1d35bdSDavid Sebek continue; // length of 0 would mean the rest of the device! 7118b1d35bdSDavid Sebek 7128b1d35bdSDavid Sebek if (lba > WS10_MAX_LBA_VALUE) { 7138b1d35bdSDavid Sebek SHOW_ERROR0(1, "LBA value is too large!" 7148b1d35bdSDavid Sebek " This unmap range will be skipped."); 7158b1d35bdSDavid Sebek continue; 7168b1d35bdSDavid Sebek } 7178b1d35bdSDavid Sebek 7188b1d35bdSDavid Sebek // Split the range into multiple requests if needed 7198b1d35bdSDavid Sebek uint64 maxLength = min_c(device->max_unmap_lba_count, 7208b1d35bdSDavid Sebek WS10_MAX_BLOCK_COUNT_VALUE); 7218b1d35bdSDavid Sebek while (length > 0) { 7228b1d35bdSDavid Sebek uint64 trimLength = min_c(length, maxLength); 7238b1d35bdSDavid Sebek if (trimLength == 0) { 7248b1d35bdSDavid Sebek SHOW_ERROR0(1, 7258b1d35bdSDavid Sebek "Error: Length of zero in WRITE SAME (10) detected"); 7268b1d35bdSDavid Sebek break; 7278b1d35bdSDavid Sebek } 7288b1d35bdSDavid Sebek 7298b1d35bdSDavid Sebek void* block = malloc(device->block_size); 7308b1d35bdSDavid Sebek if (block == NULL) 7318b1d35bdSDavid Sebek return B_NO_MEMORY; 7328b1d35bdSDavid Sebek MemoryDeleter deleter(block); 7338b1d35bdSDavid Sebek memset(block, 0, device->block_size); 7348b1d35bdSDavid Sebek 7358b1d35bdSDavid Sebek scsi_cmd_wsame_10* cmd = (scsi_cmd_wsame_10*)request->cdb; 7368b1d35bdSDavid Sebek memset(cmd, 0, sizeof(*cmd)); 7378b1d35bdSDavid Sebek cmd->opcode = SCSI_OP_WRITE_SAME_10; 7388b1d35bdSDavid Sebek cmd->unmap = 1; 7398b1d35bdSDavid Sebek cmd->lba = B_HOST_TO_BENDIAN_INT32(lba); 7408b1d35bdSDavid Sebek cmd->length = B_HOST_TO_BENDIAN_INT16(trimLength); 7418b1d35bdSDavid Sebek 7428b1d35bdSDavid Sebek request->flags = SCSI_DIR_OUT; 7438b1d35bdSDavid Sebek request->cdb_length = sizeof(*cmd); 7448b1d35bdSDavid Sebek request->sort = lba; 7458b1d35bdSDavid Sebek request->timeout = device->std_timeout; 7468b1d35bdSDavid Sebek 7478b1d35bdSDavid Sebek request->data = (uint8*)block; 7488b1d35bdSDavid Sebek request->data_length = device->block_size; 7498b1d35bdSDavid Sebek request->sg_list = NULL; 7508b1d35bdSDavid Sebek 7518b1d35bdSDavid Sebek #ifdef DEBUG_TRIM 7528b1d35bdSDavid Sebek dprintf("TRIM: SCSI: sending a WRITE SAME (10) command to" 7538b1d35bdSDavid Sebek " the device (blocks):\n"); 7548b1d35bdSDavid Sebek dprintf("%" B_PRIu32 " : %" B_PRIu16 "\n", 7558b1d35bdSDavid Sebek (uint32)B_BENDIAN_TO_HOST_INT32(cmd->lba), 7568b1d35bdSDavid Sebek (uint16)B_BENDIAN_TO_HOST_INT16(cmd->length)); 7578b1d35bdSDavid Sebek #endif 7588b1d35bdSDavid Sebek 7598b1d35bdSDavid Sebek status = periph_safe_exec(device, request); 7608b1d35bdSDavid Sebek 7618b1d35bdSDavid Sebek // peripheral layer only creates "read" error 7628b1d35bdSDavid Sebek if (status == B_DEV_READ_ERROR) 7638b1d35bdSDavid Sebek return B_DEV_WRITE_ERROR; 7648b1d35bdSDavid Sebek else if (status != B_OK) 7658b1d35bdSDavid Sebek return status; 7668b1d35bdSDavid Sebek 7678b1d35bdSDavid Sebek *trimmedBlocks += trimLength; 7688b1d35bdSDavid Sebek length -= trimLength; 7698b1d35bdSDavid Sebek lba += trimLength; 7708b1d35bdSDavid Sebek } 7718b1d35bdSDavid Sebek } 7728b1d35bdSDavid Sebek 7738b1d35bdSDavid Sebek return status; 7748b1d35bdSDavid Sebek } 7758b1d35bdSDavid Sebek 7768b1d35bdSDavid Sebek 7778b1d35bdSDavid Sebek status_t 7788b1d35bdSDavid Sebek periph_trim_device(scsi_periph_device_info* device, scsi_ccb* request, 7798b1d35bdSDavid Sebek scsi_block_range* ranges, uint32 rangeCount, uint64* trimmedBlocks) 7808b1d35bdSDavid Sebek { 7818b1d35bdSDavid Sebek *trimmedBlocks = 0; 7828b1d35bdSDavid Sebek 7838b1d35bdSDavid Sebek if (device->unmap_command == TRIM_NONE 7848b1d35bdSDavid Sebek || device->max_unmap_lba_count == 0 7858b1d35bdSDavid Sebek || device->max_unmap_descriptor_count == 0) 7868b1d35bdSDavid Sebek return B_UNSUPPORTED; 7878b1d35bdSDavid Sebek 7888b1d35bdSDavid Sebek switch (device->unmap_command) { 7898b1d35bdSDavid Sebek case TRIM_UNMAP: 7908b1d35bdSDavid Sebek return trim_unmap(device, request, ranges, rangeCount, 7918b1d35bdSDavid Sebek trimmedBlocks); 7928b1d35bdSDavid Sebek case TRIM_WRITESAME16: 7938b1d35bdSDavid Sebek return trim_writesame16(device, request, ranges, rangeCount, 7948b1d35bdSDavid Sebek trimmedBlocks); 7958b1d35bdSDavid Sebek case TRIM_WRITESAME10: 7968b1d35bdSDavid Sebek return trim_writesame10(device, request, ranges, rangeCount, 7978b1d35bdSDavid Sebek trimmedBlocks); 7988b1d35bdSDavid Sebek default: 7998b1d35bdSDavid Sebek return B_UNSUPPORTED; 8008b1d35bdSDavid Sebek } 8018b1d35bdSDavid Sebek } 802