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 #include "spdk/likely.h" 42 43 void 44 spdk_scsi_lun_complete_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task) 45 { 46 if (lun) { 47 TAILQ_REMOVE(&lun->tasks, task, scsi_link); 48 spdk_trace_record(TRACE_SCSI_TASK_DONE, lun->dev->id, 0, (uintptr_t)task, 0); 49 } 50 task->cpl_fn(task); 51 } 52 53 static void 54 scsi_lun_complete_mgmt_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task) 55 { 56 TAILQ_REMOVE(&lun->mgmt_tasks, task, scsi_link); 57 58 task->cpl_fn(task); 59 60 /* Try to execute the first pending mgmt task if it exists. */ 61 spdk_scsi_lun_execute_mgmt_task(lun); 62 } 63 64 static bool 65 scsi_lun_has_outstanding_tasks(struct spdk_scsi_lun *lun) 66 { 67 return !TAILQ_EMPTY(&lun->tasks); 68 } 69 70 /* Reset task have to wait until all prior outstanding tasks complete. */ 71 static int 72 scsi_lun_reset_check_outstanding_tasks(void *arg) 73 { 74 struct spdk_scsi_task *task = (struct spdk_scsi_task *)arg; 75 struct spdk_scsi_lun *lun = task->lun; 76 77 if (scsi_lun_has_outstanding_tasks(lun)) { 78 return 0; 79 } 80 spdk_poller_unregister(&lun->reset_poller); 81 82 scsi_lun_complete_mgmt_task(lun, task); 83 return 1; 84 } 85 86 void 87 spdk_scsi_lun_complete_reset_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task) 88 { 89 if (task->status == SPDK_SCSI_STATUS_GOOD) { 90 if (scsi_lun_has_outstanding_tasks(lun)) { 91 lun->reset_poller = 92 spdk_poller_register(scsi_lun_reset_check_outstanding_tasks, 93 task, 10); 94 return; 95 } 96 } 97 98 scsi_lun_complete_mgmt_task(lun, task); 99 } 100 101 static void 102 _scsi_lun_execute_mgmt_task(struct spdk_scsi_lun *lun, 103 struct spdk_scsi_task *task) 104 { 105 TAILQ_INSERT_TAIL(&lun->mgmt_tasks, task, scsi_link); 106 107 switch (task->function) { 108 case SPDK_SCSI_TASK_FUNC_ABORT_TASK: 109 task->response = SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED; 110 SPDK_ERRLOG("ABORT_TASK failed\n"); 111 break; 112 113 case SPDK_SCSI_TASK_FUNC_ABORT_TASK_SET: 114 task->response = SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED; 115 SPDK_ERRLOG("ABORT_TASK_SET failed\n"); 116 break; 117 118 case SPDK_SCSI_TASK_FUNC_LUN_RESET: 119 spdk_bdev_scsi_reset(task); 120 return; 121 122 default: 123 SPDK_ERRLOG("Unknown Task Management Function!\n"); 124 /* 125 * Task management functions other than those above should never 126 * reach this point having been filtered by the frontend. Reject 127 * the task as being unsupported. 128 */ 129 task->response = SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED; 130 break; 131 } 132 133 scsi_lun_complete_mgmt_task(lun, task); 134 } 135 136 void 137 spdk_scsi_lun_append_mgmt_task(struct spdk_scsi_lun *lun, 138 struct spdk_scsi_task *task) 139 { 140 TAILQ_INSERT_TAIL(&lun->pending_mgmt_tasks, task, scsi_link); 141 } 142 143 void 144 spdk_scsi_lun_execute_mgmt_task(struct spdk_scsi_lun *lun) 145 { 146 struct spdk_scsi_task *task; 147 148 if (!TAILQ_EMPTY(&lun->mgmt_tasks)) { 149 return; 150 } 151 152 task = TAILQ_FIRST(&lun->pending_mgmt_tasks); 153 if (spdk_likely(task == NULL)) { 154 /* Try to execute all pending tasks */ 155 spdk_scsi_lun_execute_tasks(lun); 156 return; 157 } 158 TAILQ_REMOVE(&lun->pending_mgmt_tasks, task, scsi_link); 159 160 _scsi_lun_execute_mgmt_task(lun, task); 161 } 162 163 static void 164 _scsi_lun_execute_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task) 165 { 166 int rc; 167 168 task->status = SPDK_SCSI_STATUS_GOOD; 169 spdk_trace_record(TRACE_SCSI_TASK_START, lun->dev->id, task->length, (uintptr_t)task, 0); 170 TAILQ_INSERT_TAIL(&lun->tasks, task, scsi_link); 171 if (!lun->removed) { 172 rc = spdk_bdev_scsi_execute(task); 173 } else { 174 spdk_scsi_task_process_abort(task); 175 rc = SPDK_SCSI_TASK_COMPLETE; 176 } 177 178 switch (rc) { 179 case SPDK_SCSI_TASK_PENDING: 180 break; 181 182 case SPDK_SCSI_TASK_COMPLETE: 183 spdk_scsi_lun_complete_task(lun, task); 184 break; 185 186 default: 187 abort(); 188 } 189 } 190 191 void 192 spdk_scsi_lun_append_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task) 193 { 194 TAILQ_INSERT_TAIL(&lun->pending_tasks, task, scsi_link); 195 } 196 197 void 198 spdk_scsi_lun_execute_tasks(struct spdk_scsi_lun *lun) 199 { 200 struct spdk_scsi_task *task, *task_tmp; 201 202 if (spdk_scsi_lun_has_pending_mgmt_tasks(lun)) { 203 /* Pending IO tasks will wait for completion of existing mgmt tasks. 204 */ 205 return; 206 } 207 208 TAILQ_FOREACH_SAFE(task, &lun->pending_tasks, scsi_link, task_tmp) { 209 TAILQ_REMOVE(&lun->pending_tasks, task, scsi_link); 210 _scsi_lun_execute_task(lun, task); 211 } 212 } 213 214 static void 215 scsi_lun_remove(struct spdk_scsi_lun *lun) 216 { 217 spdk_bdev_close(lun->bdev_desc); 218 219 free(lun); 220 } 221 222 static int 223 scsi_lun_check_io_channel(void *arg) 224 { 225 struct spdk_scsi_lun *lun = (struct spdk_scsi_lun *)arg; 226 227 if (lun->io_channel) { 228 return -1; 229 } 230 spdk_poller_unregister(&lun->hotremove_poller); 231 232 scsi_lun_remove(lun); 233 return -1; 234 } 235 236 static void 237 scsi_lun_notify_hot_remove(struct spdk_scsi_lun *lun) 238 { 239 struct spdk_scsi_lun_desc *desc, *tmp; 240 241 if (lun->hotremove_cb) { 242 lun->hotremove_cb(lun, lun->hotremove_ctx); 243 } 244 245 TAILQ_FOREACH_SAFE(desc, &lun->open_descs, link, tmp) { 246 if (desc->hotremove_cb) { 247 desc->hotremove_cb(lun, desc->hotremove_ctx); 248 } else { 249 spdk_scsi_lun_close(desc); 250 } 251 } 252 253 if (lun->io_channel) { 254 lun->hotremove_poller = spdk_poller_register(scsi_lun_check_io_channel, 255 lun, 10); 256 } else { 257 scsi_lun_remove(lun); 258 } 259 } 260 261 static int 262 scsi_lun_check_pending_tasks(void *arg) 263 { 264 struct spdk_scsi_lun *lun = (struct spdk_scsi_lun *)arg; 265 266 if (spdk_scsi_lun_has_pending_tasks(lun) || 267 spdk_scsi_lun_has_pending_mgmt_tasks(lun)) { 268 return -1; 269 } 270 spdk_poller_unregister(&lun->hotremove_poller); 271 272 scsi_lun_notify_hot_remove(lun); 273 return -1; 274 } 275 276 static void 277 _scsi_lun_hot_remove(void *arg1) 278 { 279 struct spdk_scsi_lun *lun = arg1; 280 281 if (spdk_scsi_lun_has_pending_tasks(lun) || 282 spdk_scsi_lun_has_pending_mgmt_tasks(lun)) { 283 lun->hotremove_poller = spdk_poller_register(scsi_lun_check_pending_tasks, 284 lun, 10); 285 } else { 286 scsi_lun_notify_hot_remove(lun); 287 } 288 } 289 290 static void 291 scsi_lun_hot_remove(void *remove_ctx) 292 { 293 struct spdk_scsi_lun *lun = (struct spdk_scsi_lun *)remove_ctx; 294 struct spdk_thread *thread; 295 296 if (lun->removed) { 297 return; 298 } 299 300 lun->removed = true; 301 spdk_scsi_dev_delete_lun(lun->dev, lun); 302 303 if (lun->io_channel == NULL) { 304 scsi_lun_remove(lun); 305 return; 306 } 307 308 thread = spdk_io_channel_get_thread(lun->io_channel); 309 if (thread != spdk_get_thread()) { 310 spdk_thread_send_msg(thread, _scsi_lun_hot_remove, lun); 311 } else { 312 _scsi_lun_hot_remove(lun); 313 } 314 } 315 316 /** 317 * \brief Constructs a new spdk_scsi_lun object based on the provided parameters. 318 * 319 * \param bdev bdev associated with this LUN 320 * 321 * \return NULL if bdev == NULL 322 * \return pointer to the new spdk_scsi_lun object otherwise 323 */ 324 _spdk_scsi_lun * 325 spdk_scsi_lun_construct(struct spdk_bdev *bdev, 326 void (*hotremove_cb)(const struct spdk_scsi_lun *, void *), 327 void *hotremove_ctx) 328 { 329 struct spdk_scsi_lun *lun; 330 int rc; 331 332 if (bdev == NULL) { 333 SPDK_ERRLOG("bdev must be non-NULL\n"); 334 return NULL; 335 } 336 337 lun = calloc(1, sizeof(*lun)); 338 if (lun == NULL) { 339 SPDK_ERRLOG("could not allocate lun\n"); 340 return NULL; 341 } 342 343 rc = spdk_bdev_open(bdev, true, scsi_lun_hot_remove, lun, &lun->bdev_desc); 344 345 if (rc != 0) { 346 SPDK_ERRLOG("bdev %s cannot be opened, error=%d\n", spdk_bdev_get_name(bdev), rc); 347 free(lun); 348 return NULL; 349 } 350 351 TAILQ_INIT(&lun->tasks); 352 TAILQ_INIT(&lun->pending_tasks); 353 TAILQ_INIT(&lun->mgmt_tasks); 354 TAILQ_INIT(&lun->pending_mgmt_tasks); 355 356 lun->bdev = bdev; 357 lun->io_channel = NULL; 358 lun->hotremove_cb = hotremove_cb; 359 lun->hotremove_ctx = hotremove_ctx; 360 TAILQ_INIT(&lun->open_descs); 361 362 return lun; 363 } 364 365 void 366 spdk_scsi_lun_destruct(struct spdk_scsi_lun *lun) 367 { 368 scsi_lun_hot_remove(lun); 369 } 370 371 int 372 spdk_scsi_lun_open(struct spdk_scsi_lun *lun, spdk_scsi_lun_remove_cb_t hotremove_cb, 373 void *hotremove_ctx, struct spdk_scsi_lun_desc **_desc) 374 { 375 struct spdk_scsi_lun_desc *desc; 376 377 desc = calloc(1, sizeof(*desc)); 378 if (desc == NULL) { 379 SPDK_ERRLOG("calloc() failed for LUN descriptor.\n"); 380 return -ENOMEM; 381 } 382 383 TAILQ_INSERT_TAIL(&lun->open_descs, desc, link); 384 385 desc->lun = lun; 386 desc->hotremove_cb = hotremove_cb; 387 desc->hotremove_ctx = hotremove_ctx; 388 *_desc = desc; 389 390 return 0; 391 } 392 393 void 394 spdk_scsi_lun_close(struct spdk_scsi_lun_desc *desc) 395 { 396 struct spdk_scsi_lun *lun = desc->lun; 397 398 TAILQ_REMOVE(&lun->open_descs, desc, link); 399 free(desc); 400 401 assert(!TAILQ_EMPTY(&lun->open_descs) || lun->io_channel == NULL); 402 } 403 404 int 405 _spdk_scsi_lun_allocate_io_channel(struct spdk_scsi_lun *lun) 406 { 407 if (lun->io_channel != NULL) { 408 if (spdk_get_thread() == spdk_io_channel_get_thread(lun->io_channel)) { 409 lun->ref++; 410 return 0; 411 } 412 SPDK_ERRLOG("io_channel already allocated for lun %s\n", 413 spdk_bdev_get_name(lun->bdev)); 414 return -1; 415 } 416 417 lun->io_channel = spdk_bdev_get_io_channel(lun->bdev_desc); 418 if (lun->io_channel == NULL) { 419 return -1; 420 } 421 lun->ref = 1; 422 return 0; 423 } 424 425 void 426 _spdk_scsi_lun_free_io_channel(struct spdk_scsi_lun *lun) 427 { 428 if (lun->io_channel == NULL) { 429 return; 430 } 431 432 if (spdk_get_thread() != spdk_io_channel_get_thread(lun->io_channel)) { 433 SPDK_ERRLOG("io_channel was freed by different thread\n"); 434 return; 435 } 436 437 lun->ref--; 438 if (lun->ref == 0) { 439 spdk_put_io_channel(lun->io_channel); 440 lun->io_channel = NULL; 441 } 442 } 443 444 int 445 spdk_scsi_lun_allocate_io_channel(struct spdk_scsi_lun_desc *desc) 446 { 447 struct spdk_scsi_lun *lun = desc->lun; 448 449 return _spdk_scsi_lun_allocate_io_channel(lun); 450 } 451 452 void 453 spdk_scsi_lun_free_io_channel(struct spdk_scsi_lun_desc *desc) 454 { 455 struct spdk_scsi_lun *lun = desc->lun; 456 457 _spdk_scsi_lun_free_io_channel(lun); 458 } 459 460 int 461 spdk_scsi_lun_get_id(const struct spdk_scsi_lun *lun) 462 { 463 return lun->id; 464 } 465 466 const char * 467 spdk_scsi_lun_get_bdev_name(const struct spdk_scsi_lun *lun) 468 { 469 return spdk_bdev_get_name(lun->bdev); 470 } 471 472 const struct spdk_scsi_dev * 473 spdk_scsi_lun_get_dev(const struct spdk_scsi_lun *lun) 474 { 475 return lun->dev; 476 } 477 478 bool 479 spdk_scsi_lun_has_pending_mgmt_tasks(const struct spdk_scsi_lun *lun) 480 { 481 return !TAILQ_EMPTY(&lun->pending_mgmt_tasks) || 482 !TAILQ_EMPTY(&lun->mgmt_tasks); 483 } 484 485 /* This check includes both pending and submitted (outstanding) tasks. */ 486 bool 487 spdk_scsi_lun_has_pending_tasks(const struct spdk_scsi_lun *lun) 488 { 489 return !TAILQ_EMPTY(&lun->pending_tasks) || 490 !TAILQ_EMPTY(&lun->tasks); 491 } 492 493 bool 494 spdk_scsi_lun_is_removing(const struct spdk_scsi_lun *lun) 495 { 496 return lun->removed; 497 } 498 499 bool 500 spdk_scsi_lun_get_dif_ctx(struct spdk_scsi_lun *lun, uint8_t *cdb, 501 uint32_t offset, struct spdk_dif_ctx *dif_ctx) 502 { 503 return spdk_scsi_bdev_get_dif_ctx(lun->bdev, cdb, offset, dif_ctx); 504 } 505