xref: /haiku/src/add-ons/kernel/generic/scsi_periph/block.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
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
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
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
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
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
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
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
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
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
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
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
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
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