diff options
| author | Mike Christie <michaelc@cs.wisc.edu> | 2008-09-24 12:46:09 -0400 |
|---|---|---|
| committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-10-13 09:28:58 -0400 |
| commit | 1d9edf0270cb5a434d32e95279ce9493581906b3 (patch) | |
| tree | 1b54cdd2c8b18e78a788d7f1f3c17c7b73043c24 | |
| parent | cbdc14459bd7d99d20341ec057b8f4ffab2a7fb6 (diff) | |
[SCSI] libiscsi: fix data corruption when target has to resend data-in packets
iscsi_tcp was updating the exp_statsn (exp_statsn acknowledges
status and tells the target it is ok to let the resources for
a iscsi pdu to be reused) before it got all the data for pdu read
into OS buffers. Data corruption was occuring if something happens
to a packet and the network layer requests a retransmit, and the
initiator has told the target about the udpated exp_statsn ack,
then the target may be sending data from a buffer it has reused
for a new iscsi pdu. This fixes the problem by having the LLD
(iscsi_tcp in this case) just handle the transferring of data, and
has libiscsi handle the processing of status (libiscsi completion
processing is done after LLD data transfers are complete).
Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
| -rw-r--r-- | drivers/scsi/iscsi_tcp.c | 29 | ||||
| -rw-r--r-- | drivers/scsi/libiscsi.c | 41 |
2 files changed, 40 insertions, 30 deletions
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index 2a2f0094570f..e960f00da93a 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c | |||
| @@ -523,22 +523,20 @@ iscsi_tcp_cleanup_task(struct iscsi_conn *conn, struct iscsi_task *task) | |||
| 523 | } | 523 | } |
| 524 | 524 | ||
| 525 | /** | 525 | /** |
| 526 | * iscsi_data_rsp - SCSI Data-In Response processing | 526 | * iscsi_data_in - SCSI Data-In Response processing |
| 527 | * @conn: iscsi connection | 527 | * @conn: iscsi connection |
| 528 | * @task: scsi command task | 528 | * @task: scsi command task |
| 529 | **/ | 529 | **/ |
| 530 | static int | 530 | static int |
| 531 | iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_task *task) | 531 | iscsi_data_in(struct iscsi_conn *conn, struct iscsi_task *task) |
| 532 | { | 532 | { |
| 533 | struct iscsi_tcp_conn *tcp_conn = conn->dd_data; | 533 | struct iscsi_tcp_conn *tcp_conn = conn->dd_data; |
| 534 | struct iscsi_tcp_task *tcp_task = task->dd_data; | 534 | struct iscsi_tcp_task *tcp_task = task->dd_data; |
| 535 | struct iscsi_data_rsp *rhdr = (struct iscsi_data_rsp *)tcp_conn->in.hdr; | 535 | struct iscsi_data_rsp *rhdr = (struct iscsi_data_rsp *)tcp_conn->in.hdr; |
| 536 | struct iscsi_session *session = conn->session; | ||
| 537 | struct scsi_cmnd *sc = task->sc; | ||
| 538 | int datasn = be32_to_cpu(rhdr->datasn); | 536 | int datasn = be32_to_cpu(rhdr->datasn); |
| 539 | unsigned total_in_length = scsi_in(sc)->length; | 537 | unsigned total_in_length = scsi_in(task->sc)->length; |
| 540 | 538 | ||
| 541 | iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr); | 539 | iscsi_update_cmdsn(conn->session, (struct iscsi_nopin*)rhdr); |
| 542 | if (tcp_conn->in.datalen == 0) | 540 | if (tcp_conn->in.datalen == 0) |
| 543 | return 0; | 541 | return 0; |
| 544 | 542 | ||
| @@ -558,23 +556,6 @@ iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_task *task) | |||
| 558 | return ISCSI_ERR_DATA_OFFSET; | 556 | return ISCSI_ERR_DATA_OFFSET; |
| 559 | } | 557 | } |
| 560 | 558 | ||
| 561 | if (rhdr->flags & ISCSI_FLAG_DATA_STATUS) { | ||
| 562 | sc->result = (DID_OK << 16) | rhdr->cmd_status; | ||
| 563 | conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1; | ||
| 564 | if (rhdr->flags & (ISCSI_FLAG_DATA_UNDERFLOW | | ||
| 565 | ISCSI_FLAG_DATA_OVERFLOW)) { | ||
| 566 | int res_count = be32_to_cpu(rhdr->residual_count); | ||
| 567 | |||
| 568 | if (res_count > 0 && | ||
| 569 | (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW || | ||
| 570 | res_count <= total_in_length)) | ||
| 571 | scsi_in(sc)->resid = res_count; | ||
| 572 | else | ||
| 573 | sc->result = (DID_BAD_TARGET << 16) | | ||
| 574 | rhdr->cmd_status; | ||
| 575 | } | ||
| 576 | } | ||
| 577 | |||
| 578 | conn->datain_pdus_cnt++; | 559 | conn->datain_pdus_cnt++; |
| 579 | return 0; | 560 | return 0; |
| 580 | } | 561 | } |
| @@ -774,7 +755,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) | |||
| 774 | if (!task) | 755 | if (!task) |
| 775 | rc = ISCSI_ERR_BAD_ITT; | 756 | rc = ISCSI_ERR_BAD_ITT; |
| 776 | else | 757 | else |
| 777 | rc = iscsi_data_rsp(conn, task); | 758 | rc = iscsi_data_in(conn, task); |
| 778 | if (rc) { | 759 | if (rc) { |
| 779 | spin_unlock(&conn->session->lock); | 760 | spin_unlock(&conn->session->lock); |
| 780 | break; | 761 | break; |
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 0e8f26baca6e..f9539af28f02 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c | |||
| @@ -633,6 +633,40 @@ out: | |||
| 633 | __iscsi_put_task(task); | 633 | __iscsi_put_task(task); |
| 634 | } | 634 | } |
| 635 | 635 | ||
| 636 | /** | ||
| 637 | * iscsi_data_in_rsp - SCSI Data-In Response processing | ||
| 638 | * @conn: iscsi connection | ||
| 639 | * @hdr: iscsi pdu | ||
| 640 | * @task: scsi command task | ||
| 641 | **/ | ||
| 642 | static void | ||
| 643 | iscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, | ||
| 644 | struct iscsi_task *task) | ||
| 645 | { | ||
| 646 | struct iscsi_data_rsp *rhdr = (struct iscsi_data_rsp *)hdr; | ||
| 647 | struct scsi_cmnd *sc = task->sc; | ||
| 648 | |||
| 649 | if (!(rhdr->flags & ISCSI_FLAG_DATA_STATUS)) | ||
| 650 | return; | ||
| 651 | |||
| 652 | sc->result = (DID_OK << 16) | rhdr->cmd_status; | ||
| 653 | conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1; | ||
| 654 | if (rhdr->flags & (ISCSI_FLAG_DATA_UNDERFLOW | | ||
| 655 | ISCSI_FLAG_DATA_OVERFLOW)) { | ||
| 656 | int res_count = be32_to_cpu(rhdr->residual_count); | ||
| 657 | |||
| 658 | if (res_count > 0 && | ||
| 659 | (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW || | ||
| 660 | res_count <= scsi_in(sc)->length)) | ||
| 661 | scsi_in(sc)->resid = res_count; | ||
| 662 | else | ||
| 663 | sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status; | ||
| 664 | } | ||
| 665 | |||
| 666 | conn->scsirsp_pdus_cnt++; | ||
| 667 | __iscsi_put_task(task); | ||
| 668 | } | ||
| 669 | |||
| 636 | static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr) | 670 | static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr) |
| 637 | { | 671 | { |
| 638 | struct iscsi_tm_rsp *tmf = (struct iscsi_tm_rsp *)hdr; | 672 | struct iscsi_tm_rsp *tmf = (struct iscsi_tm_rsp *)hdr; |
| @@ -818,12 +852,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, | |||
| 818 | iscsi_scsi_cmd_rsp(conn, hdr, task, data, datalen); | 852 | iscsi_scsi_cmd_rsp(conn, hdr, task, data, datalen); |
| 819 | break; | 853 | break; |
| 820 | case ISCSI_OP_SCSI_DATA_IN: | 854 | case ISCSI_OP_SCSI_DATA_IN: |
| 821 | if (hdr->flags & ISCSI_FLAG_DATA_STATUS) { | 855 | iscsi_data_in_rsp(conn, hdr, task); |
| 822 | conn->scsirsp_pdus_cnt++; | ||
| 823 | iscsi_update_cmdsn(session, | ||
| 824 | (struct iscsi_nopin*) hdr); | ||
| 825 | __iscsi_put_task(task); | ||
| 826 | } | ||
| 827 | break; | 856 | break; |
| 828 | case ISCSI_OP_LOGOUT_RSP: | 857 | case ISCSI_OP_LOGOUT_RSP: |
| 829 | iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr); | 858 | iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr); |
