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
scsi_task_free_data(struct spdk_scsi_task * task)13 scsi_task_free_data(struct spdk_scsi_task *task)
14 {
15 if (task->alloc_len != 0) {
16 spdk_dma_free(task->iov.iov_base);
17 task->alloc_len = 0;
18 }
19
20 task->iov.iov_base = NULL;
21 task->iov.iov_len = 0;
22 }
23
24 void
spdk_scsi_task_put(struct spdk_scsi_task * task)25 spdk_scsi_task_put(struct spdk_scsi_task *task)
26 {
27 if (!task) {
28 return;
29 }
30
31 assert(task->ref > 0);
32 task->ref--;
33
34 if (task->ref == 0) {
35 struct spdk_bdev_io *bdev_io = task->bdev_io;
36
37 if (bdev_io) {
38 spdk_bdev_free_io(bdev_io);
39 }
40
41 scsi_task_free_data(task);
42
43 task->free_fn(task);
44 }
45 }
46
47 void
spdk_scsi_task_construct(struct spdk_scsi_task * task,spdk_scsi_task_cpl cpl_fn,spdk_scsi_task_free free_fn)48 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 assert(task != NULL);
53 assert(cpl_fn != NULL);
54 assert(free_fn != NULL);
55
56 task->cpl_fn = cpl_fn;
57 task->free_fn = free_fn;
58
59 task->ref++;
60
61 /*
62 * Pre-fill the iov_buffers to point to the embedded iov
63 */
64 assert(task->iov.iov_base == NULL);
65 task->iovs = &task->iov;
66 task->iovcnt = 1;
67 }
68
69 static void *
scsi_task_alloc_data(struct spdk_scsi_task * task,uint32_t alloc_len)70 scsi_task_alloc_data(struct spdk_scsi_task *task, uint32_t alloc_len)
71 {
72 uint32_t zmalloc_len;
73
74 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 zmalloc_len = 4 * spdk_divide_round_up(alloc_len, 4);
82 task->iov.iov_base = spdk_dma_zmalloc(zmalloc_len, 0, NULL);
83 task->iov.iov_len = alloc_len;
84 task->alloc_len = alloc_len;
85
86 return task->iov.iov_base;
87 }
88
89 int
spdk_scsi_task_scatter_data(struct spdk_scsi_task * task,const void * src,size_t buf_len)90 spdk_scsi_task_scatter_data(struct spdk_scsi_task *task, const void *src, size_t buf_len)
91 {
92 size_t len = 0;
93 size_t buf_left = buf_len;
94 int i;
95 struct iovec *iovs = task->iovs;
96 const uint8_t *pos;
97
98 if (buf_len == 0) {
99 return 0;
100 }
101
102 if (task->iovcnt == 1 && iovs[0].iov_base == NULL) {
103 scsi_task_alloc_data(task, buf_len);
104 iovs[0] = task->iov;
105 }
106
107 for (i = 0; i < task->iovcnt; i++) {
108 assert(iovs[i].iov_base != NULL);
109 len += iovs[i].iov_len;
110 }
111
112 if (len < buf_len) {
113 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 return -1;
118 }
119
120 pos = src;
121
122 for (i = 0; i < task->iovcnt; i++) {
123 len = spdk_min(iovs[i].iov_len, buf_left);
124 buf_left -= len;
125 memcpy(iovs[i].iov_base, pos, len);
126 pos += len;
127 }
128
129 return buf_len;
130 }
131
132 void *
spdk_scsi_task_gather_data(struct spdk_scsi_task * task,int * len)133 spdk_scsi_task_gather_data(struct spdk_scsi_task *task, int *len)
134 {
135 int i;
136 struct iovec *iovs = task->iovs;
137 size_t buf_len = 0;
138 uint8_t *buf, *pos;
139
140 for (i = 0; i < task->iovcnt; i++) {
141 /* It is OK for iov_base to be NULL if iov_len is 0. */
142 assert(iovs[i].iov_base != NULL || iovs[i].iov_len == 0);
143 buf_len += iovs[i].iov_len;
144 }
145
146 if (buf_len == 0) {
147 *len = 0;
148 return NULL;
149 }
150
151 buf = calloc(1, buf_len);
152 if (buf == NULL) {
153 *len = -1;
154 return NULL;
155 }
156
157 pos = buf;
158 for (i = 0; i < task->iovcnt; i++) {
159 memcpy(pos, iovs[i].iov_base, iovs[i].iov_len);
160 pos += iovs[i].iov_len;
161 }
162
163 *len = buf_len;
164 return buf;
165 }
166
167 void
spdk_scsi_task_set_data(struct spdk_scsi_task * task,void * data,uint32_t len)168 spdk_scsi_task_set_data(struct spdk_scsi_task *task, void *data, uint32_t len)
169 {
170 assert(task->iovcnt == 1);
171 assert(task->alloc_len == 0);
172
173 task->iovs[0].iov_base = data;
174 task->iovs[0].iov_len = len;
175 }
176
177 void
spdk_scsi_task_build_sense_data(struct spdk_scsi_task * task,int sk,int asc,int ascq)178 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 resp_code = 0x70; /* Current + Fixed format */
184
185 /* Sense Data */
186 cp = task->sense_data;
187
188 /* VALID(7) RESPONSE CODE(6-0) */
189 cp[0] = 0x80 | resp_code;
190 /* Obsolete */
191 cp[1] = 0;
192 /* FILEMARK(7) EOM(6) ILI(5) SENSE KEY(3-0) */
193 cp[2] = sk & 0xf;
194 /* INFORMATION */
195 memset(&cp[3], 0, 4);
196
197 /* ADDITIONAL SENSE LENGTH */
198 cp[7] = 10;
199
200 /* COMMAND-SPECIFIC INFORMATION */
201 memset(&cp[8], 0, 4);
202 /* ADDITIONAL SENSE CODE */
203 cp[12] = asc;
204 /* ADDITIONAL SENSE CODE QUALIFIER */
205 cp[13] = ascq;
206 /* FIELD REPLACEABLE UNIT CODE */
207 cp[14] = 0;
208
209 /* SKSV(7) SENSE KEY SPECIFIC(6-0,7-0,7-0) */
210 cp[15] = 0;
211 cp[16] = 0;
212 cp[17] = 0;
213
214 /* SenseLength */
215 task->sense_data_len = 18;
216 }
217
218 void
spdk_scsi_task_set_status(struct spdk_scsi_task * task,int sc,int sk,int asc,int ascq)219 spdk_scsi_task_set_status(struct spdk_scsi_task *task, int sc, int sk,
220 int asc, int ascq)
221 {
222 if (sc == SPDK_SCSI_STATUS_CHECK_CONDITION) {
223 spdk_scsi_task_build_sense_data(task, sk, asc, ascq);
224 }
225 task->status = sc;
226 }
227
228 void
spdk_scsi_task_copy_status(struct spdk_scsi_task * dst,struct spdk_scsi_task * src)229 spdk_scsi_task_copy_status(struct spdk_scsi_task *dst,
230 struct spdk_scsi_task *src)
231 {
232 memcpy(dst->sense_data, src->sense_data, src->sense_data_len);
233 dst->sense_data_len = src->sense_data_len;
234 dst->status = src->status;
235 }
236
237 void
spdk_scsi_task_process_null_lun(struct spdk_scsi_task * task)238 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 task->length = task->transfer_len;
245 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 data_len = sizeof(buffer);
252
253 memset(buffer, 0, data_len);
254 /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
255 buffer[0] = 0x03 << 5 | 0x1f;
256 /* ADDITIONAL LENGTH */
257 buffer[4] = data_len - 5;
258
259 allocation_len = from_be16(&task->cdb[3]);
260 if (spdk_scsi_task_scatter_data(task, buffer, spdk_min(allocation_len, data_len)) >= 0) {
261 task->data_transferred = data_len;
262 task->status = SPDK_SCSI_STATUS_GOOD;
263 }
264 } else {
265 /* LOGICAL UNIT NOT SUPPORTED */
266 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 task->data_transferred = 0;
271 }
272 }
273
274 void
spdk_scsi_task_process_abort(struct spdk_scsi_task * task)275 spdk_scsi_task_process_abort(struct spdk_scsi_task *task)
276 {
277 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 }
282