xref: /spdk/lib/scsi/task.c (revision 6f338d4bf3a8a91b7abe377a605a321ea2b05bf7)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
3  *   Copyright (c) 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 	assert(task->alloc_len == 0);
73 
74 	task->iov.iov_base = spdk_dma_zmalloc(alloc_len, 0, NULL);
75 	task->iov.iov_len = alloc_len;
76 	task->alloc_len = alloc_len;
77 
78 	return task->iov.iov_base;
79 }
80 
81 int
82 spdk_scsi_task_scatter_data(struct spdk_scsi_task *task, const void *src, size_t buf_len)
83 {
84 	size_t len = 0;
85 	size_t buf_left = buf_len;
86 	int i;
87 	struct iovec *iovs = task->iovs;
88 	const uint8_t *pos;
89 
90 	if (buf_len == 0) {
91 		return 0;
92 	}
93 
94 	if (task->iovcnt == 1 && iovs[0].iov_base == NULL) {
95 		scsi_task_alloc_data(task, buf_len);
96 		iovs[0] = task->iov;
97 	}
98 
99 	for (i = 0; i < task->iovcnt; i++) {
100 		assert(iovs[i].iov_base != NULL);
101 		len += iovs[i].iov_len;
102 	}
103 
104 	if (len < buf_len) {
105 		spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
106 					  SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
107 					  SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
108 					  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
109 		return -1;
110 	}
111 
112 	pos = src;
113 
114 	for (i = 0; i < task->iovcnt; i++) {
115 		len = spdk_min(iovs[i].iov_len, buf_left);
116 		buf_left -= len;
117 		memcpy(iovs[i].iov_base, pos, len);
118 		pos += len;
119 	}
120 
121 	return buf_len;
122 }
123 
124 void *
125 spdk_scsi_task_gather_data(struct spdk_scsi_task *task, int *len)
126 {
127 	int i;
128 	struct iovec *iovs = task->iovs;
129 	size_t buf_len = 0;
130 	uint8_t *buf, *pos;
131 
132 	for (i = 0; i < task->iovcnt; i++) {
133 		/* It is OK for iov_base to be NULL if iov_len is 0. */
134 		assert(iovs[i].iov_base != NULL || iovs[i].iov_len == 0);
135 		buf_len += iovs[i].iov_len;
136 	}
137 
138 	if (buf_len == 0) {
139 		*len = 0;
140 		return NULL;
141 	}
142 
143 	buf = calloc(1, buf_len);
144 	if (buf == NULL) {
145 		*len = -1;
146 		return NULL;
147 	}
148 
149 	pos = buf;
150 	for (i = 0; i < task->iovcnt; i++) {
151 		memcpy(pos, iovs[i].iov_base, iovs[i].iov_len);
152 		pos += iovs[i].iov_len;
153 	}
154 
155 	*len = buf_len;
156 	return buf;
157 }
158 
159 void
160 spdk_scsi_task_set_data(struct spdk_scsi_task *task, void *data, uint32_t len)
161 {
162 	assert(task->iovcnt == 1);
163 	assert(task->alloc_len == 0);
164 
165 	task->iovs[0].iov_base = data;
166 	task->iovs[0].iov_len = len;
167 }
168 
169 void
170 spdk_scsi_task_build_sense_data(struct spdk_scsi_task *task, int sk, int asc, int ascq)
171 {
172 	uint8_t *cp;
173 	int resp_code;
174 
175 	resp_code = 0x70; /* Current + Fixed format */
176 
177 	/* Sense Data */
178 	cp = task->sense_data;
179 
180 	/* VALID(7) RESPONSE CODE(6-0) */
181 	cp[0] = 0x80 | resp_code;
182 	/* Obsolete */
183 	cp[1] = 0;
184 	/* FILEMARK(7) EOM(6) ILI(5) SENSE KEY(3-0) */
185 	cp[2] = sk & 0xf;
186 	/* INFORMATION */
187 	memset(&cp[3], 0, 4);
188 
189 	/* ADDITIONAL SENSE LENGTH */
190 	cp[7] = 10;
191 
192 	/* COMMAND-SPECIFIC INFORMATION */
193 	memset(&cp[8], 0, 4);
194 	/* ADDITIONAL SENSE CODE */
195 	cp[12] = asc;
196 	/* ADDITIONAL SENSE CODE QUALIFIER */
197 	cp[13] = ascq;
198 	/* FIELD REPLACEABLE UNIT CODE */
199 	cp[14] = 0;
200 
201 	/* SKSV(7) SENSE KEY SPECIFIC(6-0,7-0,7-0) */
202 	cp[15] = 0;
203 	cp[16] = 0;
204 	cp[17] = 0;
205 
206 	/* SenseLength */
207 	task->sense_data_len = 18;
208 }
209 
210 void
211 spdk_scsi_task_set_status(struct spdk_scsi_task *task, int sc, int sk,
212 			  int asc, int ascq)
213 {
214 	if (sc == SPDK_SCSI_STATUS_CHECK_CONDITION) {
215 		spdk_scsi_task_build_sense_data(task, sk, asc, ascq);
216 	}
217 	task->status = sc;
218 }
219 
220 void
221 spdk_scsi_task_copy_status(struct spdk_scsi_task *dst,
222 			   struct spdk_scsi_task *src)
223 {
224 	memcpy(dst->sense_data, src->sense_data, src->sense_data_len);
225 	dst->sense_data_len = src->sense_data_len;
226 	dst->status = src->status;
227 }
228 
229 void
230 spdk_scsi_task_process_null_lun(struct spdk_scsi_task *task)
231 {
232 	uint8_t buffer[36];
233 	uint32_t allocation_len;
234 	uint32_t data_len;
235 
236 	task->length = task->transfer_len;
237 	if (task->cdb[0] == SPDK_SPC_INQUIRY) {
238 		/*
239 		 * SPC-4 states that INQUIRY commands to an unsupported LUN
240 		 *  must be served with PERIPHERAL QUALIFIER = 0x3 and
241 		 *  PERIPHERAL DEVICE TYPE = 0x1F.
242 		 */
243 		data_len = sizeof(buffer);
244 
245 		memset(buffer, 0, data_len);
246 		/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
247 		buffer[0] = 0x03 << 5 | 0x1f;
248 		/* ADDITIONAL LENGTH */
249 		buffer[4] = data_len - 5;
250 
251 		allocation_len = from_be16(&task->cdb[3]);
252 		if (spdk_scsi_task_scatter_data(task, buffer, spdk_min(allocation_len, data_len)) >= 0) {
253 			task->data_transferred = data_len;
254 			task->status = SPDK_SCSI_STATUS_GOOD;
255 		}
256 	} else {
257 		/* LOGICAL UNIT NOT SUPPORTED */
258 		spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
259 					  SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
260 					  SPDK_SCSI_ASC_LOGICAL_UNIT_NOT_SUPPORTED,
261 					  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
262 		task->data_transferred = 0;
263 	}
264 }
265 
266 void
267 spdk_scsi_task_process_abort(struct spdk_scsi_task *task)
268 {
269 	spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
270 				  SPDK_SCSI_SENSE_ABORTED_COMMAND,
271 				  SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
272 				  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
273 }
274