1 /*- 2 * BSD LICENSE 3 * 4 * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>. 5 * Copyright (c) Intel Corporation. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * * Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * * Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in 16 * the documentation and/or other materials provided with the 17 * distribution. 18 * * Neither the name of Intel Corporation nor the names of its 19 * contributors may be used to endorse or promote products derived 20 * from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 25 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 26 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 28 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 #include "scsi_internal.h" 36 #include "spdk/endian.h" 37 #include "spdk/env.h" 38 #include "spdk/util.h" 39 40 static void 41 scsi_task_free_data(struct spdk_scsi_task *task) 42 { 43 if (task->alloc_len != 0) { 44 spdk_dma_free(task->iov.iov_base); 45 task->alloc_len = 0; 46 } 47 48 task->iov.iov_base = NULL; 49 task->iov.iov_len = 0; 50 } 51 52 void 53 spdk_scsi_task_put(struct spdk_scsi_task *task) 54 { 55 if (!task) { 56 return; 57 } 58 59 assert(task->ref > 0); 60 task->ref--; 61 62 if (task->ref == 0) { 63 struct spdk_bdev_io *bdev_io = task->bdev_io; 64 65 if (bdev_io) { 66 spdk_bdev_free_io(bdev_io); 67 } 68 69 scsi_task_free_data(task); 70 71 task->free_fn(task); 72 } 73 } 74 75 void 76 spdk_scsi_task_construct(struct spdk_scsi_task *task, 77 spdk_scsi_task_cpl cpl_fn, 78 spdk_scsi_task_free free_fn) 79 { 80 assert(task != NULL); 81 assert(cpl_fn != NULL); 82 assert(free_fn != NULL); 83 84 task->cpl_fn = cpl_fn; 85 task->free_fn = free_fn; 86 87 task->ref++; 88 89 /* 90 * Pre-fill the iov_buffers to point to the embedded iov 91 */ 92 assert(task->iov.iov_base == NULL); 93 task->iovs = &task->iov; 94 task->iovcnt = 1; 95 } 96 97 static void * 98 scsi_task_alloc_data(struct spdk_scsi_task *task, uint32_t alloc_len) 99 { 100 assert(task->alloc_len == 0); 101 102 task->iov.iov_base = spdk_dma_zmalloc(alloc_len, 0, NULL); 103 task->iov.iov_len = alloc_len; 104 task->alloc_len = alloc_len; 105 106 return task->iov.iov_base; 107 } 108 109 int 110 spdk_scsi_task_scatter_data(struct spdk_scsi_task *task, const void *src, size_t buf_len) 111 { 112 size_t len = 0; 113 size_t buf_left = buf_len; 114 int i; 115 struct iovec *iovs = task->iovs; 116 const uint8_t *pos; 117 118 if (buf_len == 0) { 119 return 0; 120 } 121 122 if (task->iovcnt == 1 && iovs[0].iov_base == NULL) { 123 scsi_task_alloc_data(task, buf_len); 124 iovs[0] = task->iov; 125 } 126 127 for (i = 0; i < task->iovcnt; i++) { 128 assert(iovs[i].iov_base != NULL); 129 len += iovs[i].iov_len; 130 } 131 132 if (len < buf_len) { 133 spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION, 134 SPDK_SCSI_SENSE_ILLEGAL_REQUEST, 135 SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB, 136 SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE); 137 return -1; 138 } 139 140 pos = src; 141 142 for (i = 0; i < task->iovcnt; i++) { 143 len = spdk_min(iovs[i].iov_len, buf_left); 144 buf_left -= len; 145 memcpy(iovs[i].iov_base, pos, len); 146 pos += len; 147 } 148 149 return buf_len; 150 } 151 152 void * 153 spdk_scsi_task_gather_data(struct spdk_scsi_task *task, int *len) 154 { 155 int i; 156 struct iovec *iovs = task->iovs; 157 size_t buf_len = 0; 158 uint8_t *buf, *pos; 159 160 for (i = 0; i < task->iovcnt; i++) { 161 /* It is OK for iov_base to be NULL if iov_len is 0. */ 162 assert(iovs[i].iov_base != NULL || iovs[i].iov_len == 0); 163 buf_len += iovs[i].iov_len; 164 } 165 166 if (buf_len == 0) { 167 *len = 0; 168 return NULL; 169 } 170 171 buf = calloc(1, buf_len); 172 if (buf == NULL) { 173 *len = -1; 174 return NULL; 175 } 176 177 pos = buf; 178 for (i = 0; i < task->iovcnt; i++) { 179 memcpy(pos, iovs[i].iov_base, iovs[i].iov_len); 180 pos += iovs[i].iov_len; 181 } 182 183 *len = buf_len; 184 return buf; 185 } 186 187 void 188 spdk_scsi_task_set_data(struct spdk_scsi_task *task, void *data, uint32_t len) 189 { 190 assert(task->iovcnt == 1); 191 assert(task->alloc_len == 0); 192 193 task->iovs[0].iov_base = data; 194 task->iovs[0].iov_len = len; 195 } 196 197 void 198 spdk_scsi_task_build_sense_data(struct spdk_scsi_task *task, int sk, int asc, int ascq) 199 { 200 uint8_t *cp; 201 int resp_code; 202 203 resp_code = 0x70; /* Current + Fixed format */ 204 205 /* Sense Data */ 206 cp = task->sense_data; 207 208 /* VALID(7) RESPONSE CODE(6-0) */ 209 cp[0] = 0x80 | resp_code; 210 /* Obsolete */ 211 cp[1] = 0; 212 /* FILEMARK(7) EOM(6) ILI(5) SENSE KEY(3-0) */ 213 cp[2] = sk & 0xf; 214 /* INFORMATION */ 215 memset(&cp[3], 0, 4); 216 217 /* ADDITIONAL SENSE LENGTH */ 218 cp[7] = 10; 219 220 /* COMMAND-SPECIFIC INFORMATION */ 221 memset(&cp[8], 0, 4); 222 /* ADDITIONAL SENSE CODE */ 223 cp[12] = asc; 224 /* ADDITIONAL SENSE CODE QUALIFIER */ 225 cp[13] = ascq; 226 /* FIELD REPLACEABLE UNIT CODE */ 227 cp[14] = 0; 228 229 /* SKSV(7) SENSE KEY SPECIFIC(6-0,7-0,7-0) */ 230 cp[15] = 0; 231 cp[16] = 0; 232 cp[17] = 0; 233 234 /* SenseLength */ 235 task->sense_data_len = 18; 236 } 237 238 void 239 spdk_scsi_task_set_status(struct spdk_scsi_task *task, int sc, int sk, 240 int asc, int ascq) 241 { 242 if (sc == SPDK_SCSI_STATUS_CHECK_CONDITION) { 243 spdk_scsi_task_build_sense_data(task, sk, asc, ascq); 244 } 245 task->status = sc; 246 } 247 248 void 249 spdk_scsi_task_copy_status(struct spdk_scsi_task *dst, 250 struct spdk_scsi_task *src) 251 { 252 memcpy(dst->sense_data, src->sense_data, src->sense_data_len); 253 dst->sense_data_len = src->sense_data_len; 254 dst->status = src->status; 255 } 256 257 void 258 spdk_scsi_task_process_null_lun(struct spdk_scsi_task *task) 259 { 260 uint8_t buffer[36]; 261 uint32_t allocation_len; 262 uint32_t data_len; 263 264 task->length = task->transfer_len; 265 if (task->cdb[0] == SPDK_SPC_INQUIRY) { 266 /* 267 * SPC-4 states that INQUIRY commands to an unsupported LUN 268 * must be served with PERIPHERAL QUALIFIER = 0x3 and 269 * PERIPHERAL DEVICE TYPE = 0x1F. 270 */ 271 data_len = sizeof(buffer); 272 273 memset(buffer, 0, data_len); 274 /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ 275 buffer[0] = 0x03 << 5 | 0x1f; 276 /* ADDITIONAL LENGTH */ 277 buffer[4] = data_len - 5; 278 279 allocation_len = from_be16(&task->cdb[3]); 280 if (spdk_scsi_task_scatter_data(task, buffer, spdk_min(allocation_len, data_len)) >= 0) { 281 task->data_transferred = data_len; 282 task->status = SPDK_SCSI_STATUS_GOOD; 283 } 284 } else { 285 /* LOGICAL UNIT NOT SUPPORTED */ 286 spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION, 287 SPDK_SCSI_SENSE_ILLEGAL_REQUEST, 288 SPDK_SCSI_ASC_LOGICAL_UNIT_NOT_SUPPORTED, 289 SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE); 290 task->data_transferred = 0; 291 } 292 } 293 294 void 295 spdk_scsi_task_process_abort(struct spdk_scsi_task *task) 296 { 297 spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION, 298 SPDK_SCSI_SENSE_ABORTED_COMMAND, 299 SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE, 300 SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE); 301 } 302