xref: /haiku/src/add-ons/kernel/bus_managers/scsi/scatter_gather.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
1 /*
2  * Copyright 2004-2008, Haiku, Inc. All RightsReserved.
3  * Copyright 2002-2003, Thomas Kurschel. All rights reserved.
4  *
5  * Distributed under the terms of the MIT License.
6  */
7 
8 /*!
9 	Creates temporary Scatter/Gather table if the peripheral
10 	driver has provided a simple pointer only.
11 */
12 
13 #include "scsi_internal.h"
14 #include "KernelExport_ext.h"
15 
16 #include <string.h>
17 #include <iovec.h>
18 
19 #include <algorithm>
20 
21 
22 static locked_pool_cookie temp_sg_pool;
23 
24 
25 static bool
26 fill_temp_sg(scsi_ccb *ccb)
27 {
28 	status_t res;
29 	scsi_bus_info *bus = ccb->bus;
30 	uint32 dma_boundary = bus->dma_params.dma_boundary;
31 	uint32 max_sg_block_size = bus->dma_params.max_sg_block_size;
32 	uint32 max_sg_blocks = std::min(bus->dma_params.max_sg_blocks,
33 		(uint32)MAX_TEMP_SG_FRAGMENTS);
34 	iovec vec = {
35 		ccb->data,
36 		ccb->data_length
37 	};
38 	size_t num_entries;
39 	size_t mapped_len;
40 	uint32 cur_idx;
41 	physical_entry *temp_sg = (physical_entry *)ccb->sg_list;
42 
43 	res = get_iovec_memory_map(&vec, 1, 0, ccb->data_length, temp_sg, max_sg_blocks,
44 				&num_entries, &mapped_len);
45 
46 	if (res != B_OK) {
47 		SHOW_ERROR(2, "cannot create temporary S/G list for IO request (%s)", strerror(res));
48 		return false;
49 	}
50 
51 	if (mapped_len != ccb->data_length)
52 		goto too_complex;
53 
54 	if (dma_boundary != ~(uint32)0 || ccb->data_length > max_sg_block_size) {
55 		// S/G list may not be controller-compatible:
56 		// we have to split offending entries
57 		SHOW_FLOW(3, "Checking violation of dma boundary 0x%x and entry size 0x%x",
58 			(int)dma_boundary, (int)max_sg_block_size);
59 
60 		for (cur_idx = 0; cur_idx < num_entries; ++cur_idx) {
61 			addr_t max_len;
62 
63 			// calculate space upto next dma boundary crossing
64 			max_len = (dma_boundary + 1) -
65 				(temp_sg[cur_idx].address & dma_boundary);
66 			// restrict size per sg item
67 			max_len = std::min(max_len, (addr_t)max_sg_block_size);
68 
69 			SHOW_FLOW(4, "addr=%#" B_PRIxPHYSADDR ", size=%x, max_len=%x, "
70 				"idx=%d, num=%d", temp_sg[cur_idx].address,
71 				(int)temp_sg[cur_idx].size, (int)max_len, (int)cur_idx,
72 				(int)num_entries);
73 
74 			if (max_len < temp_sg[cur_idx].size) {
75 				// split sg block
76 				if (++num_entries > max_sg_blocks)
77 					goto too_complex;
78 
79 				memmove(&temp_sg[cur_idx + 1], &temp_sg[cur_idx],
80 					(num_entries - 1 - cur_idx) * sizeof(physical_entry));
81 
82 				temp_sg[cur_idx].size = max_len;
83 				temp_sg[cur_idx + 1].address
84 					= temp_sg[cur_idx + 1].address + max_len;
85 				temp_sg[cur_idx + 1].size -= max_len;
86 			}
87 		}
88 	}
89 
90 	ccb->sg_count = num_entries;
91 
92 	return true;
93 
94 too_complex:
95 	SHOW_ERROR( 2, "S/G list to complex for IO request (max %d entries)",
96 		MAX_TEMP_SG_FRAGMENTS );
97 
98 	return false;
99 }
100 
101 
102 /** create temporary SG for request */
103 
104 bool
105 create_temp_sg(scsi_ccb *ccb)
106 {
107 	physical_entry *temp_sg;
108 	status_t res;
109 
110 	SHOW_FLOW(3, "ccb=%p, data=%p, data_length=%" B_PRIu32, ccb, ccb->data,
111 		ccb->data_length);
112 
113 	ccb->sg_list = temp_sg = (physical_entry*)locked_pool->alloc(temp_sg_pool);
114 	if (temp_sg == NULL) {
115 		SHOW_ERROR0(2, "cannot allocate memory for IO request!");
116 		return false;
117 	}
118 
119 	res = lock_memory(ccb->data, ccb->data_length, B_DMA_IO
120 		| ((ccb->flags & SCSI_DIR_MASK) == SCSI_DIR_IN ? B_READ_DEVICE : 0));
121 
122 	if (res != B_OK) {
123 		SHOW_ERROR(2, "cannot lock memory for IO request (%s)", strerror(res));
124 		goto err;
125 	}
126 
127 	if (fill_temp_sg(ccb))
128 		// this is the success path
129 		return true;
130 
131 	unlock_memory(ccb->data, ccb->data_length, B_DMA_IO
132 		| ((ccb->flags & SCSI_DIR_MASK) == SCSI_DIR_IN ? B_READ_DEVICE : 0));
133 
134 err:
135 	locked_pool->free(temp_sg_pool, temp_sg);
136 	return false;
137 }
138 
139 
140 /** cleanup temporary SG list */
141 
142 void
143 uninit_temp_sg(void)
144 {
145 	locked_pool->destroy(temp_sg_pool);
146 }
147 
148 
149 /** destroy SG list buffer */
150 
151 void
152 cleanup_tmp_sg(scsi_ccb *ccb)
153 {
154 	status_t res;
155 
156 	SHOW_FLOW(3, "ccb=%p, data=%p, data_length=%d",
157 		ccb, ccb->data, (int)ccb->data_length);
158 
159 	res = unlock_memory(ccb->data, ccb->data_length, B_DMA_IO
160 		|  ((ccb->flags & SCSI_DIR_MASK) == SCSI_DIR_IN ? B_READ_DEVICE : 0));
161 
162 	if (res != B_OK) {
163 		SHOW_FLOW0(3, "Cannot unlock previously locked memory!");
164 		panic("Cannot unlock previously locked memory!");
165 	}
166 
167 	locked_pool->free(temp_sg_pool, (physical_entry *)ccb->sg_list);
168 
169 	// restore previous state
170 	ccb->sg_list = NULL;
171 }
172 
173 
174 /** create SG list buffer */
175 
176 int
177 init_temp_sg(void)
178 {
179 	temp_sg_pool = locked_pool->create(
180 		MAX_TEMP_SG_FRAGMENTS * sizeof(physical_entry),
181 		sizeof(physical_entry) - 1, 0,
182 		B_PAGE_SIZE, MAX_TEMP_SG_LISTS, 1,
183 		"scsi_temp_sg_pool", B_CONTIGUOUS, NULL, NULL, NULL);
184 
185 	if (temp_sg_pool == NULL)
186 		return B_NO_MEMORY;
187 
188 	return B_OK;
189 }
190 
191