diff options
| author | Jiri Kosina <jkosina@suse.cz> | 2011-09-15 15:08:05 +0200 | 
|---|---|---|
| committer | Jiri Kosina <jkosina@suse.cz> | 2011-09-15 15:08:18 +0200 | 
| commit | e060c38434b2caa78efe7cedaff4191040b65a15 (patch) | |
| tree | 407361230bf6733f63d8e788e4b5e6566ee04818 /drivers/target/iscsi/iscsi_target_datain_values.c | |
| parent | 10e4ac572eeffe5317019bd7330b6058a400dfc2 (diff) | |
| parent | cc39c6a9bbdebfcf1a7dee64d83bf302bc38d941 (diff) | |
| download | olio-linux-3.10-e060c38434b2caa78efe7cedaff4191040b65a15.tar.xz olio-linux-3.10-e060c38434b2caa78efe7cedaff4191040b65a15.zip  | |
Merge branch 'master' into for-next
Fast-forward merge with Linus to be able to merge patches
based on more recent version of the tree.
Diffstat (limited to 'drivers/target/iscsi/iscsi_target_datain_values.c')
| -rw-r--r-- | drivers/target/iscsi/iscsi_target_datain_values.c | 531 | 
1 files changed, 531 insertions, 0 deletions
diff --git a/drivers/target/iscsi/iscsi_target_datain_values.c b/drivers/target/iscsi/iscsi_target_datain_values.c new file mode 100644 index 00000000000..8c049512951 --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_datain_values.c @@ -0,0 +1,531 @@ +/******************************************************************************* + * This file contains the iSCSI Target DataIN value generation functions. + * + * \u00a9 Copyright 2007-2011 RisingTide Systems LLC. + * + * Licensed to the Linux Foundation under the General Public License (GPL) version 2. + * + * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + ******************************************************************************/ + +#include <scsi/iscsi_proto.h> + +#include "iscsi_target_core.h" +#include "iscsi_target_seq_pdu_list.h" +#include "iscsi_target_erl1.h" +#include "iscsi_target_util.h" +#include "iscsi_target.h" +#include "iscsi_target_datain_values.h" + +struct iscsi_datain_req *iscsit_allocate_datain_req(void) +{ +	struct iscsi_datain_req *dr; + +	dr = kmem_cache_zalloc(lio_dr_cache, GFP_ATOMIC); +	if (!dr) { +		pr_err("Unable to allocate memory for" +				" struct iscsi_datain_req\n"); +		return NULL; +	} +	INIT_LIST_HEAD(&dr->dr_list); + +	return dr; +} + +void iscsit_attach_datain_req(struct iscsi_cmd *cmd, struct iscsi_datain_req *dr) +{ +	spin_lock(&cmd->datain_lock); +	list_add_tail(&dr->dr_list, &cmd->datain_list); +	spin_unlock(&cmd->datain_lock); +} + +void iscsit_free_datain_req(struct iscsi_cmd *cmd, struct iscsi_datain_req *dr) +{ +	spin_lock(&cmd->datain_lock); +	list_del(&dr->dr_list); +	spin_unlock(&cmd->datain_lock); + +	kmem_cache_free(lio_dr_cache, dr); +} + +void iscsit_free_all_datain_reqs(struct iscsi_cmd *cmd) +{ +	struct iscsi_datain_req *dr, *dr_tmp; + +	spin_lock(&cmd->datain_lock); +	list_for_each_entry_safe(dr, dr_tmp, &cmd->datain_list, dr_list) { +		list_del(&dr->dr_list); +		kmem_cache_free(lio_dr_cache, dr); +	} +	spin_unlock(&cmd->datain_lock); +} + +struct iscsi_datain_req *iscsit_get_datain_req(struct iscsi_cmd *cmd) +{ +	struct iscsi_datain_req *dr; + +	if (list_empty(&cmd->datain_list)) { +		pr_err("cmd->datain_list is empty for ITT:" +			" 0x%08x\n", cmd->init_task_tag); +		return NULL; +	} +	list_for_each_entry(dr, &cmd->datain_list, dr_list) +		break; + +	return dr; +} + +/* + *	For Normal and Recovery DataSequenceInOrder=Yes and DataPDUInOrder=Yes. + */ +static struct iscsi_datain_req *iscsit_set_datain_values_yes_and_yes( +	struct iscsi_cmd *cmd, +	struct iscsi_datain *datain) +{ +	u32 next_burst_len, read_data_done, read_data_left; +	struct iscsi_conn *conn = cmd->conn; +	struct iscsi_datain_req *dr; + +	dr = iscsit_get_datain_req(cmd); +	if (!dr) +		return NULL; + +	if (dr->recovery && dr->generate_recovery_values) { +		if (iscsit_create_recovery_datain_values_datasequenceinorder_yes( +					cmd, dr) < 0) +			return NULL; + +		dr->generate_recovery_values = 0; +	} + +	next_burst_len = (!dr->recovery) ? +			cmd->next_burst_len : dr->next_burst_len; +	read_data_done = (!dr->recovery) ? +			cmd->read_data_done : dr->read_data_done; + +	read_data_left = (cmd->data_length - read_data_done); +	if (!read_data_left) { +		pr_err("ITT: 0x%08x read_data_left is zero!\n", +				cmd->init_task_tag); +		return NULL; +	} + +	if ((read_data_left <= conn->conn_ops->MaxRecvDataSegmentLength) && +	    (read_data_left <= (conn->sess->sess_ops->MaxBurstLength - +	     next_burst_len))) { +		datain->length = read_data_left; + +		datain->flags |= (ISCSI_FLAG_CMD_FINAL | ISCSI_FLAG_DATA_STATUS); +		if (conn->sess->sess_ops->ErrorRecoveryLevel > 0) +			datain->flags |= ISCSI_FLAG_DATA_ACK; +	} else { +		if ((next_burst_len + +		     conn->conn_ops->MaxRecvDataSegmentLength) < +		     conn->sess->sess_ops->MaxBurstLength) { +			datain->length = +				conn->conn_ops->MaxRecvDataSegmentLength; +			next_burst_len += datain->length; +		} else { +			datain->length = (conn->sess->sess_ops->MaxBurstLength - +					  next_burst_len); +			next_burst_len = 0; + +			datain->flags |= ISCSI_FLAG_CMD_FINAL; +			if (conn->sess->sess_ops->ErrorRecoveryLevel > 0) +				datain->flags |= ISCSI_FLAG_DATA_ACK; +		} +	} + +	datain->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++; +	datain->offset = read_data_done; + +	if (!dr->recovery) { +		cmd->next_burst_len = next_burst_len; +		cmd->read_data_done += datain->length; +	} else { +		dr->next_burst_len = next_burst_len; +		dr->read_data_done += datain->length; +	} + +	if (!dr->recovery) { +		if (datain->flags & ISCSI_FLAG_DATA_STATUS) +			dr->dr_complete = DATAIN_COMPLETE_NORMAL; + +		return dr; +	} + +	if (!dr->runlength) { +		if (datain->flags & ISCSI_FLAG_DATA_STATUS) { +			dr->dr_complete = +			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ? +				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY : +				DATAIN_COMPLETE_CONNECTION_RECOVERY; +		} +	} else { +		if ((dr->begrun + dr->runlength) == dr->data_sn) { +			dr->dr_complete = +			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ? +				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY : +				DATAIN_COMPLETE_CONNECTION_RECOVERY; +		} +	} + +	return dr; +} + +/* + *	For Normal and Recovery DataSequenceInOrder=No and DataPDUInOrder=Yes. + */ +static struct iscsi_datain_req *iscsit_set_datain_values_no_and_yes( +	struct iscsi_cmd *cmd, +	struct iscsi_datain *datain) +{ +	u32 offset, read_data_done, read_data_left, seq_send_order; +	struct iscsi_conn *conn = cmd->conn; +	struct iscsi_datain_req *dr; +	struct iscsi_seq *seq; + +	dr = iscsit_get_datain_req(cmd); +	if (!dr) +		return NULL; + +	if (dr->recovery && dr->generate_recovery_values) { +		if (iscsit_create_recovery_datain_values_datasequenceinorder_no( +					cmd, dr) < 0) +			return NULL; + +		dr->generate_recovery_values = 0; +	} + +	read_data_done = (!dr->recovery) ? +			cmd->read_data_done : dr->read_data_done; +	seq_send_order = (!dr->recovery) ? +			cmd->seq_send_order : dr->seq_send_order; + +	read_data_left = (cmd->data_length - read_data_done); +	if (!read_data_left) { +		pr_err("ITT: 0x%08x read_data_left is zero!\n", +				cmd->init_task_tag); +		return NULL; +	} + +	seq = iscsit_get_seq_holder_for_datain(cmd, seq_send_order); +	if (!seq) +		return NULL; + +	seq->sent = 1; + +	if (!dr->recovery && !seq->next_burst_len) +		seq->first_datasn = cmd->data_sn; + +	offset = (seq->offset + seq->next_burst_len); + +	if ((offset + conn->conn_ops->MaxRecvDataSegmentLength) >= +	     cmd->data_length) { +		datain->length = (cmd->data_length - offset); +		datain->offset = offset; + +		datain->flags |= ISCSI_FLAG_CMD_FINAL; +		if (conn->sess->sess_ops->ErrorRecoveryLevel > 0) +			datain->flags |= ISCSI_FLAG_DATA_ACK; + +		seq->next_burst_len = 0; +		seq_send_order++; +	} else { +		if ((seq->next_burst_len + +		     conn->conn_ops->MaxRecvDataSegmentLength) < +		     conn->sess->sess_ops->MaxBurstLength) { +			datain->length = +				conn->conn_ops->MaxRecvDataSegmentLength; +			datain->offset = (seq->offset + seq->next_burst_len); + +			seq->next_burst_len += datain->length; +		} else { +			datain->length = (conn->sess->sess_ops->MaxBurstLength - +					  seq->next_burst_len); +			datain->offset = (seq->offset + seq->next_burst_len); + +			datain->flags |= ISCSI_FLAG_CMD_FINAL; +			if (conn->sess->sess_ops->ErrorRecoveryLevel > 0) +				datain->flags |= ISCSI_FLAG_DATA_ACK; + +			seq->next_burst_len = 0; +			seq_send_order++; +		} +	} + +	if ((read_data_done + datain->length) == cmd->data_length) +		datain->flags |= ISCSI_FLAG_DATA_STATUS; + +	datain->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++; +	if (!dr->recovery) { +		cmd->seq_send_order = seq_send_order; +		cmd->read_data_done += datain->length; +	} else { +		dr->seq_send_order = seq_send_order; +		dr->read_data_done += datain->length; +	} + +	if (!dr->recovery) { +		if (datain->flags & ISCSI_FLAG_CMD_FINAL) +			seq->last_datasn = datain->data_sn; +		if (datain->flags & ISCSI_FLAG_DATA_STATUS) +			dr->dr_complete = DATAIN_COMPLETE_NORMAL; + +		return dr; +	} + +	if (!dr->runlength) { +		if (datain->flags & ISCSI_FLAG_DATA_STATUS) { +			dr->dr_complete = +			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ? +				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY : +				DATAIN_COMPLETE_CONNECTION_RECOVERY; +		} +	} else { +		if ((dr->begrun + dr->runlength) == dr->data_sn) { +			dr->dr_complete = +			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ? +				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY : +				DATAIN_COMPLETE_CONNECTION_RECOVERY; +		} +	} + +	return dr; +} + +/* + *	For Normal and Recovery DataSequenceInOrder=Yes and DataPDUInOrder=No. + */ +static struct iscsi_datain_req *iscsit_set_datain_values_yes_and_no( +	struct iscsi_cmd *cmd, +	struct iscsi_datain *datain) +{ +	u32 next_burst_len, read_data_done, read_data_left; +	struct iscsi_conn *conn = cmd->conn; +	struct iscsi_datain_req *dr; +	struct iscsi_pdu *pdu; + +	dr = iscsit_get_datain_req(cmd); +	if (!dr) +		return NULL; + +	if (dr->recovery && dr->generate_recovery_values) { +		if (iscsit_create_recovery_datain_values_datasequenceinorder_yes( +					cmd, dr) < 0) +			return NULL; + +		dr->generate_recovery_values = 0; +	} + +	next_burst_len = (!dr->recovery) ? +			cmd->next_burst_len : dr->next_burst_len; +	read_data_done = (!dr->recovery) ? +			cmd->read_data_done : dr->read_data_done; + +	read_data_left = (cmd->data_length - read_data_done); +	if (!read_data_left) { +		pr_err("ITT: 0x%08x read_data_left is zero!\n", +				cmd->init_task_tag); +		return dr; +	} + +	pdu = iscsit_get_pdu_holder_for_seq(cmd, NULL); +	if (!pdu) +		return dr; + +	if ((read_data_done + pdu->length) == cmd->data_length) { +		pdu->flags |= (ISCSI_FLAG_CMD_FINAL | ISCSI_FLAG_DATA_STATUS); +		if (conn->sess->sess_ops->ErrorRecoveryLevel > 0) +			pdu->flags |= ISCSI_FLAG_DATA_ACK; + +		next_burst_len = 0; +	} else { +		if ((next_burst_len + conn->conn_ops->MaxRecvDataSegmentLength) < +		     conn->sess->sess_ops->MaxBurstLength) +			next_burst_len += pdu->length; +		else { +			pdu->flags |= ISCSI_FLAG_CMD_FINAL; +			if (conn->sess->sess_ops->ErrorRecoveryLevel > 0) +				pdu->flags |= ISCSI_FLAG_DATA_ACK; + +			next_burst_len = 0; +		} +	} + +	pdu->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++; +	if (!dr->recovery) { +		cmd->next_burst_len = next_burst_len; +		cmd->read_data_done += pdu->length; +	} else { +		dr->next_burst_len = next_burst_len; +		dr->read_data_done += pdu->length; +	} + +	datain->flags = pdu->flags; +	datain->length = pdu->length; +	datain->offset = pdu->offset; +	datain->data_sn = pdu->data_sn; + +	if (!dr->recovery) { +		if (datain->flags & ISCSI_FLAG_DATA_STATUS) +			dr->dr_complete = DATAIN_COMPLETE_NORMAL; + +		return dr; +	} + +	if (!dr->runlength) { +		if (datain->flags & ISCSI_FLAG_DATA_STATUS) { +			dr->dr_complete = +			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ? +				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY : +				DATAIN_COMPLETE_CONNECTION_RECOVERY; +		} +	} else { +		if ((dr->begrun + dr->runlength) == dr->data_sn) { +			dr->dr_complete = +			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ? +				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY : +				DATAIN_COMPLETE_CONNECTION_RECOVERY; +		} +	} + +	return dr; +} + +/* + *	For Normal and Recovery DataSequenceInOrder=No and DataPDUInOrder=No. + */ +static struct iscsi_datain_req *iscsit_set_datain_values_no_and_no( +	struct iscsi_cmd *cmd, +	struct iscsi_datain *datain) +{ +	u32 read_data_done, read_data_left, seq_send_order; +	struct iscsi_conn *conn = cmd->conn; +	struct iscsi_datain_req *dr; +	struct iscsi_pdu *pdu; +	struct iscsi_seq *seq = NULL; + +	dr = iscsit_get_datain_req(cmd); +	if (!dr) +		return NULL; + +	if (dr->recovery && dr->generate_recovery_values) { +		if (iscsit_create_recovery_datain_values_datasequenceinorder_no( +					cmd, dr) < 0) +			return NULL; + +		dr->generate_recovery_values = 0; +	} + +	read_data_done = (!dr->recovery) ? +			cmd->read_data_done : dr->read_data_done; +	seq_send_order = (!dr->recovery) ? +			cmd->seq_send_order : dr->seq_send_order; + +	read_data_left = (cmd->data_length - read_data_done); +	if (!read_data_left) { +		pr_err("ITT: 0x%08x read_data_left is zero!\n", +				cmd->init_task_tag); +		return NULL; +	} + +	seq = iscsit_get_seq_holder_for_datain(cmd, seq_send_order); +	if (!seq) +		return NULL; + +	seq->sent = 1; + +	if (!dr->recovery && !seq->next_burst_len) +		seq->first_datasn = cmd->data_sn; + +	pdu = iscsit_get_pdu_holder_for_seq(cmd, seq); +	if (!pdu) +		return NULL; + +	if (seq->pdu_send_order == seq->pdu_count) { +		pdu->flags |= ISCSI_FLAG_CMD_FINAL; +		if (conn->sess->sess_ops->ErrorRecoveryLevel > 0) +			pdu->flags |= ISCSI_FLAG_DATA_ACK; + +		seq->next_burst_len = 0; +		seq_send_order++; +	} else +		seq->next_burst_len += pdu->length; + +	if ((read_data_done + pdu->length) == cmd->data_length) +		pdu->flags |= ISCSI_FLAG_DATA_STATUS; + +	pdu->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++; +	if (!dr->recovery) { +		cmd->seq_send_order = seq_send_order; +		cmd->read_data_done += pdu->length; +	} else { +		dr->seq_send_order = seq_send_order; +		dr->read_data_done += pdu->length; +	} + +	datain->flags = pdu->flags; +	datain->length = pdu->length; +	datain->offset = pdu->offset; +	datain->data_sn = pdu->data_sn; + +	if (!dr->recovery) { +		if (datain->flags & ISCSI_FLAG_CMD_FINAL) +			seq->last_datasn = datain->data_sn; +		if (datain->flags & ISCSI_FLAG_DATA_STATUS) +			dr->dr_complete = DATAIN_COMPLETE_NORMAL; + +		return dr; +	} + +	if (!dr->runlength) { +		if (datain->flags & ISCSI_FLAG_DATA_STATUS) { +			dr->dr_complete = +			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ? +				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY : +				DATAIN_COMPLETE_CONNECTION_RECOVERY; +		} +	} else { +		if ((dr->begrun + dr->runlength) == dr->data_sn) { +			dr->dr_complete = +			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ? +				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY : +				DATAIN_COMPLETE_CONNECTION_RECOVERY; +		} +	} + +	return dr; +} + +struct iscsi_datain_req *iscsit_get_datain_values( +	struct iscsi_cmd *cmd, +	struct iscsi_datain *datain) +{ +	struct iscsi_conn *conn = cmd->conn; + +	if (conn->sess->sess_ops->DataSequenceInOrder && +	    conn->sess->sess_ops->DataPDUInOrder) +		return iscsit_set_datain_values_yes_and_yes(cmd, datain); +	else if (!conn->sess->sess_ops->DataSequenceInOrder && +		  conn->sess->sess_ops->DataPDUInOrder) +		return iscsit_set_datain_values_no_and_yes(cmd, datain); +	else if (conn->sess->sess_ops->DataSequenceInOrder && +		 !conn->sess->sess_ops->DataPDUInOrder) +		return iscsit_set_datain_values_yes_and_no(cmd, datain); +	else if (!conn->sess->sess_ops->DataSequenceInOrder && +		   !conn->sess->sess_ops->DataPDUInOrder) +		return iscsit_set_datain_values_no_and_no(cmd, datain); + +	return NULL; +}  |