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