diff options
-rw-r--r-- | drivers/nvme/target/fcloop.c | 147 |
1 files changed, 125 insertions, 22 deletions
diff --git a/drivers/nvme/target/fcloop.c b/drivers/nvme/target/fcloop.c index c5015199c031..9f8a6726df91 100644 --- a/drivers/nvme/target/fcloop.c +++ b/drivers/nvme/target/fcloop.c | |||
@@ -242,13 +242,22 @@ struct fcloop_lsreq { | |||
242 | int status; | 242 | int status; |
243 | }; | 243 | }; |
244 | 244 | ||
245 | enum { | ||
246 | INI_IO_START = 0, | ||
247 | INI_IO_ACTIVE = 1, | ||
248 | INI_IO_ABORTED = 2, | ||
249 | INI_IO_COMPLETED = 3, | ||
250 | }; | ||
251 | |||
245 | struct fcloop_fcpreq { | 252 | struct fcloop_fcpreq { |
246 | struct fcloop_tport *tport; | 253 | struct fcloop_tport *tport; |
247 | struct nvmefc_fcp_req *fcpreq; | 254 | struct nvmefc_fcp_req *fcpreq; |
248 | spinlock_t reqlock; | 255 | spinlock_t reqlock; |
249 | u16 status; | 256 | u16 status; |
257 | u32 inistate; | ||
250 | bool active; | 258 | bool active; |
251 | bool aborted; | 259 | bool aborted; |
260 | struct kref ref; | ||
252 | struct work_struct fcp_rcv_work; | 261 | struct work_struct fcp_rcv_work; |
253 | struct work_struct abort_rcv_work; | 262 | struct work_struct abort_rcv_work; |
254 | struct work_struct tio_done_work; | 263 | struct work_struct tio_done_work; |
@@ -258,6 +267,7 @@ struct fcloop_fcpreq { | |||
258 | struct fcloop_ini_fcpreq { | 267 | struct fcloop_ini_fcpreq { |
259 | struct nvmefc_fcp_req *fcpreq; | 268 | struct nvmefc_fcp_req *fcpreq; |
260 | struct fcloop_fcpreq *tfcp_req; | 269 | struct fcloop_fcpreq *tfcp_req; |
270 | spinlock_t inilock; | ||
261 | }; | 271 | }; |
262 | 272 | ||
263 | static inline struct fcloop_lsreq * | 273 | static inline struct fcloop_lsreq * |
@@ -349,24 +359,24 @@ fcloop_xmt_ls_rsp(struct nvmet_fc_target_port *tport, | |||
349 | } | 359 | } |
350 | 360 | ||
351 | static void | 361 | static void |
352 | fcloop_fcp_recv_work(struct work_struct *work) | 362 | fcloop_tfcp_req_free(struct kref *ref) |
353 | { | 363 | { |
354 | struct fcloop_fcpreq *tfcp_req = | 364 | struct fcloop_fcpreq *tfcp_req = |
355 | container_of(work, struct fcloop_fcpreq, fcp_rcv_work); | 365 | container_of(ref, struct fcloop_fcpreq, ref); |
356 | struct nvmefc_fcp_req *fcpreq = tfcp_req->fcpreq; | ||
357 | struct fcloop_ini_fcpreq *inireq = NULL; | ||
358 | int ret = 0; | ||
359 | 366 | ||
360 | ret = nvmet_fc_rcv_fcp_req(tfcp_req->tport->targetport, | 367 | kfree(tfcp_req); |
361 | &tfcp_req->tgt_fcp_req, | 368 | } |
362 | fcpreq->cmdaddr, fcpreq->cmdlen); | ||
363 | if (ret) { | ||
364 | inireq = fcpreq->private; | ||
365 | inireq->tfcp_req = NULL; | ||
366 | 369 | ||
367 | fcpreq->status = tfcp_req->status; | 370 | static void |
368 | fcpreq->done(fcpreq); | 371 | fcloop_tfcp_req_put(struct fcloop_fcpreq *tfcp_req) |
369 | } | 372 | { |
373 | kref_put(&tfcp_req->ref, fcloop_tfcp_req_free); | ||
374 | } | ||
375 | |||
376 | static int | ||
377 | fcloop_tfcp_req_get(struct fcloop_fcpreq *tfcp_req) | ||
378 | { | ||
379 | return kref_get_unless_zero(&tfcp_req->ref); | ||
370 | } | 380 | } |
371 | 381 | ||
372 | static void | 382 | static void |
@@ -377,11 +387,52 @@ fcloop_call_host_done(struct nvmefc_fcp_req *fcpreq, | |||
377 | 387 | ||
378 | if (fcpreq) { | 388 | if (fcpreq) { |
379 | inireq = fcpreq->private; | 389 | inireq = fcpreq->private; |
390 | spin_lock(&inireq->inilock); | ||
380 | inireq->tfcp_req = NULL; | 391 | inireq->tfcp_req = NULL; |
392 | spin_unlock(&inireq->inilock); | ||
381 | 393 | ||
382 | fcpreq->status = status; | 394 | fcpreq->status = status; |
383 | fcpreq->done(fcpreq); | 395 | fcpreq->done(fcpreq); |
384 | } | 396 | } |
397 | |||
398 | /* release original io reference on tgt struct */ | ||
399 | fcloop_tfcp_req_put(tfcp_req); | ||
400 | } | ||
401 | |||
402 | static void | ||
403 | fcloop_fcp_recv_work(struct work_struct *work) | ||
404 | { | ||
405 | struct fcloop_fcpreq *tfcp_req = | ||
406 | container_of(work, struct fcloop_fcpreq, fcp_rcv_work); | ||
407 | struct nvmefc_fcp_req *fcpreq = tfcp_req->fcpreq; | ||
408 | int ret = 0; | ||
409 | bool aborted = false; | ||
410 | |||
411 | spin_lock(&tfcp_req->reqlock); | ||
412 | switch (tfcp_req->inistate) { | ||
413 | case INI_IO_START: | ||
414 | tfcp_req->inistate = INI_IO_ACTIVE; | ||
415 | break; | ||
416 | case INI_IO_ABORTED: | ||
417 | aborted = true; | ||
418 | break; | ||
419 | default: | ||
420 | spin_unlock(&tfcp_req->reqlock); | ||
421 | WARN_ON(1); | ||
422 | return; | ||
423 | } | ||
424 | spin_unlock(&tfcp_req->reqlock); | ||
425 | |||
426 | if (unlikely(aborted)) | ||
427 | ret = -ECANCELED; | ||
428 | else | ||
429 | ret = nvmet_fc_rcv_fcp_req(tfcp_req->tport->targetport, | ||
430 | &tfcp_req->tgt_fcp_req, | ||
431 | fcpreq->cmdaddr, fcpreq->cmdlen); | ||
432 | if (ret) | ||
433 | fcloop_call_host_done(fcpreq, tfcp_req, ret); | ||
434 | |||
435 | return; | ||
385 | } | 436 | } |
386 | 437 | ||
387 | static void | 438 | static void |
@@ -389,7 +440,29 @@ fcloop_fcp_abort_recv_work(struct work_struct *work) | |||
389 | { | 440 | { |
390 | struct fcloop_fcpreq *tfcp_req = | 441 | struct fcloop_fcpreq *tfcp_req = |
391 | container_of(work, struct fcloop_fcpreq, abort_rcv_work); | 442 | container_of(work, struct fcloop_fcpreq, abort_rcv_work); |
392 | struct nvmefc_fcp_req *fcpreq = tfcp_req->fcpreq; | 443 | struct nvmefc_fcp_req *fcpreq; |
444 | bool completed = false; | ||
445 | |||
446 | spin_lock(&tfcp_req->reqlock); | ||
447 | fcpreq = tfcp_req->fcpreq; | ||
448 | switch (tfcp_req->inistate) { | ||
449 | case INI_IO_ABORTED: | ||
450 | break; | ||
451 | case INI_IO_COMPLETED: | ||
452 | completed = true; | ||
453 | break; | ||
454 | default: | ||
455 | spin_unlock(&tfcp_req->reqlock); | ||
456 | WARN_ON(1); | ||
457 | return; | ||
458 | } | ||
459 | spin_unlock(&tfcp_req->reqlock); | ||
460 | |||
461 | if (unlikely(completed)) { | ||
462 | /* remove reference taken in original abort downcall */ | ||
463 | fcloop_tfcp_req_put(tfcp_req); | ||
464 | return; | ||
465 | } | ||
393 | 466 | ||
394 | if (tfcp_req->tport->targetport) | 467 | if (tfcp_req->tport->targetport) |
395 | nvmet_fc_rcv_fcp_abort(tfcp_req->tport->targetport, | 468 | nvmet_fc_rcv_fcp_abort(tfcp_req->tport->targetport, |
@@ -400,6 +473,7 @@ fcloop_fcp_abort_recv_work(struct work_struct *work) | |||
400 | spin_unlock(&tfcp_req->reqlock); | 473 | spin_unlock(&tfcp_req->reqlock); |
401 | 474 | ||
402 | fcloop_call_host_done(fcpreq, tfcp_req, -ECANCELED); | 475 | fcloop_call_host_done(fcpreq, tfcp_req, -ECANCELED); |
476 | /* call_host_done releases reference for abort downcall */ | ||
403 | } | 477 | } |
404 | 478 | ||
405 | /* | 479 | /* |
@@ -415,12 +489,10 @@ fcloop_tgt_fcprqst_done_work(struct work_struct *work) | |||
415 | 489 | ||
416 | spin_lock(&tfcp_req->reqlock); | 490 | spin_lock(&tfcp_req->reqlock); |
417 | fcpreq = tfcp_req->fcpreq; | 491 | fcpreq = tfcp_req->fcpreq; |
418 | tfcp_req->fcpreq = NULL; | 492 | tfcp_req->inistate = INI_IO_COMPLETED; |
419 | spin_unlock(&tfcp_req->reqlock); | 493 | spin_unlock(&tfcp_req->reqlock); |
420 | 494 | ||
421 | fcloop_call_host_done(fcpreq, tfcp_req, tfcp_req->status); | 495 | fcloop_call_host_done(fcpreq, tfcp_req, tfcp_req->status); |
422 | |||
423 | kfree(tfcp_req); | ||
424 | } | 496 | } |
425 | 497 | ||
426 | 498 | ||
@@ -443,12 +515,16 @@ fcloop_fcp_req(struct nvme_fc_local_port *localport, | |||
443 | 515 | ||
444 | inireq->fcpreq = fcpreq; | 516 | inireq->fcpreq = fcpreq; |
445 | inireq->tfcp_req = tfcp_req; | 517 | inireq->tfcp_req = tfcp_req; |
518 | spin_lock_init(&inireq->inilock); | ||
519 | |||
446 | tfcp_req->fcpreq = fcpreq; | 520 | tfcp_req->fcpreq = fcpreq; |
447 | tfcp_req->tport = rport->targetport->private; | 521 | tfcp_req->tport = rport->targetport->private; |
522 | tfcp_req->inistate = INI_IO_START; | ||
448 | spin_lock_init(&tfcp_req->reqlock); | 523 | spin_lock_init(&tfcp_req->reqlock); |
449 | INIT_WORK(&tfcp_req->fcp_rcv_work, fcloop_fcp_recv_work); | 524 | INIT_WORK(&tfcp_req->fcp_rcv_work, fcloop_fcp_recv_work); |
450 | INIT_WORK(&tfcp_req->abort_rcv_work, fcloop_fcp_abort_recv_work); | 525 | INIT_WORK(&tfcp_req->abort_rcv_work, fcloop_fcp_abort_recv_work); |
451 | INIT_WORK(&tfcp_req->tio_done_work, fcloop_tgt_fcprqst_done_work); | 526 | INIT_WORK(&tfcp_req->tio_done_work, fcloop_tgt_fcprqst_done_work); |
527 | kref_init(&tfcp_req->ref); | ||
452 | 528 | ||
453 | schedule_work(&tfcp_req->fcp_rcv_work); | 529 | schedule_work(&tfcp_req->fcp_rcv_work); |
454 | 530 | ||
@@ -648,7 +724,14 @@ fcloop_fcp_abort(struct nvme_fc_local_port *localport, | |||
648 | struct nvmefc_fcp_req *fcpreq) | 724 | struct nvmefc_fcp_req *fcpreq) |
649 | { | 725 | { |
650 | struct fcloop_ini_fcpreq *inireq = fcpreq->private; | 726 | struct fcloop_ini_fcpreq *inireq = fcpreq->private; |
651 | struct fcloop_fcpreq *tfcp_req = inireq->tfcp_req; | 727 | struct fcloop_fcpreq *tfcp_req; |
728 | bool abortio = true; | ||
729 | |||
730 | spin_lock(&inireq->inilock); | ||
731 | tfcp_req = inireq->tfcp_req; | ||
732 | if (tfcp_req) | ||
733 | fcloop_tfcp_req_get(tfcp_req); | ||
734 | spin_unlock(&inireq->inilock); | ||
652 | 735 | ||
653 | if (!tfcp_req) | 736 | if (!tfcp_req) |
654 | /* abort has already been called */ | 737 | /* abort has already been called */ |
@@ -656,11 +739,31 @@ fcloop_fcp_abort(struct nvme_fc_local_port *localport, | |||
656 | 739 | ||
657 | /* break initiator/target relationship for io */ | 740 | /* break initiator/target relationship for io */ |
658 | spin_lock(&tfcp_req->reqlock); | 741 | spin_lock(&tfcp_req->reqlock); |
659 | inireq->tfcp_req = NULL; | 742 | switch (tfcp_req->inistate) { |
660 | tfcp_req->fcpreq = NULL; | 743 | case INI_IO_START: |
744 | case INI_IO_ACTIVE: | ||
745 | tfcp_req->inistate = INI_IO_ABORTED; | ||
746 | break; | ||
747 | case INI_IO_COMPLETED: | ||
748 | abortio = false; | ||
749 | break; | ||
750 | default: | ||
751 | spin_unlock(&tfcp_req->reqlock); | ||
752 | WARN_ON(1); | ||
753 | return; | ||
754 | } | ||
661 | spin_unlock(&tfcp_req->reqlock); | 755 | spin_unlock(&tfcp_req->reqlock); |
662 | 756 | ||
663 | WARN_ON(!schedule_work(&tfcp_req->abort_rcv_work)); | 757 | if (abortio) |
758 | /* leave the reference while the work item is scheduled */ | ||
759 | WARN_ON(!schedule_work(&tfcp_req->abort_rcv_work)); | ||
760 | else { | ||
761 | /* | ||
762 | * as the io has already had the done callback made, | ||
763 | * nothing more to do. So release the reference taken above | ||
764 | */ | ||
765 | fcloop_tfcp_req_put(tfcp_req); | ||
766 | } | ||
664 | } | 767 | } |
665 | 768 | ||
666 | static void | 769 | static void |