xref: /spdk/lib/scsi/task.c (revision cfe8449e7b6e0d2d2a96f6090a0c43d69e032663)
1488570ebSJim Harris /*   SPDX-License-Identifier: BSD-3-Clause
2bd4ac74eSDaniel Verkamp  *   Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
3a6dbe372Spaul luse  *   Copyright (C) 2016 Intel Corporation.
4bd4ac74eSDaniel Verkamp  *   All rights reserved.
5bd4ac74eSDaniel Verkamp  */
6bd4ac74eSDaniel Verkamp 
7bd4ac74eSDaniel Verkamp #include "scsi_internal.h"
801d13145SBen Walker #include "spdk/endian.h"
92224ff21SBen Walker #include "spdk/env.h"
10d0290b65SDaniel Verkamp #include "spdk/util.h"
11bd4ac74eSDaniel Verkamp 
12f2554ee9SShuhei Matsumoto static void
scsi_task_free_data(struct spdk_scsi_task * task)13fa2dd272SShuhei Matsumoto scsi_task_free_data(struct spdk_scsi_task *task)
14fa2dd272SShuhei Matsumoto {
15fa2dd272SShuhei Matsumoto 	if (task->alloc_len != 0) {
16fa2dd272SShuhei Matsumoto 		spdk_dma_free(task->iov.iov_base);
17fa2dd272SShuhei Matsumoto 		task->alloc_len = 0;
18fa2dd272SShuhei Matsumoto 	}
19fa2dd272SShuhei Matsumoto 
20fa2dd272SShuhei Matsumoto 	task->iov.iov_base = NULL;
21fa2dd272SShuhei Matsumoto 	task->iov.iov_len = 0;
22fa2dd272SShuhei Matsumoto }
23f2554ee9SShuhei Matsumoto 
24bd4ac74eSDaniel Verkamp void
spdk_scsi_task_put(struct spdk_scsi_task * task)256c4a07ecSZiye Yang spdk_scsi_task_put(struct spdk_scsi_task *task)
26bd4ac74eSDaniel Verkamp {
27bd4ac74eSDaniel Verkamp 	if (!task) {
28bd4ac74eSDaniel Verkamp 		return;
29bd4ac74eSDaniel Verkamp 	}
30bd4ac74eSDaniel Verkamp 
31b386a3a9SShuhei Matsumoto 	assert(task->ref > 0);
32bd4ac74eSDaniel Verkamp 	task->ref--;
33bd4ac74eSDaniel Verkamp 
34bd4ac74eSDaniel Verkamp 	if (task->ref == 0) {
35dd06e98dSJim Harris 		struct spdk_bdev_io *bdev_io = task->bdev_io;
36bd4ac74eSDaniel Verkamp 
37bd4ac74eSDaniel Verkamp 		if (bdev_io) {
38bd4ac74eSDaniel Verkamp 			spdk_bdev_free_io(bdev_io);
39bd4ac74eSDaniel Verkamp 		}
40bd4ac74eSDaniel Verkamp 
410ed66e7eSShuhei Matsumoto 		scsi_task_free_data(task);
42bd4ac74eSDaniel Verkamp 
43bd4ac74eSDaniel Verkamp 		task->free_fn(task);
44bd4ac74eSDaniel Verkamp 	}
45bd4ac74eSDaniel Verkamp }
46bd4ac74eSDaniel Verkamp 
47bd4ac74eSDaniel Verkamp void
spdk_scsi_task_construct(struct spdk_scsi_task * task,spdk_scsi_task_cpl cpl_fn,spdk_scsi_task_free free_fn)4819a98714SDaniel Verkamp spdk_scsi_task_construct(struct spdk_scsi_task *task,
4941852055SBen Walker 			 spdk_scsi_task_cpl cpl_fn,
50e45437abSDaniel Verkamp 			 spdk_scsi_task_free free_fn)
51bd4ac74eSDaniel Verkamp {
5241852055SBen Walker 	assert(task != NULL);
5341852055SBen Walker 	assert(cpl_fn != NULL);
547145cf62SDaniel Verkamp 	assert(free_fn != NULL);
5541852055SBen Walker 
5641852055SBen Walker 	task->cpl_fn = cpl_fn;
577145cf62SDaniel Verkamp 	task->free_fn = free_fn;
587145cf62SDaniel Verkamp 
59bd4ac74eSDaniel Verkamp 	task->ref++;
60bd4ac74eSDaniel Verkamp 
6146b7d49aSKrzysztof Jakimiak 	/*
6246b7d49aSKrzysztof Jakimiak 	 * Pre-fill the iov_buffers to point to the embedded iov
6346b7d49aSKrzysztof Jakimiak 	 */
64f30f0c76SPawel Wodkowski 	assert(task->iov.iov_base == NULL);
6546b7d49aSKrzysztof Jakimiak 	task->iovs = &task->iov;
6646b7d49aSKrzysztof Jakimiak 	task->iovcnt = 1;
67bd4ac74eSDaniel Verkamp }
68bd4ac74eSDaniel Verkamp 
69f2554ee9SShuhei Matsumoto static void *
scsi_task_alloc_data(struct spdk_scsi_task * task,uint32_t alloc_len)700ed66e7eSShuhei Matsumoto scsi_task_alloc_data(struct spdk_scsi_task *task, uint32_t alloc_len)
71bd4ac74eSDaniel Verkamp {
72*cfe8449eSJim Harris 	uint32_t zmalloc_len;
73*cfe8449eSJim Harris 
74f30f0c76SPawel Wodkowski 	assert(task->alloc_len == 0);
75f30f0c76SPawel Wodkowski 
76*cfe8449eSJim Harris 	/* Some ULPs (such as iSCSI) need to round len up to nearest
77*cfe8449eSJim Harris 	 * 4 bytes. We can help those ULPs by allocating memory here
78*cfe8449eSJim Harris 	 * up to next 4 byte boundary, so they don't have to worry
79*cfe8449eSJim Harris 	 * about handling out-of-bounds errors.
80*cfe8449eSJim Harris 	 */
81*cfe8449eSJim Harris 	zmalloc_len = 4 * spdk_divide_round_up(alloc_len, 4);
82*cfe8449eSJim Harris 	task->iov.iov_base = spdk_dma_zmalloc(zmalloc_len, 0, NULL);
83a1948352SPawel Wodkowski 	task->iov.iov_len = alloc_len;
84a1948352SPawel Wodkowski 	task->alloc_len = alloc_len;
85a1948352SPawel Wodkowski 
86a1948352SPawel Wodkowski 	return task->iov.iov_base;
87a1948352SPawel Wodkowski }
88a1948352SPawel Wodkowski 
89a1948352SPawel Wodkowski int
spdk_scsi_task_scatter_data(struct spdk_scsi_task * task,const void * src,size_t buf_len)90a1948352SPawel Wodkowski spdk_scsi_task_scatter_data(struct spdk_scsi_task *task, const void *src, size_t buf_len)
91a1948352SPawel Wodkowski {
92a1948352SPawel Wodkowski 	size_t len = 0;
93a1948352SPawel Wodkowski 	size_t buf_left = buf_len;
94a1948352SPawel Wodkowski 	int i;
95a1948352SPawel Wodkowski 	struct iovec *iovs = task->iovs;
96a1948352SPawel Wodkowski 	const uint8_t *pos;
97a1948352SPawel Wodkowski 
9859970a89SDaniel Verkamp 	if (buf_len == 0) {
99a1948352SPawel Wodkowski 		return 0;
10059970a89SDaniel Verkamp 	}
101a1948352SPawel Wodkowski 
102a1948352SPawel Wodkowski 	if (task->iovcnt == 1 && iovs[0].iov_base == NULL) {
1030ed66e7eSShuhei Matsumoto 		scsi_task_alloc_data(task, buf_len);
104a1948352SPawel Wodkowski 		iovs[0] = task->iov;
105a1948352SPawel Wodkowski 	}
106a1948352SPawel Wodkowski 
107a1948352SPawel Wodkowski 	for (i = 0; i < task->iovcnt; i++) {
108a1948352SPawel Wodkowski 		assert(iovs[i].iov_base != NULL);
109a1948352SPawel Wodkowski 		len += iovs[i].iov_len;
110a1948352SPawel Wodkowski 	}
111a1948352SPawel Wodkowski 
112a1948352SPawel Wodkowski 	if (len < buf_len) {
113a1948352SPawel Wodkowski 		spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
114a1948352SPawel Wodkowski 					  SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
115a1948352SPawel Wodkowski 					  SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
116a1948352SPawel Wodkowski 					  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
117a1948352SPawel Wodkowski 		return -1;
118a1948352SPawel Wodkowski 	}
119a1948352SPawel Wodkowski 
120a1948352SPawel Wodkowski 	pos = src;
121a1948352SPawel Wodkowski 
122a1948352SPawel Wodkowski 	for (i = 0; i < task->iovcnt; i++) {
123d0290b65SDaniel Verkamp 		len = spdk_min(iovs[i].iov_len, buf_left);
124a1948352SPawel Wodkowski 		buf_left -= len;
125a1948352SPawel Wodkowski 		memcpy(iovs[i].iov_base, pos, len);
126a1948352SPawel Wodkowski 		pos += len;
127a1948352SPawel Wodkowski 	}
128a1948352SPawel Wodkowski 
129a1948352SPawel Wodkowski 	return buf_len;
130a1948352SPawel Wodkowski }
131a1948352SPawel Wodkowski 
132a1948352SPawel Wodkowski void *
spdk_scsi_task_gather_data(struct spdk_scsi_task * task,int * len)133a1948352SPawel Wodkowski spdk_scsi_task_gather_data(struct spdk_scsi_task *task, int *len)
134a1948352SPawel Wodkowski {
135a1948352SPawel Wodkowski 	int i;
136a1948352SPawel Wodkowski 	struct iovec *iovs = task->iovs;
137a1948352SPawel Wodkowski 	size_t buf_len = 0;
138a1948352SPawel Wodkowski 	uint8_t *buf, *pos;
139a1948352SPawel Wodkowski 
140a1948352SPawel Wodkowski 	for (i = 0; i < task->iovcnt; i++) {
14122e5037eSJim Harris 		/* It is OK for iov_base to be NULL if iov_len is 0. */
14222e5037eSJim Harris 		assert(iovs[i].iov_base != NULL || iovs[i].iov_len == 0);
143a1948352SPawel Wodkowski 		buf_len += iovs[i].iov_len;
144a1948352SPawel Wodkowski 	}
145a1948352SPawel Wodkowski 
146a1948352SPawel Wodkowski 	if (buf_len == 0) {
147a1948352SPawel Wodkowski 		*len = 0;
148f30f0c76SPawel Wodkowski 		return NULL;
149f30f0c76SPawel Wodkowski 	}
150f30f0c76SPawel Wodkowski 
15107689e94SDarek Stojaczyk 	buf = calloc(1, buf_len);
152a1948352SPawel Wodkowski 	if (buf == NULL) {
153a1948352SPawel Wodkowski 		*len = -1;
154a1948352SPawel Wodkowski 		return NULL;
155f30f0c76SPawel Wodkowski 	}
156f30f0c76SPawel Wodkowski 
157a1948352SPawel Wodkowski 	pos = buf;
158a1948352SPawel Wodkowski 	for (i = 0; i < task->iovcnt; i++) {
159a1948352SPawel Wodkowski 		memcpy(pos, iovs[i].iov_base, iovs[i].iov_len);
160a1948352SPawel Wodkowski 		pos += iovs[i].iov_len;
161a1948352SPawel Wodkowski 	}
162f30f0c76SPawel Wodkowski 
163a1948352SPawel Wodkowski 	*len = buf_len;
164a1948352SPawel Wodkowski 	return buf;
165f30f0c76SPawel Wodkowski }
166f30f0c76SPawel Wodkowski 
167f30f0c76SPawel Wodkowski void
spdk_scsi_task_set_data(struct spdk_scsi_task * task,void * data,uint32_t len)168f30f0c76SPawel Wodkowski spdk_scsi_task_set_data(struct spdk_scsi_task *task, void *data, uint32_t len)
169f30f0c76SPawel Wodkowski {
170f30f0c76SPawel Wodkowski 	assert(task->iovcnt == 1);
171f30f0c76SPawel Wodkowski 	assert(task->alloc_len == 0);
172f30f0c76SPawel Wodkowski 
173f30f0c76SPawel Wodkowski 	task->iovs[0].iov_base = data;
174f30f0c76SPawel Wodkowski 	task->iovs[0].iov_len = len;
175bd4ac74eSDaniel Verkamp }
176bd4ac74eSDaniel Verkamp 
17751b96642SZiye Yang void
spdk_scsi_task_build_sense_data(struct spdk_scsi_task * task,int sk,int asc,int ascq)178bd4ac74eSDaniel Verkamp spdk_scsi_task_build_sense_data(struct spdk_scsi_task *task, int sk, int asc, int ascq)
179bd4ac74eSDaniel Verkamp {
180bd4ac74eSDaniel Verkamp 	uint8_t *cp;
181bd4ac74eSDaniel Verkamp 	int resp_code;
182bd4ac74eSDaniel Verkamp 
183bd4ac74eSDaniel Verkamp 	resp_code = 0x70; /* Current + Fixed format */
184bd4ac74eSDaniel Verkamp 
185bd4ac74eSDaniel Verkamp 	/* Sense Data */
186cc1146a8SDaniel Verkamp 	cp = task->sense_data;
187bd4ac74eSDaniel Verkamp 
188bd4ac74eSDaniel Verkamp 	/* VALID(7) RESPONSE CODE(6-0) */
189bd4ac74eSDaniel Verkamp 	cp[0] = 0x80 | resp_code;
190bd4ac74eSDaniel Verkamp 	/* Obsolete */
191bd4ac74eSDaniel Verkamp 	cp[1] = 0;
192bd4ac74eSDaniel Verkamp 	/* FILEMARK(7) EOM(6) ILI(5) SENSE KEY(3-0) */
193bd4ac74eSDaniel Verkamp 	cp[2] = sk & 0xf;
194bd4ac74eSDaniel Verkamp 	/* INFORMATION */
195bd4ac74eSDaniel Verkamp 	memset(&cp[3], 0, 4);
19651b96642SZiye Yang 
197bd4ac74eSDaniel Verkamp 	/* ADDITIONAL SENSE LENGTH */
19851b96642SZiye Yang 	cp[7] = 10;
199bd4ac74eSDaniel Verkamp 
200bd4ac74eSDaniel Verkamp 	/* COMMAND-SPECIFIC INFORMATION */
201bd4ac74eSDaniel Verkamp 	memset(&cp[8], 0, 4);
202bd4ac74eSDaniel Verkamp 	/* ADDITIONAL SENSE CODE */
203bd4ac74eSDaniel Verkamp 	cp[12] = asc;
204bd4ac74eSDaniel Verkamp 	/* ADDITIONAL SENSE CODE QUALIFIER */
205bd4ac74eSDaniel Verkamp 	cp[13] = ascq;
206bd4ac74eSDaniel Verkamp 	/* FIELD REPLACEABLE UNIT CODE */
207bd4ac74eSDaniel Verkamp 	cp[14] = 0;
208bd4ac74eSDaniel Verkamp 
209bd4ac74eSDaniel Verkamp 	/* SKSV(7) SENSE KEY SPECIFIC(6-0,7-0,7-0) */
210bd4ac74eSDaniel Verkamp 	cp[15] = 0;
211bd4ac74eSDaniel Verkamp 	cp[16] = 0;
212bd4ac74eSDaniel Verkamp 	cp[17] = 0;
213bd4ac74eSDaniel Verkamp 
214bd4ac74eSDaniel Verkamp 	/* SenseLength */
215cc1146a8SDaniel Verkamp 	task->sense_data_len = 18;
216bd4ac74eSDaniel Verkamp }
217bd4ac74eSDaniel Verkamp 
218bd4ac74eSDaniel Verkamp void
spdk_scsi_task_set_status(struct spdk_scsi_task * task,int sc,int sk,int asc,int ascq)219a5f03278STsuyoshi Uchida spdk_scsi_task_set_status(struct spdk_scsi_task *task, int sc, int sk,
220a5f03278STsuyoshi Uchida 			  int asc, int ascq)
221bd4ac74eSDaniel Verkamp {
2226aabf494SDaniel Verkamp 	if (sc == SPDK_SCSI_STATUS_CHECK_CONDITION) {
223bd4ac74eSDaniel Verkamp 		spdk_scsi_task_build_sense_data(task, sk, asc, ascq);
2246aabf494SDaniel Verkamp 	}
225a5f03278STsuyoshi Uchida 	task->status = sc;
226bd4ac74eSDaniel Verkamp }
22703a52d0dSShuhei Matsumoto 
22803a52d0dSShuhei Matsumoto void
spdk_scsi_task_copy_status(struct spdk_scsi_task * dst,struct spdk_scsi_task * src)22903a52d0dSShuhei Matsumoto spdk_scsi_task_copy_status(struct spdk_scsi_task *dst,
23003a52d0dSShuhei Matsumoto 			   struct spdk_scsi_task *src)
23103a52d0dSShuhei Matsumoto {
23203a52d0dSShuhei Matsumoto 	memcpy(dst->sense_data, src->sense_data, src->sense_data_len);
23303a52d0dSShuhei Matsumoto 	dst->sense_data_len = src->sense_data_len;
23403a52d0dSShuhei Matsumoto 	dst->status = src->status;
23503a52d0dSShuhei Matsumoto }
2365a365f36SShuhei Matsumoto 
2375a365f36SShuhei Matsumoto void
spdk_scsi_task_process_null_lun(struct spdk_scsi_task * task)2385a365f36SShuhei Matsumoto spdk_scsi_task_process_null_lun(struct spdk_scsi_task *task)
2395a365f36SShuhei Matsumoto {
2405a365f36SShuhei Matsumoto 	uint8_t buffer[36];
2415a365f36SShuhei Matsumoto 	uint32_t allocation_len;
2425a365f36SShuhei Matsumoto 	uint32_t data_len;
2435a365f36SShuhei Matsumoto 
2445a365f36SShuhei Matsumoto 	task->length = task->transfer_len;
2455a365f36SShuhei Matsumoto 	if (task->cdb[0] == SPDK_SPC_INQUIRY) {
2465a365f36SShuhei Matsumoto 		/*
2475a365f36SShuhei Matsumoto 		 * SPC-4 states that INQUIRY commands to an unsupported LUN
2485a365f36SShuhei Matsumoto 		 *  must be served with PERIPHERAL QUALIFIER = 0x3 and
2495a365f36SShuhei Matsumoto 		 *  PERIPHERAL DEVICE TYPE = 0x1F.
2505a365f36SShuhei Matsumoto 		 */
2515a365f36SShuhei Matsumoto 		data_len = sizeof(buffer);
2525a365f36SShuhei Matsumoto 
2535a365f36SShuhei Matsumoto 		memset(buffer, 0, data_len);
2545a365f36SShuhei Matsumoto 		/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
2555a365f36SShuhei Matsumoto 		buffer[0] = 0x03 << 5 | 0x1f;
2565a365f36SShuhei Matsumoto 		/* ADDITIONAL LENGTH */
2575a365f36SShuhei Matsumoto 		buffer[4] = data_len - 5;
2585a365f36SShuhei Matsumoto 
2595a365f36SShuhei Matsumoto 		allocation_len = from_be16(&task->cdb[3]);
2605a365f36SShuhei Matsumoto 		if (spdk_scsi_task_scatter_data(task, buffer, spdk_min(allocation_len, data_len)) >= 0) {
2615a365f36SShuhei Matsumoto 			task->data_transferred = data_len;
2625a365f36SShuhei Matsumoto 			task->status = SPDK_SCSI_STATUS_GOOD;
2635a365f36SShuhei Matsumoto 		}
2645a365f36SShuhei Matsumoto 	} else {
2655a365f36SShuhei Matsumoto 		/* LOGICAL UNIT NOT SUPPORTED */
2665a365f36SShuhei Matsumoto 		spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
2675a365f36SShuhei Matsumoto 					  SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
2685a365f36SShuhei Matsumoto 					  SPDK_SCSI_ASC_LOGICAL_UNIT_NOT_SUPPORTED,
2695a365f36SShuhei Matsumoto 					  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
2705a365f36SShuhei Matsumoto 		task->data_transferred = 0;
2715a365f36SShuhei Matsumoto 	}
2725a365f36SShuhei Matsumoto }
273ba181155SShuhei Matsumoto 
274ba181155SShuhei Matsumoto void
spdk_scsi_task_process_abort(struct spdk_scsi_task * task)275ba181155SShuhei Matsumoto spdk_scsi_task_process_abort(struct spdk_scsi_task *task)
276ba181155SShuhei Matsumoto {
277ba181155SShuhei Matsumoto 	spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
278ba181155SShuhei Matsumoto 				  SPDK_SCSI_SENSE_ABORTED_COMMAND,
279ba181155SShuhei Matsumoto 				  SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
280ba181155SShuhei Matsumoto 				  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
281ba181155SShuhei Matsumoto }
282