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/thread.h" 39 #include "spdk/event.h" 40 #include "spdk/util.h" 41 42 void 43 spdk_scsi_lun_complete_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task) 44 { 45 if (lun) { 46 TAILQ_REMOVE(&lun->tasks, task, scsi_link); 47 spdk_trace_record(TRACE_SCSI_TASK_DONE, lun->dev->id, 0, (uintptr_t)task, 0); 48 } 49 task->cpl_fn(task); 50 } 51 52 void 53 spdk_scsi_lun_complete_mgmt_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task) 54 { 55 if (task->function == SPDK_SCSI_TASK_FUNC_LUN_RESET && 56 task->status == SPDK_SCSI_STATUS_GOOD) { 57 /* 58 * The backend LUN device was just reset. If there are active tasks 59 * in the backend, it means that LUN reset fails, and we set failure 60 * status to LUN reset task. 61 */ 62 if (spdk_scsi_lun_has_pending_tasks(lun)) { 63 SPDK_ERRLOG("lun->tasks should be empty after reset\n"); 64 task->response = SPDK_SCSI_TASK_MGMT_RESP_TARGET_FAILURE; 65 } 66 } 67 task->cpl_fn(task); 68 } 69 70 int 71 spdk_scsi_lun_task_mgmt_execute(struct spdk_scsi_task *task, 72 enum spdk_scsi_task_func func) 73 { 74 if (!task) { 75 return -1; 76 } 77 78 if (!task->lun) { 79 /* LUN does not exist */ 80 task->response = SPDK_SCSI_TASK_MGMT_RESP_INVALID_LUN; 81 task->cpl_fn(task); 82 return -1; 83 } 84 85 switch (func) { 86 case SPDK_SCSI_TASK_FUNC_ABORT_TASK: 87 task->response = SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED; 88 SPDK_ERRLOG("ABORT_TASK failed\n"); 89 break; 90 91 case SPDK_SCSI_TASK_FUNC_ABORT_TASK_SET: 92 task->response = SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED; 93 SPDK_ERRLOG("ABORT_TASK_SET failed\n"); 94 break; 95 96 case SPDK_SCSI_TASK_FUNC_LUN_RESET: 97 spdk_bdev_scsi_reset(task); 98 return 0; 99 100 default: 101 SPDK_ERRLOG("Unknown Task Management Function!\n"); 102 /* 103 * Task management functions other than those above should never 104 * reach this point having been filtered by the frontend. Reject 105 * the task as being unsupported. 106 */ 107 task->response = SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED; 108 break; 109 } 110 111 spdk_scsi_lun_complete_mgmt_task(task->lun, task); 112 113 return -1; 114 } 115 116 void 117 spdk_scsi_task_process_null_lun(struct spdk_scsi_task *task) 118 { 119 uint8_t buffer[36]; 120 uint32_t allocation_len; 121 uint32_t data_len; 122 123 task->length = task->transfer_len; 124 if (task->cdb[0] == SPDK_SPC_INQUIRY) { 125 /* 126 * SPC-4 states that INQUIRY commands to an unsupported LUN 127 * must be served with PERIPHERAL QUALIFIER = 0x3 and 128 * PERIPHERAL DEVICE TYPE = 0x1F. 129 */ 130 data_len = sizeof(buffer); 131 132 memset(buffer, 0, data_len); 133 /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ 134 buffer[0] = 0x03 << 5 | 0x1f; 135 /* ADDITIONAL LENGTH */ 136 buffer[4] = data_len - 5; 137 138 allocation_len = from_be16(&task->cdb[3]); 139 if (spdk_scsi_task_scatter_data(task, buffer, spdk_min(allocation_len, data_len)) >= 0) { 140 task->data_transferred = data_len; 141 task->status = SPDK_SCSI_STATUS_GOOD; 142 } 143 } else { 144 /* LOGICAL UNIT NOT SUPPORTED */ 145 spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION, 146 SPDK_SCSI_SENSE_ILLEGAL_REQUEST, 147 SPDK_SCSI_ASC_LOGICAL_UNIT_NOT_SUPPORTED, 148 SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE); 149 task->data_transferred = 0; 150 } 151 } 152 153 void 154 spdk_scsi_lun_execute_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task) 155 { 156 int rc; 157 158 task->status = SPDK_SCSI_STATUS_GOOD; 159 spdk_trace_record(TRACE_SCSI_TASK_START, lun->dev->id, task->length, (uintptr_t)task, 0); 160 TAILQ_INSERT_TAIL(&lun->tasks, task, scsi_link); 161 if (!lun->removed) { 162 rc = spdk_bdev_scsi_execute(task); 163 } else { 164 spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION, 165 SPDK_SCSI_SENSE_ABORTED_COMMAND, 166 SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE, 167 SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE); 168 rc = SPDK_SCSI_TASK_COMPLETE; 169 } 170 171 switch (rc) { 172 case SPDK_SCSI_TASK_PENDING: 173 break; 174 175 case SPDK_SCSI_TASK_COMPLETE: 176 spdk_scsi_lun_complete_task(lun, task); 177 break; 178 179 default: 180 abort(); 181 } 182 } 183 184 static void 185 spdk_scsi_lun_remove(struct spdk_scsi_lun *lun) 186 { 187 spdk_bdev_close(lun->bdev_desc); 188 189 spdk_scsi_dev_delete_lun(lun->dev, lun); 190 free(lun); 191 } 192 193 static int 194 spdk_scsi_lun_hot_remove_poll(void *arg) 195 { 196 struct spdk_scsi_lun *lun = (struct spdk_scsi_lun *)arg; 197 198 if (spdk_scsi_lun_has_pending_tasks(lun) || 199 lun->io_channel != NULL) { 200 return -1; 201 } 202 203 spdk_poller_unregister(&lun->hotremove_poller); 204 spdk_scsi_lun_remove(lun); 205 206 return -1; 207 } 208 209 static void 210 _spdk_scsi_lun_hot_remove(void *arg1) 211 { 212 struct spdk_scsi_lun *lun = arg1; 213 214 if (lun->hotremove_cb) { 215 lun->hotremove_cb(lun, lun->hotremove_ctx); 216 } 217 218 if (spdk_scsi_lun_has_pending_tasks(lun) || 219 lun->io_channel != NULL) { 220 lun->hotremove_poller = spdk_poller_register(spdk_scsi_lun_hot_remove_poll, 221 lun, 10); 222 } else { 223 spdk_scsi_lun_remove(lun); 224 } 225 } 226 227 static void 228 spdk_scsi_lun_hot_remove(void *remove_ctx) 229 { 230 struct spdk_scsi_lun *lun = (struct spdk_scsi_lun *)remove_ctx; 231 struct spdk_thread *thread; 232 233 if (lun->removed) { 234 return; 235 } 236 237 lun->removed = true; 238 if (lun->io_channel == NULL) { 239 _spdk_scsi_lun_hot_remove(lun); 240 return; 241 } 242 243 thread = spdk_io_channel_get_thread(lun->io_channel); 244 if (thread != spdk_get_thread()) { 245 spdk_thread_send_msg(thread, _spdk_scsi_lun_hot_remove, lun); 246 } else { 247 _spdk_scsi_lun_hot_remove(lun); 248 } 249 } 250 251 /** 252 * \brief Constructs a new spdk_scsi_lun object based on the provided parameters. 253 * 254 * \param bdev bdev associated with this LUN 255 * 256 * \return NULL if bdev == NULL 257 * \return pointer to the new spdk_scsi_lun object otherwise 258 */ 259 _spdk_scsi_lun * 260 spdk_scsi_lun_construct(struct spdk_bdev *bdev, 261 void (*hotremove_cb)(const struct spdk_scsi_lun *, void *), 262 void *hotremove_ctx) 263 { 264 struct spdk_scsi_lun *lun; 265 int rc; 266 267 if (bdev == NULL) { 268 SPDK_ERRLOG("bdev must be non-NULL\n"); 269 return NULL; 270 } 271 272 lun = calloc(1, sizeof(*lun)); 273 if (lun == NULL) { 274 SPDK_ERRLOG("could not allocate lun\n"); 275 return NULL; 276 } 277 278 rc = spdk_bdev_open(bdev, true, spdk_scsi_lun_hot_remove, lun, &lun->bdev_desc); 279 280 if (rc != 0) { 281 SPDK_ERRLOG("bdev %s cannot be opened, error=%d\n", spdk_bdev_get_name(bdev), rc); 282 free(lun); 283 return NULL; 284 } 285 286 TAILQ_INIT(&lun->tasks); 287 288 lun->bdev = bdev; 289 lun->io_channel = NULL; 290 lun->hotremove_cb = hotremove_cb; 291 lun->hotremove_ctx = hotremove_ctx; 292 293 return lun; 294 } 295 296 void 297 spdk_scsi_lun_destruct(struct spdk_scsi_lun *lun) 298 { 299 spdk_scsi_lun_hot_remove(lun); 300 } 301 302 int spdk_scsi_lun_allocate_io_channel(struct spdk_scsi_lun *lun) 303 { 304 if (lun->io_channel != NULL) { 305 if (spdk_get_thread() == spdk_io_channel_get_thread(lun->io_channel)) { 306 lun->ref++; 307 return 0; 308 } 309 SPDK_ERRLOG("io_channel already allocated for lun %s\n", 310 spdk_bdev_get_name(lun->bdev)); 311 return -1; 312 } 313 314 lun->io_channel = spdk_bdev_get_io_channel(lun->bdev_desc); 315 if (lun->io_channel == NULL) { 316 return -1; 317 } 318 lun->ref = 1; 319 return 0; 320 } 321 322 void spdk_scsi_lun_free_io_channel(struct spdk_scsi_lun *lun) 323 { 324 if (lun->io_channel == NULL) { 325 return; 326 } 327 328 if (spdk_get_thread() != spdk_io_channel_get_thread(lun->io_channel)) { 329 SPDK_ERRLOG("io_channel was freed by different thread\n"); 330 return; 331 } 332 333 lun->ref--; 334 if (lun->ref == 0) { 335 spdk_put_io_channel(lun->io_channel); 336 lun->io_channel = NULL; 337 } 338 } 339 340 int 341 spdk_scsi_lun_get_id(const struct spdk_scsi_lun *lun) 342 { 343 return lun->id; 344 } 345 346 const char * 347 spdk_scsi_lun_get_bdev_name(const struct spdk_scsi_lun *lun) 348 { 349 return spdk_bdev_get_name(lun->bdev); 350 } 351 352 const struct spdk_scsi_dev * 353 spdk_scsi_lun_get_dev(const struct spdk_scsi_lun *lun) 354 { 355 return lun->dev; 356 } 357 358 bool 359 spdk_scsi_lun_has_pending_tasks(const struct spdk_scsi_lun *lun) 360 { 361 return !TAILQ_EMPTY(&lun->tasks); 362 } 363