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 /* Check the command is allowed or not when reservation is exist */ 173 rc = spdk_scsi_pr_check(task); 174 if (spdk_unlikely(rc < 0)) { 175 /* Reservation Conflict */ 176 rc = SPDK_SCSI_TASK_COMPLETE; 177 } else { 178 rc = spdk_bdev_scsi_execute(task); 179 } 180 } else { 181 spdk_scsi_task_process_abort(task); 182 rc = SPDK_SCSI_TASK_COMPLETE; 183 } 184 185 switch (rc) { 186 case SPDK_SCSI_TASK_PENDING: 187 break; 188 189 case SPDK_SCSI_TASK_COMPLETE: 190 spdk_scsi_lun_complete_task(lun, task); 191 break; 192 193 default: 194 abort(); 195 } 196 } 197 198 void 199 spdk_scsi_lun_append_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task) 200 { 201 TAILQ_INSERT_TAIL(&lun->pending_tasks, task, scsi_link); 202 } 203 204 void 205 spdk_scsi_lun_execute_tasks(struct spdk_scsi_lun *lun) 206 { 207 struct spdk_scsi_task *task, *task_tmp; 208 209 if (spdk_scsi_lun_has_pending_mgmt_tasks(lun)) { 210 /* Pending IO tasks will wait for completion of existing mgmt tasks. 211 */ 212 return; 213 } 214 215 TAILQ_FOREACH_SAFE(task, &lun->pending_tasks, scsi_link, task_tmp) { 216 TAILQ_REMOVE(&lun->pending_tasks, task, scsi_link); 217 _scsi_lun_execute_task(lun, task); 218 } 219 } 220 221 static void 222 scsi_lun_remove(struct spdk_scsi_lun *lun) 223 { 224 struct spdk_scsi_pr_registrant *reg, *tmp; 225 226 TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) { 227 TAILQ_REMOVE(&lun->reg_head, reg, link); 228 free(reg); 229 } 230 spdk_bdev_close(lun->bdev_desc); 231 232 spdk_scsi_dev_delete_lun(lun->dev, lun); 233 free(lun); 234 } 235 236 static int 237 scsi_lun_check_io_channel(void *arg) 238 { 239 struct spdk_scsi_lun *lun = (struct spdk_scsi_lun *)arg; 240 241 if (lun->io_channel) { 242 return -1; 243 } 244 spdk_poller_unregister(&lun->hotremove_poller); 245 246 scsi_lun_remove(lun); 247 return -1; 248 } 249 250 static void 251 scsi_lun_notify_hot_remove(struct spdk_scsi_lun *lun) 252 { 253 struct spdk_scsi_lun_desc *desc, *tmp; 254 255 if (lun->hotremove_cb) { 256 lun->hotremove_cb(lun, lun->hotremove_ctx); 257 } 258 259 TAILQ_FOREACH_SAFE(desc, &lun->open_descs, link, tmp) { 260 if (desc->hotremove_cb) { 261 desc->hotremove_cb(lun, desc->hotremove_ctx); 262 } else { 263 spdk_scsi_lun_close(desc); 264 } 265 } 266 267 if (lun->io_channel) { 268 lun->hotremove_poller = spdk_poller_register(scsi_lun_check_io_channel, 269 lun, 10); 270 } else { 271 scsi_lun_remove(lun); 272 } 273 } 274 275 static int 276 scsi_lun_check_pending_tasks(void *arg) 277 { 278 struct spdk_scsi_lun *lun = (struct spdk_scsi_lun *)arg; 279 280 if (spdk_scsi_lun_has_pending_tasks(lun) || 281 spdk_scsi_lun_has_pending_mgmt_tasks(lun)) { 282 return -1; 283 } 284 spdk_poller_unregister(&lun->hotremove_poller); 285 286 scsi_lun_notify_hot_remove(lun); 287 return -1; 288 } 289 290 static void 291 _scsi_lun_hot_remove(void *arg1) 292 { 293 struct spdk_scsi_lun *lun = arg1; 294 295 if (spdk_scsi_lun_has_pending_tasks(lun) || 296 spdk_scsi_lun_has_pending_mgmt_tasks(lun)) { 297 lun->hotremove_poller = spdk_poller_register(scsi_lun_check_pending_tasks, 298 lun, 10); 299 } else { 300 scsi_lun_notify_hot_remove(lun); 301 } 302 } 303 304 static void 305 scsi_lun_hot_remove(void *remove_ctx) 306 { 307 struct spdk_scsi_lun *lun = (struct spdk_scsi_lun *)remove_ctx; 308 struct spdk_thread *thread; 309 310 if (lun->removed) { 311 return; 312 } 313 314 lun->removed = true; 315 if (lun->io_channel == NULL) { 316 _scsi_lun_hot_remove(lun); 317 return; 318 } 319 320 thread = spdk_io_channel_get_thread(lun->io_channel); 321 if (thread != spdk_get_thread()) { 322 spdk_thread_send_msg(thread, _scsi_lun_hot_remove, lun); 323 } else { 324 _scsi_lun_hot_remove(lun); 325 } 326 } 327 328 /** 329 * \brief Constructs a new spdk_scsi_lun object based on the provided parameters. 330 * 331 * \param bdev bdev associated with this LUN 332 * 333 * \return NULL if bdev == NULL 334 * \return pointer to the new spdk_scsi_lun object otherwise 335 */ 336 _spdk_scsi_lun * 337 spdk_scsi_lun_construct(struct spdk_bdev *bdev, 338 void (*hotremove_cb)(const struct spdk_scsi_lun *, void *), 339 void *hotremove_ctx) 340 { 341 struct spdk_scsi_lun *lun; 342 int rc; 343 344 if (bdev == NULL) { 345 SPDK_ERRLOG("bdev must be non-NULL\n"); 346 return NULL; 347 } 348 349 lun = calloc(1, sizeof(*lun)); 350 if (lun == NULL) { 351 SPDK_ERRLOG("could not allocate lun\n"); 352 return NULL; 353 } 354 355 rc = spdk_bdev_open(bdev, true, scsi_lun_hot_remove, lun, &lun->bdev_desc); 356 357 if (rc != 0) { 358 SPDK_ERRLOG("bdev %s cannot be opened, error=%d\n", spdk_bdev_get_name(bdev), rc); 359 free(lun); 360 return NULL; 361 } 362 363 TAILQ_INIT(&lun->tasks); 364 TAILQ_INIT(&lun->pending_tasks); 365 TAILQ_INIT(&lun->mgmt_tasks); 366 TAILQ_INIT(&lun->pending_mgmt_tasks); 367 368 lun->bdev = bdev; 369 lun->io_channel = NULL; 370 lun->hotremove_cb = hotremove_cb; 371 lun->hotremove_ctx = hotremove_ctx; 372 TAILQ_INIT(&lun->open_descs); 373 TAILQ_INIT(&lun->reg_head); 374 375 return lun; 376 } 377 378 void 379 spdk_scsi_lun_destruct(struct spdk_scsi_lun *lun) 380 { 381 scsi_lun_hot_remove(lun); 382 } 383 384 int 385 spdk_scsi_lun_open(struct spdk_scsi_lun *lun, spdk_scsi_lun_remove_cb_t hotremove_cb, 386 void *hotremove_ctx, struct spdk_scsi_lun_desc **_desc) 387 { 388 struct spdk_scsi_lun_desc *desc; 389 390 desc = calloc(1, sizeof(*desc)); 391 if (desc == NULL) { 392 SPDK_ERRLOG("calloc() failed for LUN descriptor.\n"); 393 return -ENOMEM; 394 } 395 396 TAILQ_INSERT_TAIL(&lun->open_descs, desc, link); 397 398 desc->lun = lun; 399 desc->hotremove_cb = hotremove_cb; 400 desc->hotremove_ctx = hotremove_ctx; 401 *_desc = desc; 402 403 return 0; 404 } 405 406 void 407 spdk_scsi_lun_close(struct spdk_scsi_lun_desc *desc) 408 { 409 struct spdk_scsi_lun *lun = desc->lun; 410 411 TAILQ_REMOVE(&lun->open_descs, desc, link); 412 free(desc); 413 414 assert(!TAILQ_EMPTY(&lun->open_descs) || lun->io_channel == NULL); 415 } 416 417 int 418 _spdk_scsi_lun_allocate_io_channel(struct spdk_scsi_lun *lun) 419 { 420 if (lun->io_channel != NULL) { 421 if (spdk_get_thread() == spdk_io_channel_get_thread(lun->io_channel)) { 422 lun->ref++; 423 return 0; 424 } 425 SPDK_ERRLOG("io_channel already allocated for lun %s\n", 426 spdk_bdev_get_name(lun->bdev)); 427 return -1; 428 } 429 430 lun->io_channel = spdk_bdev_get_io_channel(lun->bdev_desc); 431 if (lun->io_channel == NULL) { 432 return -1; 433 } 434 lun->ref = 1; 435 return 0; 436 } 437 438 void 439 _spdk_scsi_lun_free_io_channel(struct spdk_scsi_lun *lun) 440 { 441 if (lun->io_channel == NULL) { 442 return; 443 } 444 445 if (spdk_get_thread() != spdk_io_channel_get_thread(lun->io_channel)) { 446 SPDK_ERRLOG("io_channel was freed by different thread\n"); 447 return; 448 } 449 450 lun->ref--; 451 if (lun->ref == 0) { 452 spdk_put_io_channel(lun->io_channel); 453 lun->io_channel = NULL; 454 } 455 } 456 457 int 458 spdk_scsi_lun_allocate_io_channel(struct spdk_scsi_lun_desc *desc) 459 { 460 struct spdk_scsi_lun *lun = desc->lun; 461 462 return _spdk_scsi_lun_allocate_io_channel(lun); 463 } 464 465 void 466 spdk_scsi_lun_free_io_channel(struct spdk_scsi_lun_desc *desc) 467 { 468 struct spdk_scsi_lun *lun = desc->lun; 469 470 _spdk_scsi_lun_free_io_channel(lun); 471 } 472 473 int 474 spdk_scsi_lun_get_id(const struct spdk_scsi_lun *lun) 475 { 476 return lun->id; 477 } 478 479 const char * 480 spdk_scsi_lun_get_bdev_name(const struct spdk_scsi_lun *lun) 481 { 482 return spdk_bdev_get_name(lun->bdev); 483 } 484 485 const struct spdk_scsi_dev * 486 spdk_scsi_lun_get_dev(const struct spdk_scsi_lun *lun) 487 { 488 return lun->dev; 489 } 490 491 bool 492 spdk_scsi_lun_has_pending_mgmt_tasks(const struct spdk_scsi_lun *lun) 493 { 494 return !TAILQ_EMPTY(&lun->pending_mgmt_tasks) || 495 !TAILQ_EMPTY(&lun->mgmt_tasks); 496 } 497 498 /* This check includes both pending and submitted (outstanding) tasks. */ 499 bool 500 spdk_scsi_lun_has_pending_tasks(const struct spdk_scsi_lun *lun) 501 { 502 return !TAILQ_EMPTY(&lun->pending_tasks) || 503 !TAILQ_EMPTY(&lun->tasks); 504 } 505 506 bool 507 spdk_scsi_lun_is_removing(const struct spdk_scsi_lun *lun) 508 { 509 return lun->removed; 510 } 511 512 bool 513 spdk_scsi_lun_get_dif_ctx(struct spdk_scsi_lun *lun, uint8_t *cdb, 514 uint32_t data_offset, struct spdk_dif_ctx *dif_ctx) 515 { 516 return spdk_scsi_bdev_get_dif_ctx(lun->bdev, cdb, data_offset, dif_ctx); 517 } 518