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