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