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

Commit ef3eb71d authored by Felix Beck's avatar Felix Beck Committed by James Bottomley
Browse files

[SCSI] zfcp: Introduce experimental support for DIF/DIX



Introduce support for DIF/DIX in zfcp: Report the capabilities for the
Scsi_host, map the protection data when issuing I/O requests and
handle the new error codes. Also add the fsf data_direction field to
the hba trace, it is useful information for debugging in that area.
This is an EXPERIMENTAL feature for now.

Signed-off-by: default avatarFelix Beck <felix.beck@de.ibm.com>
Signed-off-by: default avatarChristof Schmitt <christof.schmitt@de.ibm.com>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@suse.de>
parent dcc18f48
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -155,6 +155,8 @@ void _zfcp_dbf_hba_fsf_response(const char *tag2, int level,
		if (scsi_cmnd) {
			response->u.fcp.cmnd = (unsigned long)scsi_cmnd;
			response->u.fcp.serial = scsi_cmnd->serial_number;
			response->u.fcp.data_dir =
				qtcb->bottom.io.data_direction;
		}
		break;

@@ -326,6 +328,7 @@ static void zfcp_dbf_hba_view_response(char **p,
	case FSF_QTCB_FCP_CMND:
		if (r->fsf_req_status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT)
			break;
		zfcp_dbf_out(p, "data_direction", "0x%04x", r->u.fcp.data_dir);
		zfcp_dbf_out(p, "scsi_cmnd", "0x%0Lx", r->u.fcp.cmnd);
		zfcp_dbf_out(p, "scsi_serial", "0x%016Lx", r->u.fcp.serial);
		*p += sprintf(*p, "\n");
+1 −0
Original line number Diff line number Diff line
@@ -111,6 +111,7 @@ struct zfcp_dbf_hba_record_response {
		struct {
			u64 cmnd;
			u64 serial;
			u32 data_dir;
		} fcp;
		struct {
			u64 wwpn;
+2 −0
Original line number Diff line number Diff line
@@ -164,6 +164,8 @@ extern void zfcp_scsi_schedule_rport_block(struct zfcp_port *);
extern void zfcp_scsi_schedule_rports_block(struct zfcp_adapter *);
extern void zfcp_scsi_scan(struct zfcp_unit *);
extern void zfcp_scsi_scan_work(struct work_struct *);
extern void zfcp_scsi_set_prot(struct zfcp_adapter *);
extern void zfcp_scsi_dif_sense_error(struct scsi_cmnd *, int);

/* zfcp_sysfs.c */
extern struct attribute_group zfcp_sysfs_unit_attrs;
+3 −0
Original line number Diff line number Diff line
@@ -220,6 +220,9 @@ void zfcp_fc_scsi_to_fcp(struct fcp_cmnd *fcp, struct scsi_cmnd *scsi)
	memcpy(fcp->fc_cdb, scsi->cmnd, scsi->cmd_len);

	fcp->fc_dl = scsi_bufflen(scsi);

	if (scsi_get_prot_type(scsi) == SCSI_PROT_DIF_TYPE1)
		fcp->fc_dl += fcp->fc_dl / scsi->device->sector_size * 8;
}

/**
+86 −23
Original line number Diff line number Diff line
@@ -526,6 +526,8 @@ static int zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *req)
		return -EIO;
	}

	zfcp_scsi_set_prot(adapter);

	return 0;
}

@@ -988,6 +990,7 @@ static int zfcp_fsf_setup_ct_els_sbals(struct zfcp_fsf_req *req,
	bytes = zfcp_qdio_sbals_from_sg(adapter->qdio, &req->qdio_req, sg_req);
	if (bytes <= 0)
		return -EIO;
	zfcp_qdio_set_sbale_last(adapter->qdio, &req->qdio_req);
	req->qtcb->bottom.support.req_buf_length = bytes;
	zfcp_qdio_skip_to_last_sbale(&req->qdio_req);

@@ -996,6 +999,7 @@ static int zfcp_fsf_setup_ct_els_sbals(struct zfcp_fsf_req *req,
	req->qtcb->bottom.support.resp_buf_length = bytes;
	if (bytes <= 0)
		return -EIO;
	zfcp_qdio_set_sbale_last(adapter->qdio, &req->qdio_req);

	return 0;
}
@@ -2038,9 +2042,13 @@ static void zfcp_fsf_req_trace(struct zfcp_fsf_req *req, struct scsi_cmnd *scsi)
		blktrc.fabric_lat = lat_in->fabric_lat * ticks;

		switch (req->qtcb->bottom.io.data_direction) {
		case FSF_DATADIR_DIF_READ_STRIP:
		case FSF_DATADIR_DIF_READ_CONVERT:
		case FSF_DATADIR_READ:
			lat = &unit->latencies.read;
			break;
		case FSF_DATADIR_DIF_WRITE_INSERT:
		case FSF_DATADIR_DIF_WRITE_CONVERT:
		case FSF_DATADIR_WRITE:
			lat = &unit->latencies.write;
			break;
@@ -2081,6 +2089,21 @@ static void zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *req)
		goto skip_fsfstatus;
	}

	switch (req->qtcb->header.fsf_status) {
	case FSF_INCONSISTENT_PROT_DATA:
	case FSF_INVALID_PROT_PARM:
		set_host_byte(scpnt, DID_ERROR);
		goto skip_fsfstatus;
	case FSF_BLOCK_GUARD_CHECK_FAILURE:
		zfcp_scsi_dif_sense_error(scpnt, 0x1);
		goto skip_fsfstatus;
	case FSF_APP_TAG_CHECK_FAILURE:
		zfcp_scsi_dif_sense_error(scpnt, 0x2);
		goto skip_fsfstatus;
	case FSF_REF_TAG_CHECK_FAILURE:
		zfcp_scsi_dif_sense_error(scpnt, 0x3);
		goto skip_fsfstatus;
	}
	fcp_rsp = (struct fcp_resp_with_ext *) &req->qtcb->bottom.io.fcp_rsp;
	zfcp_fc_eval_fcp_rsp(fcp_rsp, scpnt);

@@ -2190,6 +2213,44 @@ skip_fsfstatus:
	}
}

static int zfcp_fsf_set_data_dir(struct scsi_cmnd *scsi_cmnd, u32 *data_dir)
{
	switch (scsi_get_prot_op(scsi_cmnd)) {
	case SCSI_PROT_NORMAL:
		switch (scsi_cmnd->sc_data_direction) {
		case DMA_NONE:
			*data_dir = FSF_DATADIR_CMND;
			break;
		case DMA_FROM_DEVICE:
			*data_dir = FSF_DATADIR_READ;
			break;
		case DMA_TO_DEVICE:
			*data_dir = FSF_DATADIR_WRITE;
			break;
		case DMA_BIDIRECTIONAL:
			return -EINVAL;
		}
		break;

	case SCSI_PROT_READ_STRIP:
		*data_dir = FSF_DATADIR_DIF_READ_STRIP;
		break;
	case SCSI_PROT_WRITE_INSERT:
		*data_dir = FSF_DATADIR_DIF_WRITE_INSERT;
		break;
	case SCSI_PROT_READ_PASS:
		*data_dir = FSF_DATADIR_DIF_READ_CONVERT;
		break;
	case SCSI_PROT_WRITE_PASS:
		*data_dir = FSF_DATADIR_DIF_WRITE_CONVERT;
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

/**
 * zfcp_fsf_send_fcp_command_task - initiate an FCP command (for a SCSI command)
 * @unit: unit where command is sent to
@@ -2201,9 +2262,10 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit,
	struct zfcp_fsf_req *req;
	struct fcp_cmnd *fcp_cmnd;
	unsigned int sbtype = SBAL_FLAGS0_TYPE_READ;
	int real_bytes, retval = -EIO;
	int real_bytes, retval = -EIO, dix_bytes = 0;
	struct zfcp_adapter *adapter = unit->port->adapter;
	struct zfcp_qdio *qdio = adapter->qdio;
	struct fsf_qtcb_bottom_io *io;

	if (unlikely(!(atomic_read(&unit->status) &
		       ZFCP_STATUS_COMMON_UNBLOCKED)))
@@ -2226,46 +2288,46 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit,
		goto out;
	}

	scsi_cmnd->host_scribble = (unsigned char *) req->req_id;

	io = &req->qtcb->bottom.io;
	req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
	req->unit = unit;
	req->data = scsi_cmnd;
	req->handler = zfcp_fsf_send_fcp_command_handler;
	req->qtcb->header.lun_handle = unit->handle;
	req->qtcb->header.port_handle = unit->port->handle;
	req->qtcb->bottom.io.service_class = FSF_CLASS_3;
	req->qtcb->bottom.io.fcp_cmnd_length = FCP_CMND_LEN;
	io->service_class = FSF_CLASS_3;
	io->fcp_cmnd_length = FCP_CMND_LEN;

	scsi_cmnd->host_scribble = (unsigned char *) req->req_id;

	/*
	 * set depending on data direction:
	 *      data direction bits in SBALE (SB Type)
	 *      data direction bits in QTCB
	 */
	switch (scsi_cmnd->sc_data_direction) {
	case DMA_NONE:
		req->qtcb->bottom.io.data_direction = FSF_DATADIR_CMND;
		break;
	case DMA_FROM_DEVICE:
		req->qtcb->bottom.io.data_direction = FSF_DATADIR_READ;
		break;
	case DMA_TO_DEVICE:
		req->qtcb->bottom.io.data_direction = FSF_DATADIR_WRITE;
		break;
	case DMA_BIDIRECTIONAL:
		goto failed_scsi_cmnd;
	if (scsi_get_prot_op(scsi_cmnd) != SCSI_PROT_NORMAL) {
		io->data_block_length = scsi_cmnd->device->sector_size;
		io->ref_tag_value = scsi_get_lba(scsi_cmnd) & 0xFFFFFFFF;
	}

	zfcp_fsf_set_data_dir(scsi_cmnd, &io->data_direction);

	get_device(&unit->dev);

	fcp_cmnd = (struct fcp_cmnd *) &req->qtcb->bottom.io.fcp_cmnd;
	zfcp_fc_scsi_to_fcp(fcp_cmnd, scsi_cmnd);

	if (scsi_prot_sg_count(scsi_cmnd)) {
		zfcp_qdio_set_data_div(qdio, &req->qdio_req,
				       scsi_prot_sg_count(scsi_cmnd));
		dix_bytes = zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req,
						scsi_prot_sglist(scsi_cmnd));
		io->prot_data_length = dix_bytes;
	}

	real_bytes = zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req,
					     scsi_sglist(scsi_cmnd));
	if (unlikely(real_bytes < 0))

	if (unlikely(real_bytes < 0) || unlikely(dix_bytes < 0))
		goto failed_scsi_cmnd;

	zfcp_qdio_set_sbale_last(adapter->qdio, &req->qdio_req);

	retval = zfcp_fsf_req_send(req);
	if (unlikely(retval))
		goto failed_scsi_cmnd;
@@ -2389,6 +2451,7 @@ struct zfcp_fsf_req *zfcp_fsf_control_file(struct zfcp_adapter *adapter,
		zfcp_fsf_req_free(req);
		goto out;
	}
	zfcp_qdio_set_sbale_last(adapter->qdio, &req->qdio_req);

	zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT);
	retval = zfcp_fsf_req_send(req);
Loading