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 assert(iovs[i].iov_base != NULL); 162 buf_len += iovs[i].iov_len; 163 } 164 165 if (buf_len == 0) { 166 *len = 0; 167 return NULL; 168 } 169 170 buf = calloc(1, buf_len); 171 if (buf == NULL) { 172 *len = -1; 173 return NULL; 174 } 175 176 pos = buf; 177 for (i = 0; i < task->iovcnt; i++) { 178 memcpy(pos, iovs[i].iov_base, iovs[i].iov_len); 179 pos += iovs[i].iov_len; 180 } 181 182 *len = buf_len; 183 return buf; 184 } 185 186 void 187 spdk_scsi_task_set_data(struct spdk_scsi_task *task, void *data, uint32_t len) 188 { 189 assert(task->iovcnt == 1); 190 assert(task->alloc_len == 0); 191 192 task->iovs[0].iov_base = data; 193 task->iovs[0].iov_len = len; 194 } 195 196 void 197 spdk_scsi_task_build_sense_data(struct spdk_scsi_task *task, int sk, int asc, int ascq) 198 { 199 uint8_t *cp; 200 int resp_code; 201 202 resp_code = 0x70; /* Current + Fixed format */ 203 204 /* Sense Data */ 205 cp = task->sense_data; 206 207 /* VALID(7) RESPONSE CODE(6-0) */ 208 cp[0] = 0x80 | resp_code; 209 /* Obsolete */ 210 cp[1] = 0; 211 /* FILEMARK(7) EOM(6) ILI(5) SENSE KEY(3-0) */ 212 cp[2] = sk & 0xf; 213 /* INFORMATION */ 214 memset(&cp[3], 0, 4); 215 216 /* ADDITIONAL SENSE LENGTH */ 217 cp[7] = 10; 218 219 /* COMMAND-SPECIFIC INFORMATION */ 220 memset(&cp[8], 0, 4); 221 /* ADDITIONAL SENSE CODE */ 222 cp[12] = asc; 223 /* ADDITIONAL SENSE CODE QUALIFIER */ 224 cp[13] = ascq; 225 /* FIELD REPLACEABLE UNIT CODE */ 226 cp[14] = 0; 227 228 /* SKSV(7) SENSE KEY SPECIFIC(6-0,7-0,7-0) */ 229 cp[15] = 0; 230 cp[16] = 0; 231 cp[17] = 0; 232 233 /* SenseLength */ 234 task->sense_data_len = 18; 235 } 236 237 void 238 spdk_scsi_task_set_status(struct spdk_scsi_task *task, int sc, int sk, 239 int asc, int ascq) 240 { 241 if (sc == SPDK_SCSI_STATUS_CHECK_CONDITION) { 242 spdk_scsi_task_build_sense_data(task, sk, asc, ascq); 243 } 244 task->status = sc; 245 } 246 247 void 248 spdk_scsi_task_copy_status(struct spdk_scsi_task *dst, 249 struct spdk_scsi_task *src) 250 { 251 memcpy(dst->sense_data, src->sense_data, src->sense_data_len); 252 dst->sense_data_len = src->sense_data_len; 253 dst->status = src->status; 254 } 255 256 void 257 spdk_scsi_task_process_null_lun(struct spdk_scsi_task *task) 258 { 259 uint8_t buffer[36]; 260 uint32_t allocation_len; 261 uint32_t data_len; 262 263 task->length = task->transfer_len; 264 if (task->cdb[0] == SPDK_SPC_INQUIRY) { 265 /* 266 * SPC-4 states that INQUIRY commands to an unsupported LUN 267 * must be served with PERIPHERAL QUALIFIER = 0x3 and 268 * PERIPHERAL DEVICE TYPE = 0x1F. 269 */ 270 data_len = sizeof(buffer); 271 272 memset(buffer, 0, data_len); 273 /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ 274 buffer[0] = 0x03 << 5 | 0x1f; 275 /* ADDITIONAL LENGTH */ 276 buffer[4] = data_len - 5; 277 278 allocation_len = from_be16(&task->cdb[3]); 279 if (spdk_scsi_task_scatter_data(task, buffer, spdk_min(allocation_len, data_len)) >= 0) { 280 task->data_transferred = data_len; 281 task->status = SPDK_SCSI_STATUS_GOOD; 282 } 283 } else { 284 /* LOGICAL UNIT NOT SUPPORTED */ 285 spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION, 286 SPDK_SCSI_SENSE_ILLEGAL_REQUEST, 287 SPDK_SCSI_ASC_LOGICAL_UNIT_NOT_SUPPORTED, 288 SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE); 289 task->data_transferred = 0; 290 } 291 } 292 293 void 294 spdk_scsi_task_process_abort(struct spdk_scsi_task *task) 295 { 296 spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION, 297 SPDK_SCSI_SENSE_ABORTED_COMMAND, 298 SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE, 299 SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE); 300 } 301