Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c07d4444 authored by Boaz Harrosh's avatar Boaz Harrosh Committed by James Bottomley
Browse files

[SCSI] iscsi: bidi support at the generic libiscsi level



- prepare the additional bidi_read rlength header.
- access the right scsi_in() and/or scsi_out() side of things.
  also for resid.
- Handle BIDI underflow overflow from target

Signed-off-by: default avatarBoaz Harrosh <bharrosh@panasas.com>
Reviewed-by: default avatarPete Wyckoff <pw@osc.edu>
Signed-off-by: default avatarMike Christie <michaelc@cs.wisc.edu>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@HansenPartnership.com>
parent 38d1c069
Loading
Loading
Loading
Loading
+70 −15
Original line number Diff line number Diff line
@@ -176,6 +176,31 @@ static int iscsi_prep_ecdb_ahs(struct iscsi_cmd_task *ctask)
	return 0;
}

static int iscsi_prep_bidi_ahs(struct iscsi_cmd_task *ctask)
{
	struct scsi_cmnd *sc = ctask->sc;
	struct iscsi_rlength_ahdr *rlen_ahdr;
	int rc;

	rlen_ahdr = iscsi_next_hdr(ctask);
	rc = iscsi_add_hdr(ctask, sizeof(*rlen_ahdr));
	if (rc)
		return rc;

	rlen_ahdr->ahslength =
		cpu_to_be16(sizeof(rlen_ahdr->read_length) +
						  sizeof(rlen_ahdr->reserved));
	rlen_ahdr->ahstype = ISCSI_AHSTYPE_RLENGTH;
	rlen_ahdr->reserved = 0;
	rlen_ahdr->read_length = cpu_to_be32(scsi_in(sc)->length);

	debug_scsi("bidi-in rlen_ahdr->read_length(%d) "
		   "rlen_ahdr->ahslength(%d)\n",
		   be32_to_cpu(rlen_ahdr->read_length),
		   be16_to_cpu(rlen_ahdr->ahslength));
	return 0;
}

/**
 * iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu
 * @ctask: iscsi cmd task
@@ -200,7 +225,6 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
	hdr->flags = ISCSI_ATTR_SIMPLE;
	int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun);
	hdr->itt = build_itt(ctask->itt, session->age);
	hdr->data_length = cpu_to_be32(scsi_bufflen(sc));
	hdr->cmdsn = cpu_to_be32(session->cmdsn);
	session->cmdsn++;
	hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);
@@ -216,7 +240,15 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
	memcpy(hdr->cdb, sc->cmnd, cmd_len);

	ctask->imm_count = 0;
	if (scsi_bidi_cmnd(sc)) {
		hdr->flags |= ISCSI_FLAG_CMD_READ;
		rc = iscsi_prep_bidi_ahs(ctask);
		if (rc)
			return rc;
	}
	if (sc->sc_data_direction == DMA_TO_DEVICE) {
		unsigned out_len = scsi_out(sc)->length;
		hdr->data_length = cpu_to_be32(out_len);
		hdr->flags |= ISCSI_FLAG_CMD_WRITE;
		/*
		 * Write counters:
@@ -237,19 +269,19 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
		ctask->unsol_datasn = 0;

		if (session->imm_data_en) {
			if (scsi_bufflen(sc) >= session->first_burst)
			if (out_len >= session->first_burst)
				ctask->imm_count = min(session->first_burst,
							conn->max_xmit_dlength);
			else
				ctask->imm_count = min(scsi_bufflen(sc),
				ctask->imm_count = min(out_len,
							conn->max_xmit_dlength);
			hton24(hdr->dlength, ctask->imm_count);
		} else
			zero_data(hdr->dlength);

		if (!session->initial_r2t_en) {
			ctask->unsol_count = min((session->first_burst),
				(scsi_bufflen(sc))) - ctask->imm_count;
			ctask->unsol_count = min(session->first_burst, out_len)
							     - ctask->imm_count;
			ctask->unsol_offset = ctask->imm_count;
		}

@@ -259,6 +291,7 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
	} else {
		hdr->flags |= ISCSI_FLAG_CMD_FINAL;
		zero_data(hdr->dlength);
		hdr->data_length = cpu_to_be32(scsi_in(sc)->length);

		if (sc->sc_data_direction == DMA_FROM_DEVICE)
			hdr->flags |= ISCSI_FLAG_CMD_READ;
@@ -277,10 +310,12 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
		return EIO;

	conn->scsicmd_pdus_cnt++;
	debug_scsi("iscsi prep [%s cid %d sc %p cdb 0x%x itt 0x%x len %d "
		"cmdsn %d win %d]\n",
	debug_scsi("iscsi prep [%s cid %d sc %p cdb 0x%x itt 0x%x "
		"len %d bidi_len %d cmdsn %d win %d]\n",
		scsi_bidi_cmnd(sc) ? "bidirectional" :
		     sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read",
		conn->id, sc, sc->cmnd[0], ctask->itt, scsi_bufflen(sc),
		conn->id, sc, sc->cmnd[0], ctask->itt,
		scsi_bufflen(sc), scsi_bidi_cmnd(sc) ? scsi_in(sc)->length : 0,
		session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1);
	return 0;
}
@@ -343,7 +378,12 @@ static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
		conn->session->tt->cleanup_cmd_task(conn, ctask);

	sc->result = err;
	if (!scsi_bidi_cmnd(sc))
		scsi_set_resid(sc, scsi_bufflen(sc));
	else {
		scsi_out(sc)->resid = scsi_out(sc)->length;
		scsi_in(sc)->resid = scsi_in(sc)->length;
	}
	if (conn->ctask == ctask)
		conn->ctask = NULL;
	/* release ref from queuecommand */
