diff options
author | Mike Christie <michaelc@cs.wisc.edu> | 2008-05-21 16:54:18 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-07-12 09:22:22 -0400 |
commit | 913e5bf435617aa529919a4f7567f849f9f35f9f (patch) | |
tree | db7357d78d7d6f20358d61bf88a8137ad044a5a5 /drivers/scsi/libiscsi.c | |
parent | 3cf7b233ffc45d4fc381221f74d24f10e692c4ea (diff) |
[SCSI] libiscsi, iser, tcp: remove recv_lock
The recv lock was defined so the iscsi layer could block
the recv path from processing IO during recovery. It
turns out iser just set a lock to that pointer which was pointless.
We now disconnect the transport connection before doing recovery
so we do not need the recv lock. For iscsi_tcp we still stop
the recv path incase older tools are being used.
This patch also has iscsi_itt_to_ctask user grab the session lock
and has the caller access the task with the lock or get a ref
to it in case the target is broken and sends a tmf success response
then sends data or a response for the command that was supposed to
be affected bty the tmf.
Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers/scsi/libiscsi.c')
-rw-r--r-- | drivers/scsi/libiscsi.c | 152 |
1 files changed, 86 insertions, 66 deletions
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index c723e60f02b0..9c267b440444 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c | |||
@@ -362,10 +362,11 @@ static void iscsi_complete_command(struct iscsi_task *task) | |||
362 | } | 362 | } |
363 | } | 363 | } |
364 | 364 | ||
365 | static void __iscsi_get_task(struct iscsi_task *task) | 365 | void __iscsi_get_task(struct iscsi_task *task) |
366 | { | 366 | { |
367 | atomic_inc(&task->refcount); | 367 | atomic_inc(&task->refcount); |
368 | } | 368 | } |
369 | EXPORT_SYMBOL_GPL(__iscsi_get_task); | ||
369 | 370 | ||
370 | static void __iscsi_put_task(struct iscsi_task *task) | 371 | static void __iscsi_put_task(struct iscsi_task *task) |
371 | { | 372 | { |
@@ -403,9 +404,13 @@ static void fail_command(struct iscsi_conn *conn, struct iscsi_task *task, | |||
403 | conn->session->queued_cmdsn--; | 404 | conn->session->queued_cmdsn--; |
404 | else | 405 | else |
405 | conn->session->tt->cleanup_task(conn, task); | 406 | conn->session->tt->cleanup_task(conn, task); |
407 | /* | ||
408 | * Check if cleanup_task dropped the lock and the command completed, | ||
409 | */ | ||
410 | if (!task->sc) | ||
411 | return; | ||
406 | 412 | ||
407 | sc->result = err; | 413 | sc->result = err; |
408 | |||
409 | if (!scsi_bidi_cmnd(sc)) | 414 | if (!scsi_bidi_cmnd(sc)) |
410 | scsi_set_resid(sc, scsi_bufflen(sc)); | 415 | scsi_set_resid(sc, scsi_bufflen(sc)); |
411 | else { | 416 | else { |
@@ -697,6 +702,31 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr, | |||
697 | } | 702 | } |
698 | 703 | ||
699 | /** | 704 | /** |
705 | * iscsi_itt_to_task - look up task by itt | ||
706 | * @conn: iscsi connection | ||
707 | * @itt: itt | ||
708 | * | ||
709 | * This should be used for mgmt tasks like login and nops, or if | ||
710 | * the LDD's itt space does not include the session age. | ||
711 | * | ||
712 | * The session lock must be held. | ||
713 | */ | ||
714 | static struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *conn, itt_t itt) | ||
715 | { | ||
716 | struct iscsi_session *session = conn->session; | ||
717 | uint32_t i; | ||
718 | |||
719 | if (itt == RESERVED_ITT) | ||
720 | return NULL; | ||
721 | |||
722 | i = get_itt(itt); | ||
723 | if (i >= session->cmds_max) | ||
724 | return NULL; | ||
725 | |||
726 | return session->cmds[i]; | ||
727 | } | ||
728 | |||
729 | /** | ||
700 | * __iscsi_complete_pdu - complete pdu | 730 | * __iscsi_complete_pdu - complete pdu |
701 | * @conn: iscsi conn | 731 | * @conn: iscsi conn |
702 | * @hdr: iscsi header | 732 | * @hdr: iscsi header |
@@ -707,8 +737,8 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr, | |||
707 | * queuecommand or send generic. session lock must be held and verify | 737 | * queuecommand or send generic. session lock must be held and verify |
708 | * itt must have been called. | 738 | * itt must have been called. |
709 | */ | 739 | */ |
710 | static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, | 740 | int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, |
711 | char *data, int datalen) | 741 | char *data, int datalen) |
712 | { | 742 | { |
713 | struct iscsi_session *session = conn->session; | 743 | struct iscsi_session *session = conn->session; |
714 | int opcode = hdr->opcode & ISCSI_OPCODE_MASK, rc = 0; | 744 | int opcode = hdr->opcode & ISCSI_OPCODE_MASK, rc = 0; |
@@ -758,22 +788,36 @@ static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, | |||
758 | goto out; | 788 | goto out; |
759 | } | 789 | } |
760 | 790 | ||
761 | task = session->cmds[itt]; | ||
762 | switch(opcode) { | 791 | switch(opcode) { |
763 | case ISCSI_OP_SCSI_CMD_RSP: | 792 | case ISCSI_OP_SCSI_CMD_RSP: |
764 | if (!task->sc) { | 793 | case ISCSI_OP_SCSI_DATA_IN: |
765 | rc = ISCSI_ERR_NO_SCSI_CMD; | 794 | task = iscsi_itt_to_ctask(conn, hdr->itt); |
766 | break; | 795 | if (!task) |
767 | } | 796 | return ISCSI_ERR_BAD_ITT; |
768 | BUG_ON((void*)task != task->sc->SCp.ptr); | 797 | break; |
798 | case ISCSI_OP_R2T: | ||
799 | /* | ||
800 | * LLD handles R2Ts if they need to. | ||
801 | */ | ||
802 | return 0; | ||
803 | case ISCSI_OP_LOGOUT_RSP: | ||
804 | case ISCSI_OP_LOGIN_RSP: | ||
805 | case ISCSI_OP_TEXT_RSP: | ||
806 | case ISCSI_OP_SCSI_TMFUNC_RSP: | ||
807 | case ISCSI_OP_NOOP_IN: | ||
808 | task = iscsi_itt_to_task(conn, hdr->itt); | ||
809 | if (!task) | ||
810 | return ISCSI_ERR_BAD_ITT; | ||
811 | break; | ||
812 | default: | ||
813 | return ISCSI_ERR_BAD_OPCODE; | ||
814 | } | ||
815 | |||
816 | switch(opcode) { | ||
817 | case ISCSI_OP_SCSI_CMD_RSP: | ||
769 | iscsi_scsi_cmd_rsp(conn, hdr, task, data, datalen); | 818 | iscsi_scsi_cmd_rsp(conn, hdr, task, data, datalen); |
770 | break; | 819 | break; |
771 | case ISCSI_OP_SCSI_DATA_IN: | 820 | case ISCSI_OP_SCSI_DATA_IN: |
772 | if (!task->sc) { | ||
773 | rc = ISCSI_ERR_NO_SCSI_CMD; | ||
774 | break; | ||
775 | } | ||
776 | BUG_ON((void*)task != task->sc->SCp.ptr); | ||
777 | if (hdr->flags & ISCSI_FLAG_DATA_STATUS) { | 821 | if (hdr->flags & ISCSI_FLAG_DATA_STATUS) { |
778 | conn->scsirsp_pdus_cnt++; | 822 | conn->scsirsp_pdus_cnt++; |
779 | iscsi_update_cmdsn(session, | 823 | iscsi_update_cmdsn(session, |
@@ -781,9 +825,6 @@ static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, | |||
781 | __iscsi_put_task(task); | 825 | __iscsi_put_task(task); |
782 | } | 826 | } |
783 | break; | 827 | break; |
784 | case ISCSI_OP_R2T: | ||
785 | /* LLD handles this for now */ | ||
786 | break; | ||
787 | case ISCSI_OP_LOGOUT_RSP: | 828 | case ISCSI_OP_LOGOUT_RSP: |
788 | iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr); | 829 | iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr); |
789 | if (datalen) { | 830 | if (datalen) { |
@@ -841,6 +882,7 @@ recv_pdu: | |||
841 | __iscsi_put_task(task); | 882 | __iscsi_put_task(task); |
842 | return rc; | 883 | return rc; |
843 | } | 884 | } |
885 | EXPORT_SYMBOL_GPL(__iscsi_complete_pdu); | ||
844 | 886 | ||
845 | int iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, | 887 | int iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, |
846 | char *data, int datalen) | 888 | char *data, int datalen) |
@@ -857,7 +899,6 @@ EXPORT_SYMBOL_GPL(iscsi_complete_pdu); | |||
857 | int iscsi_verify_itt(struct iscsi_conn *conn, itt_t itt) | 899 | int iscsi_verify_itt(struct iscsi_conn *conn, itt_t itt) |
858 | { | 900 | { |
859 | struct iscsi_session *session = conn->session; | 901 | struct iscsi_session *session = conn->session; |
860 | struct iscsi_task *task; | ||
861 | uint32_t i; | 902 | uint32_t i; |
862 | 903 | ||
863 | if (itt == RESERVED_ITT) | 904 | if (itt == RESERVED_ITT) |
@@ -867,8 +908,7 @@ int iscsi_verify_itt(struct iscsi_conn *conn, itt_t itt) | |||
867 | (session->age << ISCSI_AGE_SHIFT)) { | 908 | (session->age << ISCSI_AGE_SHIFT)) { |
868 | iscsi_conn_printk(KERN_ERR, conn, | 909 | iscsi_conn_printk(KERN_ERR, conn, |
869 | "received itt %x expected session age (%x)\n", | 910 | "received itt %x expected session age (%x)\n", |
870 | (__force u32)itt, | 911 | (__force u32)itt, session->age); |
871 | session->age & ISCSI_AGE_MASK); | ||
872 | return ISCSI_ERR_BAD_ITT; | 912 | return ISCSI_ERR_BAD_ITT; |
873 | } | 913 | } |
874 | 914 | ||
@@ -879,42 +919,36 @@ int iscsi_verify_itt(struct iscsi_conn *conn, itt_t itt) | |||
879 | "%u.\n", i, session->cmds_max); | 919 | "%u.\n", i, session->cmds_max); |
880 | return ISCSI_ERR_BAD_ITT; | 920 | return ISCSI_ERR_BAD_ITT; |
881 | } | 921 | } |
882 | |||
883 | task = session->cmds[i]; | ||
884 | if (task->sc && task->sc->SCp.phase != session->age) { | ||
885 | iscsi_conn_printk(KERN_ERR, conn, | ||
886 | "iscsi: task's session age %d, " | ||
887 | "expected %d\n", task->sc->SCp.phase, | ||
888 | session->age); | ||
889 | return ISCSI_ERR_SESSION_FAILED; | ||
890 | } | ||
891 | return 0; | 922 | return 0; |
892 | } | 923 | } |
893 | EXPORT_SYMBOL_GPL(iscsi_verify_itt); | 924 | EXPORT_SYMBOL_GPL(iscsi_verify_itt); |
894 | 925 | ||
895 | struct iscsi_task * | 926 | /** |
896 | iscsi_itt_to_ctask(struct iscsi_conn *conn, itt_t itt) | 927 | * iscsi_itt_to_ctask - look up ctask by itt |
928 | * @conn: iscsi connection | ||
929 | * @itt: itt | ||
930 | * | ||
931 | * This should be used for cmd tasks. | ||
932 | * | ||
933 | * The session lock must be held. | ||
934 | */ | ||
935 | struct iscsi_task *iscsi_itt_to_ctask(struct iscsi_conn *conn, itt_t itt) | ||
897 | { | 936 | { |
898 | struct iscsi_session *session = conn->session; | ||
899 | struct iscsi_task *task; | 937 | struct iscsi_task *task; |
900 | uint32_t i; | ||
901 | 938 | ||
902 | if (iscsi_verify_itt(conn, itt)) | 939 | if (iscsi_verify_itt(conn, itt)) |
903 | return NULL; | 940 | return NULL; |
904 | 941 | ||
905 | if (itt == RESERVED_ITT) | 942 | task = iscsi_itt_to_task(conn, itt); |
943 | if (!task || !task->sc) | ||
906 | return NULL; | 944 | return NULL; |
907 | 945 | ||
908 | i = get_itt(itt); | 946 | if (task->sc->SCp.phase != conn->session->age) { |
909 | if (i >= session->cmds_max) | 947 | iscsi_session_printk(KERN_ERR, conn->session, |
910 | return NULL; | 948 | "task's session age %d, expected %d\n", |
911 | 949 | task->sc->SCp.phase, conn->session->age); | |
912 | task = session->cmds[i]; | ||
913 | if (!task->sc) | ||
914 | return NULL; | ||
915 | |||
916 | if (task->sc->SCp.phase != session->age) | ||
917 | return NULL; | 950 | return NULL; |
951 | } | ||
918 | 952 | ||
919 | return task; | 953 | return task; |
920 | } | 954 | } |
@@ -1620,16 +1654,20 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) | |||
1620 | switch (conn->tmf_state) { | 1654 | switch (conn->tmf_state) { |
1621 | case TMF_SUCCESS: | 1655 | case TMF_SUCCESS: |
1622 | spin_unlock_bh(&session->lock); | 1656 | spin_unlock_bh(&session->lock); |
1657 | /* | ||
1658 | * stop tx side incase the target had sent a abort rsp but | ||
1659 | * the initiator was still writing out data. | ||
1660 | */ | ||
1623 | iscsi_suspend_tx(conn); | 1661 | iscsi_suspend_tx(conn); |
1624 | /* | 1662 | /* |
1625 | * clean up task if aborted. grab the recv lock as a writer | 1663 | * we do not stop the recv side because targets have been |
1664 | * good and have never sent us a successful tmf response | ||
1665 | * then sent more data for the cmd. | ||
1626 | */ | 1666 | */ |
1627 | write_lock_bh(conn->recv_lock); | ||
1628 | spin_lock(&session->lock); | 1667 | spin_lock(&session->lock); |
1629 | fail_command(conn, task, DID_ABORT << 16); | 1668 | fail_command(conn, task, DID_ABORT << 16); |
1630 | conn->tmf_state = TMF_INITIAL; | 1669 | conn->tmf_state = TMF_INITIAL; |
1631 | spin_unlock(&session->lock); | 1670 | spin_unlock(&session->lock); |
1632 | write_unlock_bh(conn->recv_lock); | ||
1633 | iscsi_start_tx(conn); | 1671 | iscsi_start_tx(conn); |
1634 | goto success_unlocked; | 1672 | goto success_unlocked; |
1635 | case TMF_TIMEDOUT: | 1673 | case TMF_TIMEDOUT: |
@@ -1729,13 +1767,11 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc) | |||
1729 | spin_unlock_bh(&session->lock); | 1767 | spin_unlock_bh(&session->lock); |
1730 | 1768 | ||
1731 | iscsi_suspend_tx(conn); | 1769 | iscsi_suspend_tx(conn); |
1732 | /* need to grab the recv lock then session lock */ | 1770 | |
1733 | write_lock_bh(conn->recv_lock); | ||
1734 | spin_lock(&session->lock); | 1771 | spin_lock(&session->lock); |
1735 | fail_all_commands(conn, sc->device->lun, DID_ERROR); | 1772 | fail_all_commands(conn, sc->device->lun, DID_ERROR); |
1736 | conn->tmf_state = TMF_INITIAL; | 1773 | conn->tmf_state = TMF_INITIAL; |
1737 | spin_unlock(&session->lock); | 1774 | spin_unlock(&session->lock); |
1738 | write_unlock_bh(conn->recv_lock); | ||
1739 | 1775 | ||
1740 | iscsi_start_tx(conn); | 1776 | iscsi_start_tx(conn); |
1741 | goto done; | 1777 | goto done; |
@@ -2257,17 +2293,6 @@ static void iscsi_start_session_recovery(struct iscsi_session *session, | |||
2257 | } | 2293 | } |
2258 | 2294 | ||
2259 | /* | 2295 | /* |
2260 | * The LLD either freed/unset the lock on us, or userspace called | ||
2261 | * stop but did not create a proper connection (connection was never | ||
2262 | * bound or it was unbound then stop was called). | ||
2263 | */ | ||
2264 | if (!conn->recv_lock) { | ||
2265 | spin_unlock_bh(&session->lock); | ||
2266 | mutex_unlock(&session->eh_mutex); | ||
2267 | return; | ||
2268 | } | ||
2269 | |||
2270 | /* | ||
2271 | * When this is called for the in_login state, we only want to clean | 2296 | * When this is called for the in_login state, we only want to clean |
2272 | * up the login task and connection. We do not need to block and set | 2297 | * up the login task and connection. We do not need to block and set |
2273 | * the recovery state again | 2298 | * the recovery state again |
@@ -2283,11 +2308,6 @@ static void iscsi_start_session_recovery(struct iscsi_session *session, | |||
2283 | spin_unlock_bh(&session->lock); | 2308 | spin_unlock_bh(&session->lock); |
2284 | 2309 | ||
2285 | iscsi_suspend_tx(conn); | 2310 | iscsi_suspend_tx(conn); |
2286 | |||
2287 | write_lock_bh(conn->recv_lock); | ||
2288 | set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx); | ||
2289 | write_unlock_bh(conn->recv_lock); | ||
2290 | |||
2291 | /* | 2311 | /* |
2292 | * for connection level recovery we should not calculate | 2312 | * for connection level recovery we should not calculate |
2293 | * header digest. conn->hdr_size used for optimization | 2313 | * header digest. conn->hdr_size used for optimization |