svn commit: r273310 - stable/10/sys/cam/ctl
Alexander Motin
mav at FreeBSD.org
Mon Oct 20 07:32:34 UTC 2014
Author: mav
Date: Mon Oct 20 07:32:33 2014
New Revision: 273310
URL: https://svnweb.freebsd.org/changeset/base/273310
Log:
MFC r272613:
Add support for MaxBurstLength and Expected Data transfer Length parameters.
Before this change target could send R2T request for write transfer of any
size, that could violate iSCSI RFC, which allows initiator to limit maximum
R2T size by negotiating MaxBurstLength connection parameter.
Also report an error in case of write underflow, when initiator provides
less data than initiator expects. Previously in such case our target
sent R2T request for non-existing data, violating the RFC, and confusing
some initiators. SCSI specs don't explicitly define how write underflows
should be handled and there are different oppinions, but reporting error
is hopefully better then violating iSCSI RFC with unpredictable results.
Modified:
stable/10/sys/cam/ctl/ctl_frontend_iscsi.c
stable/10/sys/cam/ctl/ctl_frontend_iscsi.h
Directory Properties:
stable/10/ (props changed)
Modified: stable/10/sys/cam/ctl/ctl_frontend_iscsi.c
==============================================================================
--- stable/10/sys/cam/ctl/ctl_frontend_iscsi.c Mon Oct 20 07:31:23 2014 (r273309)
+++ stable/10/sys/cam/ctl/ctl_frontend_iscsi.c Mon Oct 20 07:32:33 2014 (r273310)
@@ -158,6 +158,8 @@ static uint32_t cfiscsi_lun_map(void *ar
static int cfiscsi_ioctl(struct cdev *dev,
u_long cmd, caddr_t addr, int flag, struct thread *td);
static void cfiscsi_datamove(union ctl_io *io);
+static void cfiscsi_datamove_in(union ctl_io *io);
+static void cfiscsi_datamove_out(union ctl_io *io);
static void cfiscsi_done(union ctl_io *io);
static bool cfiscsi_pdu_update_cmdsn(const struct icl_pdu *request);
static void cfiscsi_pdu_handle_nop_out(struct icl_pdu *request);
@@ -828,7 +830,7 @@ cfiscsi_handle_data_segment(struct icl_p
return (true);
}
- if (io->scsiio.ext_data_filled == io->scsiio.kern_data_len &&
+ if (io->scsiio.ext_data_filled == cdw->cdw_r2t_end &&
(bhsdo->bhsdo_flags & BHSDO_FLAGS_F) == 0) {
CFISCSI_SESSION_WARN(cs, "got the final packet without "
"the F flag; flags = 0x%x; dropping connection",
@@ -838,7 +840,7 @@ cfiscsi_handle_data_segment(struct icl_p
return (true);
}
- if (io->scsiio.ext_data_filled != io->scsiio.kern_data_len &&
+ if (io->scsiio.ext_data_filled != cdw->cdw_r2t_end &&
(bhsdo->bhsdo_flags & BHSDO_FLAGS_F) != 0) {
if ((request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) ==
ISCSI_BHS_OPCODE_SCSI_DATA_OUT) {
@@ -846,7 +848,7 @@ cfiscsi_handle_data_segment(struct icl_p
"transmitted size was %zd bytes instead of %d; "
"dropping connection",
(size_t)io->scsiio.ext_data_filled,
- io->scsiio.kern_data_len);
+ cdw->cdw_r2t_end);
ctl_set_data_phase_error(&io->scsiio);
cfiscsi_session_terminate(cs);
return (true);
@@ -859,7 +861,7 @@ cfiscsi_handle_data_segment(struct icl_p
}
}
- if (io->scsiio.ext_data_filled == io->scsiio.kern_data_len) {
+ if (io->scsiio.ext_data_filled == cdw->cdw_r2t_end) {
#if 0
CFISCSI_SESSION_DEBUG(cs, "no longer expecting Data-Out with target "
"transfer tag 0x%x", cdw->cdw_target_transfer_tag);
@@ -915,8 +917,13 @@ cfiscsi_pdu_handle_data_out(struct icl_p
CFISCSI_SESSION_LOCK(cs);
TAILQ_REMOVE(&cs->cs_waiting_for_data_out, cdw, cdw_next);
CFISCSI_SESSION_UNLOCK(cs);
+ done = (io->scsiio.ext_data_filled != cdw->cdw_r2t_end ||
+ io->scsiio.ext_data_filled == io->scsiio.kern_data_len);
uma_zfree(cfiscsi_data_wait_zone, cdw);
- io->scsiio.be_move_done(io);
+ if (done)
+ io->scsiio.be_move_done(io);
+ else
+ cfiscsi_datamove_out(io);
}
icl_pdu_free(request);
@@ -2571,6 +2578,8 @@ cfiscsi_datamove_out(union ctl_io *io)
const struct iscsi_bhs_scsi_command *bhssc;
struct iscsi_bhs_r2t *bhsr2t;
struct cfiscsi_data_wait *cdw;
+ struct ctl_sg_entry ctl_sg_entry, *ctl_sglist;
+ uint32_t expected_len, r2t_off, r2t_len;
uint32_t target_transfer_tag;
bool done;
@@ -2589,9 +2598,16 @@ cfiscsi_datamove_out(union ctl_io *io)
PDU_TOTAL_TRANSFER_LEN(request) = io->scsiio.kern_total_len;
/*
- * We hadn't received anything during this datamove yet.
+ * Report write underflow as error since CTL and backends don't
+ * really support it, and SCSI does not tell how to do it right.
*/
- io->scsiio.ext_data_filled = 0;
+ expected_len = ntohl(bhssc->bhssc_expected_data_transfer_length);
+ if (io->scsiio.kern_rel_offset + io->scsiio.kern_data_len >
+ expected_len) {
+ io->scsiio.io_hdr.port_status = 43;
+ io->scsiio.be_move_done(io);
+ return;
+ }
target_transfer_tag =
atomic_fetchadd_32(&cs->cs_target_transfer_tag, 1);
@@ -2613,8 +2629,35 @@ cfiscsi_datamove_out(union ctl_io *io)
cdw->cdw_ctl_io = io;
cdw->cdw_target_transfer_tag = target_transfer_tag;
cdw->cdw_initiator_task_tag = bhssc->bhssc_initiator_task_tag;
+ cdw->cdw_r2t_end = io->scsiio.kern_data_len;
+
+ /* Set initial data pointer for the CDW respecting ext_data_filled. */
+ if (io->scsiio.kern_sg_entries > 0) {
+ ctl_sglist = (struct ctl_sg_entry *)io->scsiio.kern_data_ptr;
+ } else {
+ ctl_sglist = &ctl_sg_entry;
+ ctl_sglist->addr = io->scsiio.kern_data_ptr;
+ ctl_sglist->len = io->scsiio.kern_data_len;
+ }
+ cdw->cdw_sg_index = 0;
+ cdw->cdw_sg_addr = ctl_sglist[cdw->cdw_sg_index].addr;
+ cdw->cdw_sg_len = ctl_sglist[cdw->cdw_sg_index].len;
+ r2t_off = io->scsiio.ext_data_filled;
+ while (r2t_off > 0) {
+ if (r2t_off >= cdw->cdw_sg_len) {
+ r2t_off -= cdw->cdw_sg_len;
+ cdw->cdw_sg_index++;
+ cdw->cdw_sg_addr = ctl_sglist[cdw->cdw_sg_index].addr;
+ cdw->cdw_sg_len = ctl_sglist[cdw->cdw_sg_index].len;
+ continue;
+ }
+ cdw->cdw_sg_addr += r2t_off;
+ cdw->cdw_sg_len -= r2t_off;
+ r2t_off = 0;
+ }
- if (cs->cs_immediate_data && io->scsiio.kern_rel_offset <
+ if (cs->cs_immediate_data &&
+ io->scsiio.kern_rel_offset + io->scsiio.ext_data_filled <
icl_pdu_data_segment_length(request)) {
done = cfiscsi_handle_data_segment(request, cdw);
if (done) {
@@ -2624,6 +2667,11 @@ cfiscsi_datamove_out(union ctl_io *io)
}
}
+ r2t_off = io->scsiio.kern_rel_offset + io->scsiio.ext_data_filled;
+ r2t_len = MIN(io->scsiio.kern_data_len - io->scsiio.ext_data_filled,
+ cs->cs_max_burst_length);
+ cdw->cdw_r2t_end = io->scsiio.ext_data_filled + r2t_len;
+
CFISCSI_SESSION_LOCK(cs);
TAILQ_INSERT_TAIL(&cs->cs_waiting_for_data_out, cdw, cdw_next);
CFISCSI_SESSION_UNLOCK(cs);
@@ -2663,16 +2711,13 @@ cfiscsi_datamove_out(union ctl_io *io)
* The ext_data_filled is to account for unsolicited
* (immediate) data that might have already arrived.
*/
- bhsr2t->bhsr2t_buffer_offset =
- htonl(io->scsiio.kern_rel_offset + io->scsiio.ext_data_filled);
+ bhsr2t->bhsr2t_buffer_offset = htonl(r2t_off);
/*
* This is the total length (sum of S/G lengths) this call
- * to cfiscsi_datamove() is supposed to handle.
- *
- * XXX: Limit it to MaxBurstLength.
+ * to cfiscsi_datamove() is supposed to handle, limited by
+ * MaxBurstLength.
*/
- bhsr2t->bhsr2t_desired_data_transfer_length =
- htonl(io->scsiio.kern_data_len - io->scsiio.ext_data_filled);
+ bhsr2t->bhsr2t_desired_data_transfer_length = htonl(r2t_len);
cfiscsi_pdu_queue(response);
}
@@ -2682,8 +2727,11 @@ cfiscsi_datamove(union ctl_io *io)
if ((io->io_hdr.flags & CTL_FLAG_DATA_MASK) == CTL_FLAG_DATA_IN)
cfiscsi_datamove_in(io);
- else
+ else {
+ /* We hadn't received anything during this datamove yet. */
+ io->scsiio.ext_data_filled = 0;
cfiscsi_datamove_out(io);
+ }
}
static void
Modified: stable/10/sys/cam/ctl/ctl_frontend_iscsi.h
==============================================================================
--- stable/10/sys/cam/ctl/ctl_frontend_iscsi.h Mon Oct 20 07:31:23 2014 (r273309)
+++ stable/10/sys/cam/ctl/ctl_frontend_iscsi.h Mon Oct 20 07:32:33 2014 (r273310)
@@ -56,6 +56,7 @@ struct cfiscsi_data_wait {
int cdw_sg_index;
char *cdw_sg_addr;
size_t cdw_sg_len;
+ uint32_t cdw_r2t_end;
};
#define CFISCSI_SESSION_STATE_INVALID 0
More information about the svn-src-stable
mailing list