@@ -478,6 +518,18 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
			   min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE));
	}

	if (rhdr->flags & (ISCSI_FLAG_CMD_BIDI_UNDERFLOW |
			   ISCSI_FLAG_CMD_BIDI_OVERFLOW)) {
		int res_count = be32_to_cpu(rhdr->bi_residual_count);

		if (scsi_bidi_cmnd(sc) && res_count > 0 &&
				(rhdr->flags & ISCSI_FLAG_CMD_BIDI_OVERFLOW ||
				 res_count <= scsi_in(sc)->length))
			scsi_in(sc)->resid = res_count;
		else
			sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
	}

	if (rhdr->flags & (ISCSI_FLAG_CMD_UNDERFLOW |
	                   ISCSI_FLAG_CMD_OVERFLOW)) {
		int res_count = be32_to_cpu(rhdr->residual_count);
@@ -485,13 +537,11 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
		if (res_count > 0 &&
		    (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW ||
		     res_count <= scsi_bufflen(sc)))
			/* write side for bidi or uni-io set_resid */
			scsi_set_resid(sc, res_count);
		else
			sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
	} else if (rhdr->flags & (ISCSI_FLAG_CMD_BIDI_UNDERFLOW |
	                          ISCSI_FLAG_CMD_BIDI_OVERFLOW))
		sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;

	}
out:
	debug_scsi("done [sc %lx res %d itt 0x%x]\n",
		   (long)sc, sc->result, ctask->itt);
@@ -1147,7 +1197,12 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
fault:
	spin_unlock(&session->lock);
	debug_scsi("iscsi: cmd 0x%x is not queued (%d)\n", sc->cmnd[0], reason);
	if (!scsi_bidi_cmnd(sc))
		scsi_set_resid(sc, scsi_bufflen(sc));
	else {
		scsi_out(sc)->resid = scsi_out(sc)->length;
		scsi_in(sc)->resid = scsi_in(sc)->length;
	}
	sc->scsi_done(sc);
	spin_lock(host->host_lock);
	return 0;