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