mirror of
https://git.FreeBSD.org/src.git
synced 2025-01-19 15:33:56 +00:00
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
This commit is contained in:
parent
3615981425
commit
04f10b6c96
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=272613
@ -154,6 +154,8 @@ static uint32_t cfiscsi_lun_map(void *arg, uint32_t lun);
|
||||
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_pdu *request, struct cfiscsi_data_wait *c
|
||||
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_pdu *request, struct cfiscsi_data_wait *c
|
||||
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_pdu *request, struct cfiscsi_data_wait *c
|
||||
"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_pdu *request, struct cfiscsi_data_wait *c
|
||||
}
|
||||
}
|
||||
|
||||
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_pdu *request)
|
||||
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;
|
||||
|
||||
if (cs->cs_immediate_data && io->scsiio.kern_rel_offset <
|
||||
/* 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 + 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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user