Line data Source code
1 : /* SPDX-License-Identifier: BSD-3-Clause
2 : * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
3 : * Copyright (C) 2016 Intel Corporation.
4 : * All rights reserved.
5 : */
6 :
7 : #include "scsi_internal.h"
8 : #include "spdk/endian.h"
9 : #include "spdk/env.h"
10 : #include "spdk/util.h"
11 :
12 : static void
13 2 : scsi_task_free_data(struct spdk_scsi_task *task)
14 : {
15 2 : if (task->alloc_len != 0) {
16 1 : spdk_dma_free(task->iov.iov_base);
17 1 : task->alloc_len = 0;
18 : }
19 :
20 2 : task->iov.iov_base = NULL;
21 2 : task->iov.iov_len = 0;
22 2 : }
23 :
24 : void
25 2 : spdk_scsi_task_put(struct spdk_scsi_task *task)
26 : {
27 2 : if (!task) {
28 0 : return;
29 : }
30 :
31 2 : assert(task->ref > 0);
32 2 : task->ref--;
33 :
34 2 : if (task->ref == 0) {
35 2 : struct spdk_bdev_io *bdev_io = task->bdev_io;
36 :
37 2 : if (bdev_io) {
38 0 : spdk_bdev_free_io(bdev_io);
39 : }
40 :
41 2 : scsi_task_free_data(task);
42 :
43 2 : task->free_fn(task);
44 : }
45 : }
46 :
47 : void
48 23 : spdk_scsi_task_construct(struct spdk_scsi_task *task,
49 : spdk_scsi_task_cpl cpl_fn,
50 : spdk_scsi_task_free free_fn)
51 : {
52 23 : assert(task != NULL);
53 23 : assert(cpl_fn != NULL);
54 23 : assert(free_fn != NULL);
55 :
56 23 : task->cpl_fn = cpl_fn;
57 23 : task->free_fn = free_fn;
58 :
59 23 : task->ref++;
60 :
61 : /*
62 : * Pre-fill the iov_buffers to point to the embedded iov
63 : */
64 23 : assert(task->iov.iov_base == NULL);
65 23 : task->iovs = &task->iov;
66 23 : task->iovcnt = 1;
67 23 : }
68 :
69 : static void *
70 4 : scsi_task_alloc_data(struct spdk_scsi_task *task, uint32_t alloc_len)
71 : {
72 : uint32_t zmalloc_len;
73 :
74 4 : assert(task->alloc_len == 0);
75 :
76 : /* Some ULPs (such as iSCSI) need to round len up to nearest
77 : * 4 bytes. We can help those ULPs by allocating memory here
78 : * up to next 4 byte boundary, so they don't have to worry
79 : * about handling out-of-bounds errors.
80 : */
81 4 : zmalloc_len = 4 * spdk_divide_round_up(alloc_len, 4);
82 4 : task->iov.iov_base = spdk_dma_zmalloc(zmalloc_len, 0, NULL);
83 4 : task->iov.iov_len = alloc_len;
84 4 : task->alloc_len = alloc_len;
85 :
86 4 : return task->iov.iov_base;
87 : }
88 :
89 : int
90 261 : spdk_scsi_task_scatter_data(struct spdk_scsi_task *task, const void *src, size_t buf_len)
91 : {
92 261 : size_t len = 0;
93 261 : size_t buf_left = buf_len;
94 : int i;
95 261 : struct iovec *iovs = task->iovs;
96 : const uint8_t *pos;
97 :
98 261 : if (buf_len == 0) {
99 2 : return 0;
100 : }
101 :
102 259 : if (task->iovcnt == 1 && iovs[0].iov_base == NULL) {
103 4 : scsi_task_alloc_data(task, buf_len);
104 4 : iovs[0] = task->iov;
105 : }
106 :
107 518 : for (i = 0; i < task->iovcnt; i++) {
108 259 : assert(iovs[i].iov_base != NULL);
109 259 : len += iovs[i].iov_len;
110 : }
111 :
112 259 : if (len < buf_len) {
113 0 : spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
114 : SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
115 : SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
116 : SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
117 0 : return -1;
118 : }
119 :
120 259 : pos = src;
121 :
122 518 : for (i = 0; i < task->iovcnt; i++) {
123 259 : len = spdk_min(iovs[i].iov_len, buf_left);
124 259 : buf_left -= len;
125 259 : memcpy(iovs[i].iov_base, pos, len);
126 259 : pos += len;
127 : }
128 :
129 259 : return buf_len;
130 : }
131 :
132 : void *
133 1 : spdk_scsi_task_gather_data(struct spdk_scsi_task *task, int *len)
134 : {
135 : int i;
136 1 : struct iovec *iovs = task->iovs;
137 1 : size_t buf_len = 0;
138 : uint8_t *buf, *pos;
139 :
140 2 : for (i = 0; i < task->iovcnt; i++) {
141 : /* It is OK for iov_base to be NULL if iov_len is 0. */
142 1 : assert(iovs[i].iov_base != NULL || iovs[i].iov_len == 0);
143 1 : buf_len += iovs[i].iov_len;
144 : }
145 :
146 1 : if (buf_len == 0) {
147 0 : *len = 0;
148 0 : return NULL;
149 : }
150 :
151 1 : buf = calloc(1, buf_len);
152 1 : if (buf == NULL) {
153 0 : *len = -1;
154 0 : return NULL;
155 : }
156 :
157 1 : pos = buf;
158 2 : for (i = 0; i < task->iovcnt; i++) {
159 1 : memcpy(pos, iovs[i].iov_base, iovs[i].iov_len);
160 1 : pos += iovs[i].iov_len;
161 : }
162 :
163 1 : *len = buf_len;
164 1 : return buf;
165 : }
166 :
167 : void
168 260 : spdk_scsi_task_set_data(struct spdk_scsi_task *task, void *data, uint32_t len)
169 : {
170 260 : assert(task->iovcnt == 1);
171 260 : assert(task->alloc_len == 0);
172 :
173 260 : task->iovs[0].iov_base = data;
174 260 : task->iovs[0].iov_len = len;
175 260 : }
176 :
177 : void
178 9 : spdk_scsi_task_build_sense_data(struct spdk_scsi_task *task, int sk, int asc, int ascq)
179 : {
180 : uint8_t *cp;
181 : int resp_code;
182 :
183 9 : resp_code = 0x70; /* Current + Fixed format */
184 :
185 : /* Sense Data */
186 9 : cp = task->sense_data;
187 :
188 : /* VALID(7) RESPONSE CODE(6-0) */
189 9 : cp[0] = 0x80 | resp_code;
190 : /* Obsolete */
191 9 : cp[1] = 0;
192 : /* FILEMARK(7) EOM(6) ILI(5) SENSE KEY(3-0) */
193 9 : cp[2] = sk & 0xf;
194 : /* INFORMATION */
195 9 : memset(&cp[3], 0, 4);
196 :
197 : /* ADDITIONAL SENSE LENGTH */
198 9 : cp[7] = 10;
199 :
200 : /* COMMAND-SPECIFIC INFORMATION */
201 9 : memset(&cp[8], 0, 4);
202 : /* ADDITIONAL SENSE CODE */
203 9 : cp[12] = asc;
204 : /* ADDITIONAL SENSE CODE QUALIFIER */
205 9 : cp[13] = ascq;
206 : /* FIELD REPLACEABLE UNIT CODE */
207 9 : cp[14] = 0;
208 :
209 : /* SKSV(7) SENSE KEY SPECIFIC(6-0,7-0,7-0) */
210 9 : cp[15] = 0;
211 9 : cp[16] = 0;
212 9 : cp[17] = 0;
213 :
214 : /* SenseLength */
215 9 : task->sense_data_len = 18;
216 9 : }
217 :
218 : void
219 20 : spdk_scsi_task_set_status(struct spdk_scsi_task *task, int sc, int sk,
220 : int asc, int ascq)
221 : {
222 20 : if (sc == SPDK_SCSI_STATUS_CHECK_CONDITION) {
223 9 : spdk_scsi_task_build_sense_data(task, sk, asc, ascq);
224 : }
225 20 : task->status = sc;
226 20 : }
227 :
228 : void
229 0 : spdk_scsi_task_copy_status(struct spdk_scsi_task *dst,
230 : struct spdk_scsi_task *src)
231 : {
232 0 : memcpy(dst->sense_data, src->sense_data, src->sense_data_len);
233 0 : dst->sense_data_len = src->sense_data_len;
234 0 : dst->status = src->status;
235 0 : }
236 :
237 : void
238 3 : spdk_scsi_task_process_null_lun(struct spdk_scsi_task *task)
239 : {
240 : uint8_t buffer[36];
241 : uint32_t allocation_len;
242 : uint32_t data_len;
243 :
244 3 : task->length = task->transfer_len;
245 3 : if (task->cdb[0] == SPDK_SPC_INQUIRY) {
246 : /*
247 : * SPC-4 states that INQUIRY commands to an unsupported LUN
248 : * must be served with PERIPHERAL QUALIFIER = 0x3 and
249 : * PERIPHERAL DEVICE TYPE = 0x1F.
250 : */
251 2 : data_len = sizeof(buffer);
252 :
253 2 : memset(buffer, 0, data_len);
254 : /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
255 2 : buffer[0] = 0x03 << 5 | 0x1f;
256 : /* ADDITIONAL LENGTH */
257 2 : buffer[4] = data_len - 5;
258 :
259 2 : allocation_len = from_be16(&task->cdb[3]);
260 2 : if (spdk_scsi_task_scatter_data(task, buffer, spdk_min(allocation_len, data_len)) >= 0) {
261 2 : task->data_transferred = data_len;
262 2 : task->status = SPDK_SCSI_STATUS_GOOD;
263 : }
264 : } else {
265 : /* LOGICAL UNIT NOT SUPPORTED */
266 1 : spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
267 : SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
268 : SPDK_SCSI_ASC_LOGICAL_UNIT_NOT_SUPPORTED,
269 : SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
270 1 : task->data_transferred = 0;
271 : }
272 3 : }
273 :
274 : void
275 0 : spdk_scsi_task_process_abort(struct spdk_scsi_task *task)
276 : {
277 0 : spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
278 : SPDK_SCSI_SENSE_ABORTED_COMMAND,
279 : SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
280 : SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
281 0 : }
|