svn commit: r272613 - head/sys/cam/ctl

Alexander Motin mav at FreeBSD.org
Mon Oct 6 12:20:47 UTC 2014


Author: mav
Date: Mon Oct  6 12:20:46 2014
New Revision: 272613
URL: https://svnweb.freebsd.org/changeset/base/272613

Log:
  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.
  
  MFC after:	2 weeks

Modified:
  head/sys/cam/ctl/ctl_frontend_iscsi.c
  head/sys/cam/ctl/ctl_frontend_iscsi.h

Modified: head/sys/cam/ctl/ctl_frontend_iscsi.c
==============================================================================
--- head/sys/cam/ctl/ctl_frontend_iscsi.c	Mon Oct  6 12:08:47 2014	(r272612)
+++ head/sys/cam/ctl/ctl_frontend_iscsi.c	Mon Oct  6 12:20:46 2014	(r272613)
@@ -154,6 +154,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);
@@ -824,7 +826,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",
@@ -834,7 +836,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) {
@@ -842,7 +844,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);
@@ -855,7 +857,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);
@@ -911,8 +913,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);
@@ -2567,6 +2574,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;
 
@@ -2585,9 +2594,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);
@@ -2609,8 +2625,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) {
@@ -2620,6 +2663,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);
@@ -2659,16 +2707,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);
 }
 
@@ -2678,8 +2723,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: head/sys/cam/ctl/ctl_frontend_iscsi.h
==============================================================================
--- head/sys/cam/ctl/ctl_frontend_iscsi.h	Mon Oct  6 12:08:47 2014	(r272612)
+++ head/sys/cam/ctl/ctl_frontend_iscsi.h	Mon Oct  6 12:20:46 2014	(r272613)
@@ -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-all mailing list