xref: /spdk/lib/scsi/task.c (revision a9eea50dc9e5167f529749ae693f51cc0ee3b67b)
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 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
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
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 *
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
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 *
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
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
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
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
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
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
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