diff options
Diffstat (limited to 'drivers/scsi/libiscsi.c')
-rw-r--r-- | drivers/scsi/libiscsi.c | 106 |
1 files changed, 87 insertions, 19 deletions
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index c68cb53a984b..1d7a8b7e8a75 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c | |||
@@ -857,27 +857,102 @@ static void iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr) | |||
857 | } | 857 | } |
858 | } | 858 | } |
859 | 859 | ||
860 | static int iscsi_nop_out_rsp(struct iscsi_task *task, | ||
861 | struct iscsi_nopin *nop, char *data, int datalen) | ||
862 | { | ||
863 | struct iscsi_conn *conn = task->conn; | ||
864 | int rc = 0; | ||
865 | |||
866 | if (conn->ping_task != task) { | ||
867 | /* | ||
868 | * If this is not in response to one of our | ||
869 | * nops then it must be from userspace. | ||
870 | */ | ||
871 | if (iscsi_recv_pdu(conn->cls_conn, (struct iscsi_hdr *)nop, | ||
872 | data, datalen)) | ||
873 | rc = ISCSI_ERR_CONN_FAILED; | ||
874 | } else | ||
875 | mod_timer(&conn->transport_timer, jiffies + conn->recv_timeout); | ||
876 | iscsi_complete_task(task, ISCSI_TASK_COMPLETED); | ||
877 | return rc; | ||
878 | } | ||
879 | |||
860 | static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr, | 880 | static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr, |
861 | char *data, int datalen) | 881 | char *data, int datalen) |
862 | { | 882 | { |
863 | struct iscsi_reject *reject = (struct iscsi_reject *)hdr; | 883 | struct iscsi_reject *reject = (struct iscsi_reject *)hdr; |
864 | struct iscsi_hdr rejected_pdu; | 884 | struct iscsi_hdr rejected_pdu; |
885 | int opcode, rc = 0; | ||
865 | 886 | ||
866 | conn->exp_statsn = be32_to_cpu(reject->statsn) + 1; | 887 | conn->exp_statsn = be32_to_cpu(reject->statsn) + 1; |
867 | 888 | ||
868 | if (reject->reason == ISCSI_REASON_DATA_DIGEST_ERROR) { | 889 | if (ntoh24(reject->dlength) > datalen || |
869 | if (ntoh24(reject->dlength) > datalen) | 890 | ntoh24(reject->dlength) < sizeof(struct iscsi_hdr)) { |
870 | return ISCSI_ERR_PROTO; | 891 | iscsi_conn_printk(KERN_ERR, conn, "Cannot handle rejected " |
892 | "pdu. Invalid data length (pdu dlength " | ||
893 | "%u, datalen %d\n", ntoh24(reject->dlength), | ||
894 | datalen); | ||
895 | return ISCSI_ERR_PROTO; | ||
896 | } | ||
897 | memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr)); | ||
898 | opcode = rejected_pdu.opcode & ISCSI_OPCODE_MASK; | ||
871 | 899 | ||
872 | if (ntoh24(reject->dlength) >= sizeof(struct iscsi_hdr)) { | 900 | switch (reject->reason) { |
873 | memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr)); | 901 | case ISCSI_REASON_DATA_DIGEST_ERROR: |
874 | iscsi_conn_printk(KERN_ERR, conn, | 902 | iscsi_conn_printk(KERN_ERR, conn, |
875 | "pdu (op 0x%x) rejected " | 903 | "pdu (op 0x%x itt 0x%x) rejected " |
876 | "due to DataDigest error.\n", | 904 | "due to DataDigest error.\n", |
877 | rejected_pdu.opcode); | 905 | rejected_pdu.itt, opcode); |
906 | break; | ||
907 | case ISCSI_REASON_IMM_CMD_REJECT: | ||
908 | iscsi_conn_printk(KERN_ERR, conn, | ||
909 | "pdu (op 0x%x itt 0x%x) rejected. Too many " | ||
910 | "immediate commands.\n", | ||
911 | rejected_pdu.itt, opcode); | ||
912 | /* | ||
913 | * We only send one TMF at a time so if the target could not | ||
914 | * handle it, then it should get fixed (RFC mandates that | ||
915 | * a target can handle one immediate TMF per conn). | ||
916 | * | ||
917 | * For nops-outs, we could have sent more than one if | ||
918 | * the target is sending us lots of nop-ins | ||
919 | */ | ||
920 | if (opcode != ISCSI_OP_NOOP_OUT) | ||
921 | return 0; | ||
922 | |||
923 | if (rejected_pdu.itt == cpu_to_be32(ISCSI_RESERVED_TAG)) | ||
924 | /* | ||
925 | * nop-out in response to target's nop-out rejected. | ||
926 | * Just resend. | ||
927 | */ | ||
928 | iscsi_send_nopout(conn, | ||
929 | (struct iscsi_nopin*)&rejected_pdu); | ||
930 | else { | ||
931 | struct iscsi_task *task; | ||
932 | /* | ||
933 | * Our nop as ping got dropped. We know the target | ||
934 | * and transport are ok so just clean up | ||
935 | */ | ||
936 | task = iscsi_itt_to_task(conn, rejected_pdu.itt); | ||
937 | if (!task) { | ||
938 | iscsi_conn_printk(KERN_ERR, conn, | ||
939 | "Invalid pdu reject. Could " | ||
940 | "not lookup rejected task.\n"); | ||
941 | rc = ISCSI_ERR_BAD_ITT; | ||
942 | } else | ||
943 | rc = iscsi_nop_out_rsp(task, | ||
944 | (struct iscsi_nopin*)&rejected_pdu, | ||
945 | NULL, 0); | ||
878 | } | 946 | } |
947 | break; | ||
948 | default: | ||
949 | iscsi_conn_printk(KERN_ERR, conn, | ||
950 | "pdu (op 0x%x itt 0x%x) rejected. Reason " | ||
951 | "code 0x%x\n", rejected_pdu.itt, | ||
952 | rejected_pdu.opcode, reject->reason); | ||
953 | break; | ||
879 | } | 954 | } |
880 | return 0; | 955 | return rc; |
881 | } | 956 | } |
882 | 957 | ||
883 | /** | 958 | /** |
@@ -1038,15 +1113,8 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, | |||
1038 | } | 1113 | } |
1039 | conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; | 1114 | conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; |
1040 | 1115 | ||
1041 | if (conn->ping_task != task) | 1116 | rc = iscsi_nop_out_rsp(task, (struct iscsi_nopin*)hdr, |
1042 | /* | 1117 | data, datalen); |
1043 | * If this is not in response to one of our | ||
1044 | * nops then it must be from userspace. | ||
1045 | */ | ||
1046 | goto recv_pdu; | ||
1047 | |||
1048 | mod_timer(&conn->transport_timer, jiffies + conn->recv_timeout); | ||
1049 | iscsi_complete_task(task, ISCSI_TASK_COMPLETED); | ||
1050 | break; | 1118 | break; |
1051 | default: | 1119 | default: |
1052 | rc = ISCSI_ERR_BAD_OPCODE; | 1120 | rc = ISCSI_ERR_BAD_OPCODE; |