diff options
Diffstat (limited to 'drivers/target')
27 files changed, 1652 insertions, 967 deletions
diff --git a/drivers/target/iscsi/Makefile b/drivers/target/iscsi/Makefile index 5b9a2cf7f0a..13a92403fe3 100644 --- a/drivers/target/iscsi/Makefile +++ b/drivers/target/iscsi/Makefile @@ -15,6 +15,7 @@ iscsi_target_mod-y +=		iscsi_target_parameters.o \  				iscsi_target_util.o \  				iscsi_target.o \  				iscsi_target_configfs.o \ -				iscsi_target_stat.o +				iscsi_target_stat.o \ +				iscsi_target_transport.o  obj-$(CONFIG_ISCSI_TARGET)	+= iscsi_target_mod.o diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 7ea246a0773..ffbc6a94be5 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -49,6 +49,8 @@  #include "iscsi_target_device.h"  #include "iscsi_target_stat.h" +#include <target/iscsi/iscsi_transport.h> +  static LIST_HEAD(g_tiqn_list);  static LIST_HEAD(g_np_list);  static DEFINE_SPINLOCK(tiqn_lock); @@ -68,8 +70,7 @@ struct kmem_cache *lio_ooo_cache;  struct kmem_cache *lio_r2t_cache;  static int iscsit_handle_immediate_data(struct iscsi_cmd *, -			unsigned char *buf, u32); -static int iscsit_logout_post_handler(struct iscsi_cmd *, struct iscsi_conn *); +			struct iscsi_scsi_req *, u32);  struct iscsi_tiqn *iscsit_get_tiqn_for_login(unsigned char *buf)  { @@ -401,8 +402,7 @@ struct iscsi_np *iscsit_add_np(  	spin_unlock_bh(&np_lock);  	pr_debug("CORE[0] - Added Network Portal: %s:%hu on %s\n", -		np->np_ip, np->np_port, (np->np_network_transport == ISCSI_TCP) ? -		"TCP" : "SCTP"); +		np->np_ip, np->np_port, np->np_transport->name);  	return np;  } @@ -441,11 +441,10 @@ int iscsit_reset_np_thread(  	return 0;  } -static int iscsit_del_np_comm(struct iscsi_np *np) +static void iscsit_free_np(struct iscsi_np *np)  {  	if (np->np_socket)  		sock_release(np->np_socket); -	return 0;  }  int iscsit_del_np(struct iscsi_np *np) @@ -467,20 +466,47 @@ int iscsit_del_np(struct iscsi_np *np)  		send_sig(SIGINT, np->np_thread, 1);  		kthread_stop(np->np_thread);  	} -	iscsit_del_np_comm(np); + +	np->np_transport->iscsit_free_np(np);  	spin_lock_bh(&np_lock);  	list_del(&np->np_list);  	spin_unlock_bh(&np_lock);  	pr_debug("CORE[0] - Removed Network Portal: %s:%hu on %s\n", -		np->np_ip, np->np_port, (np->np_network_transport == ISCSI_TCP) ? -		"TCP" : "SCTP"); +		np->np_ip, np->np_port, np->np_transport->name); +	iscsit_put_transport(np->np_transport);  	kfree(np);  	return 0;  } +static int iscsit_immediate_queue(struct iscsi_conn *, struct iscsi_cmd *, int); +static int iscsit_response_queue(struct iscsi_conn *, struct iscsi_cmd *, int); + +static int iscsit_queue_rsp(struct iscsi_conn *conn, struct iscsi_cmd *cmd) +{ +	iscsit_add_cmd_to_response_queue(cmd, cmd->conn, cmd->i_state); +	return 0; +} + +static struct iscsit_transport iscsi_target_transport = { +	.name			= "iSCSI/TCP", +	.transport_type		= ISCSI_TCP, +	.owner			= NULL, +	.iscsit_setup_np	= iscsit_setup_np, +	.iscsit_accept_np	= iscsit_accept_np, +	.iscsit_free_np		= iscsit_free_np, +	.iscsit_alloc_cmd	= iscsit_alloc_cmd, +	.iscsit_get_login_rx	= iscsit_get_login_rx, +	.iscsit_put_login_tx	= iscsit_put_login_tx, +	.iscsit_get_dataout	= iscsit_build_r2ts_for_cmd, +	.iscsit_immediate_queue	= iscsit_immediate_queue, +	.iscsit_response_queue	= iscsit_response_queue, +	.iscsit_queue_data_in	= iscsit_queue_rsp, +	.iscsit_queue_status	= iscsit_queue_rsp, +}; +  static int __init iscsi_target_init_module(void)  {  	int ret = 0; @@ -557,6 +583,8 @@ static int __init iscsi_target_init_module(void)  		goto ooo_out;  	} +	iscsit_register_transport(&iscsi_target_transport); +  	if (iscsit_load_discovery_tpg() < 0)  		goto r2t_out; @@ -587,6 +615,7 @@ static void __exit iscsi_target_cleanup_module(void)  	iscsi_deallocate_thread_sets();  	iscsi_thread_set_free();  	iscsit_release_discovery_tpg(); +	iscsit_unregister_transport(&iscsi_target_transport);  	kmem_cache_destroy(lio_cmd_cache);  	kmem_cache_destroy(lio_qr_cache);  	kmem_cache_destroy(lio_dr_cache); @@ -682,11 +711,20 @@ int iscsit_add_reject_from_cmd(  	iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);  	ret = wait_for_completion_interruptible(&cmd->reject_comp); +	/* +	 * Perform the kref_put now if se_cmd has already been setup by +	 * scsit_setup_scsi_cmd() +	 */ +	if (cmd->se_cmd.se_tfo != NULL) { +		pr_debug("iscsi reject: calling target_put_sess_cmd >>>>>>\n"); +		target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd); +	}  	if (ret != 0)  		return -1;  	return (!fail_conn) ? 0 : -1;  } +EXPORT_SYMBOL(iscsit_add_reject_from_cmd);  /*   * Map some portion of the allocated scatterlist to an iovec, suitable for @@ -745,6 +783,9 @@ static void iscsit_ack_from_expstatsn(struct iscsi_conn *conn, u32 exp_statsn)  	conn->exp_statsn = exp_statsn; +	if (conn->sess->sess_ops->RDMAExtensions) +		return; +  	spin_lock_bh(&conn->cmd_lock);  	list_for_each_entry(cmd, &conn->conn_cmd_list, i_conn_node) {  		spin_lock(&cmd->istate_lock); @@ -777,12 +818,10 @@ static int iscsit_allocate_iovecs(struct iscsi_cmd *cmd)  	return 0;  } -static int iscsit_handle_scsi_cmd( -	struct iscsi_conn *conn, -	unsigned char *buf) +int iscsit_setup_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, +			  unsigned char *buf)  { -	int data_direction, payload_length, cmdsn_ret = 0, immed_ret; -	struct iscsi_cmd *cmd = NULL; +	int data_direction, payload_length;  	struct iscsi_scsi_req *hdr;  	int iscsi_task_attr;  	int sam_task_attr; @@ -805,8 +844,8 @@ static int iscsit_handle_scsi_cmd(  	    !(hdr->flags & ISCSI_FLAG_CMD_FINAL)) {  		pr_err("ISCSI_FLAG_CMD_WRITE & ISCSI_FLAG_CMD_FINAL"  				" not set. Bad iSCSI Initiator.\n"); -		return iscsit_add_reject(ISCSI_REASON_BOOKMARK_INVALID, 1, -				buf, conn); +		return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_INVALID, +				1, 1, buf, cmd);  	}  	if (((hdr->flags & ISCSI_FLAG_CMD_READ) || @@ -826,8 +865,8 @@ static int iscsit_handle_scsi_cmd(  		pr_err("ISCSI_FLAG_CMD_READ or ISCSI_FLAG_CMD_WRITE"  			" set when Expected Data Transfer Length is 0 for"  			" CDB: 0x%02x. Bad iSCSI Initiator.\n", hdr->cdb[0]); -		return iscsit_add_reject(ISCSI_REASON_BOOKMARK_INVALID, 1, -				buf, conn); +		return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_INVALID, +				1, 1, buf, cmd);  	}  done: @@ -836,29 +875,29 @@ done:  		pr_err("ISCSI_FLAG_CMD_READ and/or ISCSI_FLAG_CMD_WRITE"  			" MUST be set if Expected Data Transfer Length is not 0."  			" Bad iSCSI Initiator\n"); -		return iscsit_add_reject(ISCSI_REASON_BOOKMARK_INVALID, 1, -				buf, conn); +		return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_INVALID, +				1, 1, buf, cmd);  	}  	if ((hdr->flags & ISCSI_FLAG_CMD_READ) &&  	    (hdr->flags & ISCSI_FLAG_CMD_WRITE)) {  		pr_err("Bidirectional operations not supported!\n"); -		return iscsit_add_reject(ISCSI_REASON_BOOKMARK_INVALID, 1, -				buf, conn); +		return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_INVALID, +				1, 1, buf, cmd);  	}  	if (hdr->opcode & ISCSI_OP_IMMEDIATE) {  		pr_err("Illegally set Immediate Bit in iSCSI Initiator"  				" Scsi Command PDU.\n"); -		return iscsit_add_reject(ISCSI_REASON_BOOKMARK_INVALID, 1, -				buf, conn); +		return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_INVALID, +				1, 1, buf, cmd);  	}  	if (payload_length && !conn->sess->sess_ops->ImmediateData) {  		pr_err("ImmediateData=No but DataSegmentLength=%u,"  			" protocol error.\n", payload_length); -		return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1, -				buf, conn); +		return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR, +				1, 1, buf, cmd);  	}  	if ((be32_to_cpu(hdr->data_length )== payload_length) && @@ -866,43 +905,38 @@ done:  		pr_err("Expected Data Transfer Length and Length of"  			" Immediate Data are the same, but ISCSI_FLAG_CMD_FINAL"  			" bit is not set protocol error\n"); -		return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1, -				buf, conn); +		return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR, +				1, 1, buf, cmd);  	}  	if (payload_length > be32_to_cpu(hdr->data_length)) {  		pr_err("DataSegmentLength: %u is greater than"  			" EDTL: %u, protocol error.\n", payload_length,  				hdr->data_length); -		return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1, -				buf, conn); +		return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR, +				1, 1, buf, cmd);  	}  	if (payload_length > conn->conn_ops->MaxXmitDataSegmentLength) {  		pr_err("DataSegmentLength: %u is greater than"  			" MaxXmitDataSegmentLength: %u, protocol error.\n",  			payload_length, conn->conn_ops->MaxXmitDataSegmentLength); -		return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1, -				buf, conn); +		return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR, +				1, 1, buf, cmd);  	}  	if (payload_length > conn->sess->sess_ops->FirstBurstLength) {  		pr_err("DataSegmentLength: %u is greater than"  			" FirstBurstLength: %u, protocol error.\n",  			payload_length, conn->sess->sess_ops->FirstBurstLength); -		return iscsit_add_reject(ISCSI_REASON_BOOKMARK_INVALID, 1, -					buf, conn); +		return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_INVALID, +				1, 1, buf, cmd);  	}  	data_direction = (hdr->flags & ISCSI_FLAG_CMD_WRITE) ? DMA_TO_DEVICE :  			 (hdr->flags & ISCSI_FLAG_CMD_READ) ? DMA_FROM_DEVICE :  			  DMA_NONE; -	cmd = iscsit_allocate_cmd(conn, GFP_KERNEL); -	if (!cmd) -		return iscsit_add_reject(ISCSI_REASON_BOOKMARK_NO_RESOURCES, 1, -					 buf, conn); -  	cmd->data_direction = data_direction;  	iscsi_task_attr = hdr->flags & ISCSI_FLAG_CMD_ATTR_MASK;  	/* @@ -945,7 +979,8 @@ done:  	cmd->exp_stat_sn	= be32_to_cpu(hdr->exp_statsn);  	cmd->first_burst_len	= payload_length; -	if (cmd->data_direction == DMA_FROM_DEVICE) { +	if (!conn->sess->sess_ops->RDMAExtensions && +	     cmd->data_direction == DMA_FROM_DEVICE) {  		struct iscsi_datain_req *dr;  		dr = iscsit_allocate_datain_req(); @@ -967,7 +1002,10 @@ done:  	pr_debug("Got SCSI Command, ITT: 0x%08x, CmdSN: 0x%08x,"  		" ExpXferLen: %u, Length: %u, CID: %hu\n", hdr->itt, -		hdr->cmdsn, hdr->data_length, payload_length, conn->cid); +		hdr->cmdsn, be32_to_cpu(hdr->data_length), payload_length, +		conn->cid); + +	target_get_sess_cmd(conn->sess->se_sess, &cmd->se_cmd, true);  	cmd->sense_reason = transport_lookup_cmd_lun(&cmd->se_cmd,  						     scsilun_to_int(&hdr->lun)); @@ -1001,12 +1039,24 @@ attach_cmd:  	 */  	core_alua_check_nonop_delay(&cmd->se_cmd); -	if (iscsit_allocate_iovecs(cmd) < 0) { -		return iscsit_add_reject_from_cmd( -				ISCSI_REASON_BOOKMARK_NO_RESOURCES, -				1, 0, buf, cmd); -	} +	return 0; +} +EXPORT_SYMBOL(iscsit_setup_scsi_cmd); + +void iscsit_set_unsoliticed_dataout(struct iscsi_cmd *cmd) +{ +	iscsit_set_dataout_sequence_values(cmd); + +	spin_lock_bh(&cmd->dataout_timeout_lock); +	iscsit_start_dataout_timer(cmd, cmd->conn); +	spin_unlock_bh(&cmd->dataout_timeout_lock); +} +EXPORT_SYMBOL(iscsit_set_unsoliticed_dataout); +int iscsit_process_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, +			    struct iscsi_scsi_req *hdr) +{ +	int cmdsn_ret = 0;  	/*  	 * Check the CmdSN against ExpCmdSN/MaxCmdSN here if  	 * the Immediate Bit is not set, and no Immediate @@ -1019,12 +1069,17 @@ attach_cmd:  	 */  	if (!cmd->immediate_data) {  		cmdsn_ret = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn); -		if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) +		if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) { +			if (!cmd->sense_reason) +				return 0; + +			target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd);  			return 0; -		else if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) +		} else if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) {  			return iscsit_add_reject_from_cmd(  				ISCSI_REASON_PROTOCOL_ERROR, -				1, 0, buf, cmd); +				1, 0, (unsigned char *)hdr, cmd); +		}  	}  	iscsit_ack_from_expstatsn(conn, be32_to_cpu(hdr->exp_statsn)); @@ -1033,25 +1088,23 @@ attach_cmd:  	 * If no Immediate Data is attached, it's OK to return now.  	 */  	if (!cmd->immediate_data) { -		if (!cmd->sense_reason && cmd->unsolicited_data) { -			iscsit_set_dataout_sequence_values(cmd); - -			spin_lock_bh(&cmd->dataout_timeout_lock); -			iscsit_start_dataout_timer(cmd, cmd->conn); -			spin_unlock_bh(&cmd->dataout_timeout_lock); -		} +		if (!cmd->sense_reason && cmd->unsolicited_data) +			iscsit_set_unsoliticed_dataout(cmd); +		if (!cmd->sense_reason) +			return 0; +		target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd);  		return 0;  	}  	/* -	 * Early CHECK_CONDITIONs never make it to the transport processing -	 * thread.  They are processed in CmdSN order by -	 * iscsit_check_received_cmdsn() below. +	 * Early CHECK_CONDITIONs with ImmediateData never make it to command +	 * execution.  These exceptions are processed in CmdSN order using +	 * iscsit_check_received_cmdsn() in iscsit_get_immediate_data() below.  	 */  	if (cmd->sense_reason) { -		immed_ret = IMMEDIATE_DATA_NORMAL_OPERATION; -		goto after_immediate_data; +		target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd); +		return 1;  	}  	/*  	 * Call directly into transport_generic_new_cmd() to perform @@ -1059,11 +1112,27 @@ attach_cmd:  	 */  	cmd->sense_reason = transport_generic_new_cmd(&cmd->se_cmd);  	if (cmd->sense_reason) { -		immed_ret = IMMEDIATE_DATA_NORMAL_OPERATION; -		goto after_immediate_data; +		target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd); +		return 1;  	} -	immed_ret = iscsit_handle_immediate_data(cmd, buf, payload_length); +	return 0; +} +EXPORT_SYMBOL(iscsit_process_scsi_cmd); + +static int +iscsit_get_immediate_data(struct iscsi_cmd *cmd, struct iscsi_scsi_req *hdr, +			  bool dump_payload) +{ +	int cmdsn_ret = 0, immed_ret = IMMEDIATE_DATA_NORMAL_OPERATION; +	/* +	 * Special case for Unsupported SAM WRITE Opcodes and ImmediateData=Yes. +	 */ +	if (dump_payload == true) +		goto after_immediate_data; + +	immed_ret = iscsit_handle_immediate_data(cmd, hdr, +					cmd->first_burst_len);  after_immediate_data:  	if (immed_ret == IMMEDIATE_DATA_NORMAL_OPERATION) {  		/* @@ -1071,26 +1140,19 @@ after_immediate_data:  		 * DataCRC, check against ExpCmdSN/MaxCmdSN if  		 * Immediate Bit is not set.  		 */ -		cmdsn_ret = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn); -		/* -		 * Special case for Unsupported SAM WRITE Opcodes -		 * and ImmediateData=Yes. -		 */ +		cmdsn_ret = iscsit_sequence_cmd(cmd->conn, cmd, hdr->cmdsn); +  		if (cmd->sense_reason) { -			if (iscsit_dump_data_payload(conn, payload_length, 1) < 0) +			if (iscsit_dump_data_payload(cmd->conn, +					cmd->first_burst_len, 1) < 0)  				return -1; -		} else if (cmd->unsolicited_data) { -			iscsit_set_dataout_sequence_values(cmd); - -			spin_lock_bh(&cmd->dataout_timeout_lock); -			iscsit_start_dataout_timer(cmd, cmd->conn); -			spin_unlock_bh(&cmd->dataout_timeout_lock); -		} +		} else if (cmd->unsolicited_data) +			iscsit_set_unsoliticed_dataout(cmd);  		if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)  			return iscsit_add_reject_from_cmd(  				ISCSI_REASON_PROTOCOL_ERROR, -				1, 0, buf, cmd); +				1, 0, (unsigned char *)hdr, cmd);  	} else if (immed_ret == IMMEDIATE_DATA_ERL1_CRC_FAILURE) {  		/* @@ -1105,13 +1167,47 @@ after_immediate_data:  		 * CmdSN and issue a retry to plug the sequence.  		 */  		cmd->i_state = ISTATE_REMOVE; -		iscsit_add_cmd_to_immediate_queue(cmd, conn, cmd->i_state); +		iscsit_add_cmd_to_immediate_queue(cmd, cmd->conn, cmd->i_state);  	} else /* immed_ret == IMMEDIATE_DATA_CANNOT_RECOVER */  		return -1;  	return 0;  } +static int +iscsit_handle_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, +			   unsigned char *buf) +{ +	struct iscsi_scsi_req *hdr = (struct iscsi_scsi_req *)buf; +	int rc, immed_data; +	bool dump_payload = false; + +	rc = iscsit_setup_scsi_cmd(conn, cmd, buf); +	if (rc < 0) +		return rc; +	/* +	 * Allocation iovecs needed for struct socket operations for +	 * traditional iSCSI block I/O. +	 */ +	if (iscsit_allocate_iovecs(cmd) < 0) { +		return iscsit_add_reject_from_cmd( +				ISCSI_REASON_BOOKMARK_NO_RESOURCES, +				1, 0, buf, cmd); +	} +	immed_data = cmd->immediate_data; + +	rc = iscsit_process_scsi_cmd(conn, cmd, hdr); +	if (rc < 0) +		return rc; +	else if (rc > 0) +		dump_payload = true; + +	if (!immed_data) +		return 0; + +	return iscsit_get_immediate_data(cmd, hdr, dump_payload); +} +  static u32 iscsit_do_crypto_hash_sg(  	struct hash_desc *hash,  	struct iscsi_cmd *cmd, @@ -1174,20 +1270,16 @@ static void iscsit_do_crypto_hash_buf(  	crypto_hash_final(hash, data_crc);  } -static int iscsit_handle_data_out(struct iscsi_conn *conn, unsigned char *buf) +int +iscsit_check_dataout_hdr(struct iscsi_conn *conn, unsigned char *buf, +			  struct iscsi_cmd **out_cmd)  { -	int iov_ret, ooo_cmdsn = 0, ret; -	u8 data_crc_failed = 0; -	u32 checksum, iov_count = 0, padding = 0, rx_got = 0; -	u32 rx_size = 0, payload_length; +	struct iscsi_data *hdr = (struct iscsi_data *)buf;  	struct iscsi_cmd *cmd = NULL;  	struct se_cmd *se_cmd; -	struct iscsi_data *hdr; -	struct kvec *iov;  	unsigned long flags; - -	hdr			= (struct iscsi_data *) buf; -	payload_length		= ntoh24(hdr->dlength); +	u32 payload_length = ntoh24(hdr->dlength); +	int rc;  	if (!payload_length) {  		pr_err("DataOUT payload is ZERO, protocol error.\n"); @@ -1220,7 +1312,7 @@ static int iscsit_handle_data_out(struct iscsi_conn *conn, unsigned char *buf)  	pr_debug("Got DataOut ITT: 0x%08x, TTT: 0x%08x,"  		" DataSN: 0x%08x, Offset: %u, Length: %u, CID: %hu\n", -		hdr->itt, hdr->ttt, hdr->datasn, hdr->offset, +		hdr->itt, hdr->ttt, hdr->datasn, ntohl(hdr->offset),  		payload_length, conn->cid);  	if (cmd->cmd_flags & ICF_GOT_LAST_DATAOUT) { @@ -1312,12 +1404,26 @@ static int iscsit_handle_data_out(struct iscsi_conn *conn, unsigned char *buf)  	 * Preform DataSN, DataSequenceInOrder, DataPDUInOrder, and  	 * within-command recovery checks before receiving the payload.  	 */ -	ret = iscsit_check_pre_dataout(cmd, buf); -	if (ret == DATAOUT_WITHIN_COMMAND_RECOVERY) +	rc = iscsit_check_pre_dataout(cmd, buf); +	if (rc == DATAOUT_WITHIN_COMMAND_RECOVERY)  		return 0; -	else if (ret == DATAOUT_CANNOT_RECOVER) +	else if (rc == DATAOUT_CANNOT_RECOVER)  		return -1; +	*out_cmd = cmd; +	return 0; +} +EXPORT_SYMBOL(iscsit_check_dataout_hdr); + +static int +iscsit_get_dataout(struct iscsi_conn *conn, struct iscsi_cmd *cmd, +		   struct iscsi_data *hdr) +{ +	struct kvec *iov; +	u32 checksum, iov_count = 0, padding = 0, rx_got = 0, rx_size = 0; +	u32 payload_length = ntoh24(hdr->dlength); +	int iov_ret, data_crc_failed = 0; +  	rx_size += payload_length;  	iov = &cmd->iov_data[0]; @@ -1370,17 +1476,27 @@ static int iscsit_handle_data_out(struct iscsi_conn *conn, unsigned char *buf)  				payload_length);  		}  	} + +	return data_crc_failed; +} + +int +iscsit_check_dataout_payload(struct iscsi_cmd *cmd, struct iscsi_data *hdr, +			     bool data_crc_failed) +{ +	struct iscsi_conn *conn = cmd->conn; +	int rc, ooo_cmdsn;  	/*  	 * Increment post receive data and CRC values or perform  	 * within-command recovery.  	 */ -	ret = iscsit_check_post_dataout(cmd, buf, data_crc_failed); -	if ((ret == DATAOUT_NORMAL) || (ret == DATAOUT_WITHIN_COMMAND_RECOVERY)) +	rc = iscsit_check_post_dataout(cmd, (unsigned char *)hdr, data_crc_failed); +	if ((rc == DATAOUT_NORMAL) || (rc == DATAOUT_WITHIN_COMMAND_RECOVERY))  		return 0; -	else if (ret == DATAOUT_SEND_R2T) { +	else if (rc == DATAOUT_SEND_R2T) {  		iscsit_set_dataout_sequence_values(cmd); -		iscsit_build_r2ts_for_cmd(cmd, conn, false); -	} else if (ret == DATAOUT_SEND_TO_TRANSPORT) { +		conn->conn_transport->iscsit_get_dataout(conn, cmd, false); +	} else if (rc == DATAOUT_SEND_TO_TRANSPORT) {  		/*  		 * Handle extra special case for out of order  		 * Unsolicited Data Out. @@ -1401,15 +1517,37 @@ static int iscsit_handle_data_out(struct iscsi_conn *conn, unsigned char *buf)  	return 0;  } +EXPORT_SYMBOL(iscsit_check_dataout_payload); -static int iscsit_handle_nop_out( -	struct iscsi_conn *conn, -	unsigned char *buf) +static int iscsit_handle_data_out(struct iscsi_conn *conn, unsigned char *buf) +{ +	struct iscsi_cmd *cmd; +	struct iscsi_data *hdr = (struct iscsi_data *)buf; +	int rc; +	bool data_crc_failed = false; + +	rc = iscsit_check_dataout_hdr(conn, buf, &cmd); +	if (rc < 0) +		return rc; +	else if (!cmd) +		return 0; + +	rc = iscsit_get_dataout(conn, cmd, hdr); +	if (rc < 0) +		return rc; +	else if (rc > 0) +		data_crc_failed = true; + +	return iscsit_check_dataout_payload(cmd, hdr, data_crc_failed); +} + +int iscsit_handle_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd, +			unsigned char *buf)  {  	unsigned char *ping_data = NULL;  	int cmdsn_ret, niov = 0, ret = 0, rx_got, rx_size;  	u32 checksum, data_crc, padding = 0, payload_length; -	struct iscsi_cmd *cmd = NULL; +	struct iscsi_cmd *cmd_p = NULL;  	struct kvec *iov = NULL;  	struct iscsi_nopout *hdr; @@ -1432,7 +1570,7 @@ static int iscsit_handle_nop_out(  					buf, conn);  	} -	pr_debug("Got NOPOUT Ping %s ITT: 0x%08x, TTT: 0x%09x," +	pr_debug("Got NOPOUT Ping %s ITT: 0x%08x, TTT: 0x%08x,"  		" CmdSN: 0x%08x, ExpStatSN: 0x%08x, Length: %u\n",  		hdr->itt == RESERVED_ITT ? "Response" : "Request",  		hdr->itt, hdr->ttt, hdr->cmdsn, hdr->exp_statsn, @@ -1445,7 +1583,6 @@ static int iscsit_handle_nop_out(  	 * can contain ping data.  	 */  	if (hdr->ttt == cpu_to_be32(0xFFFFFFFF)) { -		cmd = iscsit_allocate_cmd(conn, GFP_KERNEL);  		if (!cmd)  			return iscsit_add_reject(  					ISCSI_REASON_BOOKMARK_NO_RESOURCES, @@ -1580,14 +1717,14 @@ static int iscsit_handle_nop_out(  		/*  		 * This was a response to a unsolicited NOPIN ping.  		 */ -		cmd = iscsit_find_cmd_from_ttt(conn, be32_to_cpu(hdr->ttt)); -		if (!cmd) +		cmd_p = iscsit_find_cmd_from_ttt(conn, be32_to_cpu(hdr->ttt)); +		if (!cmd_p)  			return -1;  		iscsit_stop_nopin_response_timer(conn); -		cmd->i_state = ISTATE_REMOVE; -		iscsit_add_cmd_to_immediate_queue(cmd, conn, cmd->i_state); +		cmd_p->i_state = ISTATE_REMOVE; +		iscsit_add_cmd_to_immediate_queue(cmd_p, conn, cmd_p->i_state);  		iscsit_start_nopin_timer(conn);  	} else {  		/* @@ -1611,12 +1748,12 @@ ping_out:  	kfree(ping_data);  	return ret;  } +EXPORT_SYMBOL(iscsit_handle_nop_out); -static int iscsit_handle_task_mgt_cmd( -	struct iscsi_conn *conn, -	unsigned char *buf) +int +iscsit_handle_task_mgt_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, +			   unsigned char *buf)  { -	struct iscsi_cmd *cmd;  	struct se_tmr_req *se_tmr;  	struct iscsi_tmr_req *tmr_req;  	struct iscsi_tm *hdr; @@ -1645,18 +1782,13 @@ static int iscsit_handle_task_mgt_cmd(  		pr_err("Task Management Request TASK_REASSIGN not"  			" issued as immediate command, bad iSCSI Initiator"  				"implementation\n"); -		return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1, -					buf, conn); +		return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR, +					1, 1, buf, cmd);  	}  	if ((function != ISCSI_TM_FUNC_ABORT_TASK) &&  	    be32_to_cpu(hdr->refcmdsn) != ISCSI_RESERVED_TAG)  		hdr->refcmdsn = cpu_to_be32(ISCSI_RESERVED_TAG); -	cmd = iscsit_allocate_cmd(conn, GFP_KERNEL); -	if (!cmd) -		return iscsit_add_reject(ISCSI_REASON_BOOKMARK_NO_RESOURCES, -					 1, buf, conn); -  	cmd->data_direction = DMA_NONE;  	cmd->tmr_req = kzalloc(sizeof(struct iscsi_tmr_req), GFP_KERNEL); @@ -1827,6 +1959,7 @@ attach:  	iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);  	return 0;  } +EXPORT_SYMBOL(iscsit_handle_task_mgt_cmd);  /* #warning FIXME: Support Text Command parameters besides SendTargets */  static int iscsit_handle_text_cmd( @@ -2089,13 +2222,12 @@ int iscsit_logout_removeconnforrecovery(struct iscsi_cmd *cmd, struct iscsi_conn  	return 0;  } -static int iscsit_handle_logout_cmd( -	struct iscsi_conn *conn, -	unsigned char *buf) +int +iscsit_handle_logout_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, +			unsigned char *buf)  {  	int cmdsn_ret, logout_remove = 0;  	u8 reason_code = 0; -	struct iscsi_cmd *cmd;  	struct iscsi_logout *hdr;  	struct iscsi_tiqn *tiqn = iscsit_snmp_get_tiqn(conn); @@ -2119,14 +2251,10 @@ static int iscsit_handle_logout_cmd(  	if (conn->conn_state != TARG_CONN_STATE_LOGGED_IN) {  		pr_err("Received logout request on connection that"  			" is not in logged in state, ignoring request.\n"); +		iscsit_release_cmd(cmd);  		return 0;  	} -	cmd = iscsit_allocate_cmd(conn, GFP_KERNEL); -	if (!cmd) -		return iscsit_add_reject(ISCSI_REASON_BOOKMARK_NO_RESOURCES, 1, -					buf, conn); -  	cmd->iscsi_opcode       = ISCSI_OP_LOGOUT;  	cmd->i_state            = ISTATE_SEND_LOGOUTRSP;  	cmd->immediate_cmd      = ((hdr->opcode & ISCSI_OP_IMMEDIATE) ? 1 : 0); @@ -2176,6 +2304,7 @@ static int iscsit_handle_logout_cmd(  	return logout_remove;  } +EXPORT_SYMBOL(iscsit_handle_logout_cmd);  static int iscsit_handle_snack(  	struct iscsi_conn *conn, @@ -2243,7 +2372,7 @@ static void iscsit_rx_thread_wait_for_tcp(struct iscsi_conn *conn)  static int iscsit_handle_immediate_data(  	struct iscsi_cmd *cmd, -	unsigned char *buf, +	struct iscsi_scsi_req *hdr,  	u32 length)  {  	int iov_ret, rx_got = 0, rx_size = 0; @@ -2299,12 +2428,12 @@ static int iscsit_handle_immediate_data(  					" in ERL=0.\n");  				iscsit_add_reject_from_cmd(  						ISCSI_REASON_DATA_DIGEST_ERROR, -						1, 0, buf, cmd); +						1, 0, (unsigned char *)hdr, cmd);  				return IMMEDIATE_DATA_CANNOT_RECOVER;  			} else {  				iscsit_add_reject_from_cmd(  						ISCSI_REASON_DATA_DIGEST_ERROR, -						0, 0, buf, cmd); +						0, 0, (unsigned char *)hdr, cmd);  				return IMMEDIATE_DATA_ERL1_CRC_FAILURE;  			}  		} else { @@ -2424,18 +2553,60 @@ static void iscsit_tx_thread_wait_for_tcp(struct iscsi_conn *conn)  	}  } -static int iscsit_send_data_in( -	struct iscsi_cmd *cmd, -	struct iscsi_conn *conn) +static void +iscsit_build_datain_pdu(struct iscsi_cmd *cmd, struct iscsi_conn *conn, +			struct iscsi_datain *datain, struct iscsi_data_rsp *hdr, +			bool set_statsn)  { -	int iov_ret = 0, set_statsn = 0; -	u32 iov_count = 0, tx_size = 0; +	hdr->opcode		= ISCSI_OP_SCSI_DATA_IN; +	hdr->flags		= datain->flags; +	if (hdr->flags & ISCSI_FLAG_DATA_STATUS) { +		if (cmd->se_cmd.se_cmd_flags & SCF_OVERFLOW_BIT) { +			hdr->flags |= ISCSI_FLAG_DATA_OVERFLOW; +			hdr->residual_count = cpu_to_be32(cmd->se_cmd.residual_count); +		} else if (cmd->se_cmd.se_cmd_flags & SCF_UNDERFLOW_BIT) { +			hdr->flags |= ISCSI_FLAG_DATA_UNDERFLOW; +			hdr->residual_count = cpu_to_be32(cmd->se_cmd.residual_count); +		} +	} +	hton24(hdr->dlength, datain->length); +	if (hdr->flags & ISCSI_FLAG_DATA_ACK) +		int_to_scsilun(cmd->se_cmd.orig_fe_lun, +				(struct scsi_lun *)&hdr->lun); +	else +		put_unaligned_le64(0xFFFFFFFFFFFFFFFFULL, &hdr->lun); + +	hdr->itt		= cmd->init_task_tag; + +	if (hdr->flags & ISCSI_FLAG_DATA_ACK) +		hdr->ttt		= cpu_to_be32(cmd->targ_xfer_tag); +	else +		hdr->ttt		= cpu_to_be32(0xFFFFFFFF); +	if (set_statsn) +		hdr->statsn		= cpu_to_be32(cmd->stat_sn); +	else +		hdr->statsn		= cpu_to_be32(0xFFFFFFFF); + +	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn); +	hdr->max_cmdsn		= cpu_to_be32(conn->sess->max_cmd_sn); +	hdr->datasn		= cpu_to_be32(datain->data_sn); +	hdr->offset		= cpu_to_be32(datain->offset); + +	pr_debug("Built DataIN ITT: 0x%08x, StatSN: 0x%08x," +		" DataSN: 0x%08x, Offset: %u, Length: %u, CID: %hu\n", +		cmd->init_task_tag, ntohl(hdr->statsn), ntohl(hdr->datasn), +		ntohl(hdr->offset), datain->length, conn->cid); +} + +static int iscsit_send_datain(struct iscsi_cmd *cmd, struct iscsi_conn *conn) +{ +	struct iscsi_data_rsp *hdr = (struct iscsi_data_rsp *)&cmd->pdu[0];  	struct iscsi_datain datain;  	struct iscsi_datain_req *dr; -	struct iscsi_data_rsp *hdr;  	struct kvec *iov; -	int eodr = 0; -	int ret; +	u32 iov_count = 0, tx_size = 0; +	int eodr = 0, ret, iov_ret; +	bool set_statsn = false;  	memset(&datain, 0, sizeof(struct iscsi_datain));  	dr = iscsit_get_datain_values(cmd, &datain); @@ -2444,7 +2615,6 @@ static int iscsit_send_data_in(  				cmd->init_task_tag);  		return -1;  	} -  	/*  	 * Be paranoid and double check the logic for now.  	 */ @@ -2452,7 +2622,7 @@ static int iscsit_send_data_in(  		pr_err("Command ITT: 0x%08x, datain.offset: %u and"  			" datain.length: %u exceeds cmd->data_length: %u\n",  			cmd->init_task_tag, datain.offset, datain.length, -				cmd->se_cmd.data_length); +			cmd->se_cmd.data_length);  		return -1;  	} @@ -2476,47 +2646,13 @@ static int iscsit_send_data_in(  		    (dr->dr_complete == DATAIN_COMPLETE_CONNECTION_RECOVERY)) {  			iscsit_increment_maxcmdsn(cmd, conn->sess);  			cmd->stat_sn = conn->stat_sn++; -			set_statsn = 1; +			set_statsn = true;  		} else if (dr->dr_complete == -				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY) -			set_statsn = 1; +			   DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY) +			set_statsn = true;  	} -	hdr	= (struct iscsi_data_rsp *) cmd->pdu; -	memset(hdr, 0, ISCSI_HDR_LEN); -	hdr->opcode		= ISCSI_OP_SCSI_DATA_IN; -	hdr->flags		= datain.flags; -	if (hdr->flags & ISCSI_FLAG_DATA_STATUS) { -		if (cmd->se_cmd.se_cmd_flags & SCF_OVERFLOW_BIT) { -			hdr->flags |= ISCSI_FLAG_DATA_OVERFLOW; -			hdr->residual_count = cpu_to_be32(cmd->se_cmd.residual_count); -		} else if (cmd->se_cmd.se_cmd_flags & SCF_UNDERFLOW_BIT) { -			hdr->flags |= ISCSI_FLAG_DATA_UNDERFLOW; -			hdr->residual_count = cpu_to_be32(cmd->se_cmd.residual_count); -		} -	} -	hton24(hdr->dlength, datain.length); -	if (hdr->flags & ISCSI_FLAG_DATA_ACK) -		int_to_scsilun(cmd->se_cmd.orig_fe_lun, -				(struct scsi_lun *)&hdr->lun); -	else -		put_unaligned_le64(0xFFFFFFFFFFFFFFFFULL, &hdr->lun); - -	hdr->itt		= cmd->init_task_tag; - -	if (hdr->flags & ISCSI_FLAG_DATA_ACK) -		hdr->ttt		= cpu_to_be32(cmd->targ_xfer_tag); -	else -		hdr->ttt		= cpu_to_be32(0xFFFFFFFF); -	if (set_statsn) -		hdr->statsn		= cpu_to_be32(cmd->stat_sn); -	else -		hdr->statsn		= cpu_to_be32(0xFFFFFFFF); - -	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn); -	hdr->max_cmdsn		= cpu_to_be32(conn->sess->max_cmd_sn); -	hdr->datasn		= cpu_to_be32(datain.data_sn); -	hdr->offset		= cpu_to_be32(datain.offset); +	iscsit_build_datain_pdu(cmd, conn, &datain, hdr, set_statsn);  	iov = &cmd->iov_data[0];  	iov[iov_count].iov_base	= cmd->pdu; @@ -2527,7 +2663,7 @@ static int iscsit_send_data_in(  		u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];  		iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, -				(unsigned char *)hdr, ISCSI_HDR_LEN, +				(unsigned char *)cmd->pdu, ISCSI_HDR_LEN,  				0, NULL, (u8 *)header_digest);  		iov[0].iov_len += ISCSI_CRC_LEN; @@ -2537,7 +2673,8 @@ static int iscsit_send_data_in(  			" for DataIN PDU 0x%08x\n", *header_digest);  	} -	iov_ret = iscsit_map_iovec(cmd, &cmd->iov_data[1], datain.offset, datain.length); +	iov_ret = iscsit_map_iovec(cmd, &cmd->iov_data[1], +				datain.offset, datain.length);  	if (iov_ret < 0)  		return -1; @@ -2568,11 +2705,6 @@ static int iscsit_send_data_in(  	cmd->iov_data_count = iov_count;  	cmd->tx_size = tx_size; -	pr_debug("Built DataIN ITT: 0x%08x, StatSN: 0x%08x," -		" DataSN: 0x%08x, Offset: %u, Length: %u, CID: %hu\n", -		cmd->init_task_tag, ntohl(hdr->statsn), ntohl(hdr->datasn), -		ntohl(hdr->offset), datain.length, conn->cid); -  	/* sendpage is preferred but can't insert markers */  	if (!conn->conn_ops->IFMarker)  		ret = iscsit_fe_sendpage_sg(cmd, conn); @@ -2595,16 +2727,13 @@ static int iscsit_send_data_in(  	return eodr;  } -static int iscsit_send_logout_response( -	struct iscsi_cmd *cmd, -	struct iscsi_conn *conn) +int +iscsit_build_logout_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn, +			struct iscsi_logout_rsp *hdr)  { -	int niov = 0, tx_size;  	struct iscsi_conn *logout_conn = NULL;  	struct iscsi_conn_recovery *cr = NULL;  	struct iscsi_session *sess = conn->sess; -	struct kvec *iov; -	struct iscsi_logout_rsp *hdr;  	/*  	 * The actual shutting down of Sessions and/or Connections  	 * for CLOSESESSION and CLOSECONNECTION Logout Requests @@ -2673,9 +2802,6 @@ static int iscsit_send_logout_response(  		return -1;  	} -	tx_size = ISCSI_HDR_LEN; -	hdr			= (struct iscsi_logout_rsp *)cmd->pdu; -	memset(hdr, 0, ISCSI_HDR_LEN);  	hdr->opcode		= ISCSI_OP_LOGOUT_RSP;  	hdr->flags		|= ISCSI_FLAG_CMD_FINAL;  	hdr->response		= cmd->logout_response; @@ -2687,6 +2813,27 @@ static int iscsit_send_logout_response(  	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);  	hdr->max_cmdsn		= cpu_to_be32(conn->sess->max_cmd_sn); +	pr_debug("Built Logout Response ITT: 0x%08x StatSN:" +		" 0x%08x Response: 0x%02x CID: %hu on CID: %hu\n", +		cmd->init_task_tag, cmd->stat_sn, hdr->response, +		cmd->logout_cid, conn->cid); + +	return 0; +} +EXPORT_SYMBOL(iscsit_build_logout_rsp); + +static int +iscsit_send_logout(struct iscsi_cmd *cmd, struct iscsi_conn *conn) +{ +	struct kvec *iov; +	int niov = 0, tx_size, rc; + +	rc = iscsit_build_logout_rsp(cmd, conn, +			(struct iscsi_logout_rsp *)&cmd->pdu[0]); +	if (rc < 0) +		return rc; + +	tx_size = ISCSI_HDR_LEN;  	iov = &cmd->iov_misc[0];  	iov[niov].iov_base	= cmd->pdu;  	iov[niov++].iov_len	= ISCSI_HDR_LEN; @@ -2695,7 +2842,7 @@ static int iscsit_send_logout_response(  		u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];  		iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, -				(unsigned char *)hdr, ISCSI_HDR_LEN, +				(unsigned char *)&cmd->pdu[0], ISCSI_HDR_LEN,  				0, NULL, (u8 *)header_digest);  		iov[0].iov_len += ISCSI_CRC_LEN; @@ -2706,14 +2853,37 @@ static int iscsit_send_logout_response(  	cmd->iov_misc_count = niov;  	cmd->tx_size = tx_size; -	pr_debug("Sending Logout Response ITT: 0x%08x StatSN:" -		" 0x%08x Response: 0x%02x CID: %hu on CID: %hu\n", -		cmd->init_task_tag, cmd->stat_sn, hdr->response, -		cmd->logout_cid, conn->cid); -  	return 0;  } +void +iscsit_build_nopin_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn, +		       struct iscsi_nopin *hdr, bool nopout_response) +{ +	hdr->opcode		= ISCSI_OP_NOOP_IN; +	hdr->flags		|= ISCSI_FLAG_CMD_FINAL; +        hton24(hdr->dlength, cmd->buf_ptr_size); +	if (nopout_response) +		put_unaligned_le64(0xFFFFFFFFFFFFFFFFULL, &hdr->lun); +	hdr->itt		= cmd->init_task_tag; +	hdr->ttt		= cpu_to_be32(cmd->targ_xfer_tag); +	cmd->stat_sn		= (nopout_response) ? conn->stat_sn++ : +				  conn->stat_sn; +	hdr->statsn		= cpu_to_be32(cmd->stat_sn); + +	if (nopout_response) +		iscsit_increment_maxcmdsn(cmd, conn->sess); + +	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn); +	hdr->max_cmdsn		= cpu_to_be32(conn->sess->max_cmd_sn); + +	pr_debug("Built NOPIN %s Response ITT: 0x%08x, TTT: 0x%08x," +		" StatSN: 0x%08x, Length %u\n", (nopout_response) ? +		"Solicitied" : "Unsolicitied", cmd->init_task_tag, +		cmd->targ_xfer_tag, cmd->stat_sn, cmd->buf_ptr_size); +} +EXPORT_SYMBOL(iscsit_build_nopin_rsp); +  /*   *	Unsolicited NOPIN, either requesting a response or not.   */ @@ -2722,20 +2892,10 @@ static int iscsit_send_unsolicited_nopin(  	struct iscsi_conn *conn,  	int want_response)  { -	int tx_size = ISCSI_HDR_LEN; -	struct iscsi_nopin *hdr; -	int ret; +	struct iscsi_nopin *hdr = (struct iscsi_nopin *)&cmd->pdu[0]; +	int tx_size = ISCSI_HDR_LEN, ret; -	hdr			= (struct iscsi_nopin *) cmd->pdu; -	memset(hdr, 0, ISCSI_HDR_LEN); -	hdr->opcode		= ISCSI_OP_NOOP_IN; -	hdr->flags		|= ISCSI_FLAG_CMD_FINAL; -	hdr->itt		= cmd->init_task_tag; -	hdr->ttt		= cpu_to_be32(cmd->targ_xfer_tag); -	cmd->stat_sn		= conn->stat_sn; -	hdr->statsn		= cpu_to_be32(cmd->stat_sn); -	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn); -	hdr->max_cmdsn		= cpu_to_be32(conn->sess->max_cmd_sn); +	iscsit_build_nopin_rsp(cmd, conn, hdr, false);  	if (conn->conn_ops->HeaderDigest) {  		u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN]; @@ -2771,31 +2931,17 @@ static int iscsit_send_unsolicited_nopin(  	return 0;  } -static int iscsit_send_nopin_response( -	struct iscsi_cmd *cmd, -	struct iscsi_conn *conn) +static int +iscsit_send_nopin(struct iscsi_cmd *cmd, struct iscsi_conn *conn)  { -	int niov = 0, tx_size; -	u32 padding = 0; +	struct iscsi_nopin *hdr = (struct iscsi_nopin *)&cmd->pdu[0];  	struct kvec *iov; -	struct iscsi_nopin *hdr; - -	tx_size = ISCSI_HDR_LEN; -	hdr			= (struct iscsi_nopin *) cmd->pdu; -	memset(hdr, 0, ISCSI_HDR_LEN); -	hdr->opcode		= ISCSI_OP_NOOP_IN; -	hdr->flags		|= ISCSI_FLAG_CMD_FINAL; -	hton24(hdr->dlength, cmd->buf_ptr_size); -	put_unaligned_le64(0xFFFFFFFFFFFFFFFFULL, &hdr->lun); -	hdr->itt		= cmd->init_task_tag; -	hdr->ttt		= cpu_to_be32(cmd->targ_xfer_tag); -	cmd->stat_sn		= conn->stat_sn++; -	hdr->statsn		= cpu_to_be32(cmd->stat_sn); +	u32 padding = 0; +	int niov = 0, tx_size; -	iscsit_increment_maxcmdsn(cmd, conn->sess); -	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn); -	hdr->max_cmdsn		= cpu_to_be32(conn->sess->max_cmd_sn); +	iscsit_build_nopin_rsp(cmd, conn, hdr, true); +	tx_size = ISCSI_HDR_LEN;  	iov = &cmd->iov_misc[0];  	iov[niov].iov_base	= cmd->pdu;  	iov[niov++].iov_len	= ISCSI_HDR_LEN; @@ -2851,10 +2997,6 @@ static int iscsit_send_nopin_response(  	cmd->iov_misc_count = niov;  	cmd->tx_size = tx_size; -	pr_debug("Sending NOPIN Response ITT: 0x%08x, TTT:" -		" 0x%08x, StatSN: 0x%08x, Length %u\n", cmd->init_task_tag, -		cmd->targ_xfer_tag, cmd->stat_sn, cmd->buf_ptr_size); -  	return 0;  } @@ -2939,8 +3081,8 @@ static int iscsit_send_r2t(   *		connection recovery.   */  int iscsit_build_r2ts_for_cmd( -	struct iscsi_cmd *cmd,  	struct iscsi_conn *conn, +	struct iscsi_cmd *cmd,  	bool recovery)  {  	int first_r2t = 1; @@ -3015,24 +3157,16 @@ int iscsit_build_r2ts_for_cmd(  	return 0;  } -static int iscsit_send_status( -	struct iscsi_cmd *cmd, -	struct iscsi_conn *conn) +void iscsit_build_rsp_pdu(struct iscsi_cmd *cmd, struct iscsi_conn *conn, +			bool inc_stat_sn, struct iscsi_scsi_rsp *hdr)  { -	u8 iov_count = 0, recovery; -	u32 padding = 0, tx_size = 0; -	struct iscsi_scsi_rsp *hdr; -	struct kvec *iov; - -	recovery = (cmd->i_state != ISTATE_SEND_STATUS); -	if (!recovery) +	if (inc_stat_sn)  		cmd->stat_sn = conn->stat_sn++;  	spin_lock_bh(&conn->sess->session_stats_lock);  	conn->sess->rsp_pdus++;  	spin_unlock_bh(&conn->sess->session_stats_lock); -	hdr			= (struct iscsi_scsi_rsp *) cmd->pdu;  	memset(hdr, 0, ISCSI_HDR_LEN);  	hdr->opcode		= ISCSI_OP_SCSI_CMD_RSP;  	hdr->flags		|= ISCSI_FLAG_CMD_FINAL; @@ -3052,6 +3186,23 @@ static int iscsit_send_status(  	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);  	hdr->max_cmdsn		= cpu_to_be32(conn->sess->max_cmd_sn); +	pr_debug("Built SCSI Response, ITT: 0x%08x, StatSN: 0x%08x," +		" Response: 0x%02x, SAM Status: 0x%02x, CID: %hu\n", +		cmd->init_task_tag, cmd->stat_sn, cmd->se_cmd.scsi_status, +		cmd->se_cmd.scsi_status, conn->cid); +} +EXPORT_SYMBOL(iscsit_build_rsp_pdu); + +static int iscsit_send_response(struct iscsi_cmd *cmd, struct iscsi_conn *conn) +{ +	struct iscsi_scsi_rsp *hdr = (struct iscsi_scsi_rsp *)&cmd->pdu[0]; +	struct kvec *iov; +	u32 padding = 0, tx_size = 0; +	int iov_count = 0; +	bool inc_stat_sn = (cmd->i_state == ISTATE_SEND_STATUS); + +	iscsit_build_rsp_pdu(cmd, conn, inc_stat_sn, hdr); +  	iov = &cmd->iov_misc[0];  	iov[iov_count].iov_base	= cmd->pdu;  	iov[iov_count++].iov_len = ISCSI_HDR_LEN; @@ -3106,7 +3257,7 @@ static int iscsit_send_status(  		u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];  		iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, -				(unsigned char *)hdr, ISCSI_HDR_LEN, +				(unsigned char *)cmd->pdu, ISCSI_HDR_LEN,  				0, NULL, (u8 *)header_digest);  		iov[0].iov_len += ISCSI_CRC_LEN; @@ -3118,11 +3269,6 @@ static int iscsit_send_status(  	cmd->iov_misc_count = iov_count;  	cmd->tx_size = tx_size; -	pr_debug("Built %sSCSI Response, ITT: 0x%08x, StatSN: 0x%08x," -		" Response: 0x%02x, SAM Status: 0x%02x, CID: %hu\n", -		(!recovery) ? "" : "Recovery ", cmd->init_task_tag, -		cmd->stat_sn, 0x00, cmd->se_cmd.scsi_status, conn->cid); -  	return 0;  } @@ -3145,16 +3291,12 @@ static u8 iscsit_convert_tcm_tmr_rsp(struct se_tmr_req *se_tmr)  	}  } -static int iscsit_send_task_mgt_rsp( -	struct iscsi_cmd *cmd, -	struct iscsi_conn *conn) +void +iscsit_build_task_mgt_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn, +			  struct iscsi_tm_rsp *hdr)  {  	struct se_tmr_req *se_tmr = cmd->se_cmd.se_tmr_req; -	struct iscsi_tm_rsp *hdr; -	u32 tx_size = 0; -	hdr			= (struct iscsi_tm_rsp *) cmd->pdu; -	memset(hdr, 0, ISCSI_HDR_LEN);  	hdr->opcode		= ISCSI_OP_SCSI_TMFUNC_RSP;  	hdr->flags		= ISCSI_FLAG_CMD_FINAL;  	hdr->response		= iscsit_convert_tcm_tmr_rsp(se_tmr); @@ -3166,6 +3308,20 @@ static int iscsit_send_task_mgt_rsp(  	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);  	hdr->max_cmdsn		= cpu_to_be32(conn->sess->max_cmd_sn); +	pr_debug("Built Task Management Response ITT: 0x%08x," +		" StatSN: 0x%08x, Response: 0x%02x, CID: %hu\n", +		cmd->init_task_tag, cmd->stat_sn, hdr->response, conn->cid); +} +EXPORT_SYMBOL(iscsit_build_task_mgt_rsp); + +static int +iscsit_send_task_mgt_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn) +{ +	struct iscsi_tm_rsp *hdr = (struct iscsi_tm_rsp *)&cmd->pdu[0]; +	u32 tx_size = 0; + +	iscsit_build_task_mgt_rsp(cmd, conn, hdr); +  	cmd->iov_misc[0].iov_base	= cmd->pdu;  	cmd->iov_misc[0].iov_len	= ISCSI_HDR_LEN;  	tx_size += ISCSI_HDR_LEN; @@ -3186,10 +3342,6 @@ static int iscsit_send_task_mgt_rsp(  	cmd->iov_misc_count = 1;  	cmd->tx_size = tx_size; -	pr_debug("Built Task Management Response ITT: 0x%08x," -		" StatSN: 0x%08x, Response: 0x%02x, CID: %hu\n", -		cmd->init_task_tag, cmd->stat_sn, hdr->response, conn->cid); -  	return 0;  } @@ -3385,6 +3537,22 @@ static int iscsit_send_text_rsp(  	return 0;  } +void +iscsit_build_reject(struct iscsi_cmd *cmd, struct iscsi_conn *conn, +		    struct iscsi_reject *hdr) +{ +	hdr->opcode		= ISCSI_OP_REJECT; +	hdr->flags		|= ISCSI_FLAG_CMD_FINAL; +	hton24(hdr->dlength, ISCSI_HDR_LEN); +	hdr->ffffffff		= cpu_to_be32(0xffffffff); +	cmd->stat_sn		= conn->stat_sn++; +	hdr->statsn		= cpu_to_be32(cmd->stat_sn); +	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn); +	hdr->max_cmdsn		= cpu_to_be32(conn->sess->max_cmd_sn); + +} +EXPORT_SYMBOL(iscsit_build_reject); +  static int iscsit_send_reject(  	struct iscsi_cmd *cmd,  	struct iscsi_conn *conn) @@ -3393,18 +3561,9 @@ static int iscsit_send_reject(  	struct iscsi_reject *hdr;  	struct kvec *iov; -	hdr			= (struct iscsi_reject *) cmd->pdu; -	hdr->opcode		= ISCSI_OP_REJECT; -	hdr->flags		|= ISCSI_FLAG_CMD_FINAL; -	hton24(hdr->dlength, ISCSI_HDR_LEN); -	hdr->ffffffff		= cpu_to_be32(0xffffffff); -	cmd->stat_sn		= conn->stat_sn++; -	hdr->statsn		= cpu_to_be32(cmd->stat_sn); -	hdr->exp_cmdsn	= cpu_to_be32(conn->sess->exp_cmd_sn); -	hdr->max_cmdsn	= cpu_to_be32(conn->sess->max_cmd_sn); +	iscsit_build_reject(cmd, conn, (struct iscsi_reject *)&cmd->pdu[0]);  	iov = &cmd->iov_misc[0]; -  	iov[iov_count].iov_base = cmd->pdu;  	iov[iov_count++].iov_len = ISCSI_HDR_LEN;  	iov[iov_count].iov_base = cmd->buf_ptr; @@ -3501,55 +3660,41 @@ static inline void iscsit_thread_check_cpumask(  	set_cpus_allowed_ptr(p, conn->conn_cpumask);  } -static int handle_immediate_queue(struct iscsi_conn *conn) +static int +iscsit_immediate_queue(struct iscsi_conn *conn, struct iscsi_cmd *cmd, int state)  { -	struct iscsi_queue_req *qr; -	struct iscsi_cmd *cmd; -	u8 state;  	int ret; -	while ((qr = iscsit_get_cmd_from_immediate_queue(conn))) { -		atomic_set(&conn->check_immediate_queue, 0); -		cmd = qr->cmd; -		state = qr->state; -		kmem_cache_free(lio_qr_cache, qr); - -		switch (state) { -		case ISTATE_SEND_R2T: -			ret = iscsit_send_r2t(cmd, conn); -			if (ret < 0) -				goto err; -			break; -		case ISTATE_REMOVE: -			if (cmd->data_direction == DMA_TO_DEVICE) -				iscsit_stop_dataout_timer(cmd); - -			spin_lock_bh(&conn->cmd_lock); -			list_del(&cmd->i_conn_node); -			spin_unlock_bh(&conn->cmd_lock); +	switch (state) { +	case ISTATE_SEND_R2T: +		ret = iscsit_send_r2t(cmd, conn); +		if (ret < 0) +			goto err; +		break; +	case ISTATE_REMOVE: +		spin_lock_bh(&conn->cmd_lock); +		list_del(&cmd->i_conn_node); +		spin_unlock_bh(&conn->cmd_lock); -			iscsit_free_cmd(cmd); -			continue; -		case ISTATE_SEND_NOPIN_WANT_RESPONSE: -			iscsit_mod_nopin_response_timer(conn); -			ret = iscsit_send_unsolicited_nopin(cmd, -							    conn, 1); -			if (ret < 0) -				goto err; -			break; -		case ISTATE_SEND_NOPIN_NO_RESPONSE: -			ret = iscsit_send_unsolicited_nopin(cmd, -							    conn, 0); -			if (ret < 0) -				goto err; -			break; -		default: -			pr_err("Unknown Opcode: 0x%02x ITT:" -			       " 0x%08x, i_state: %d on CID: %hu\n", -			       cmd->iscsi_opcode, cmd->init_task_tag, state, -			       conn->cid); +		iscsit_free_cmd(cmd); +		break; +	case ISTATE_SEND_NOPIN_WANT_RESPONSE: +		iscsit_mod_nopin_response_timer(conn); +		ret = iscsit_send_unsolicited_nopin(cmd, conn, 1); +		if (ret < 0)  			goto err; -		} +		break; +	case ISTATE_SEND_NOPIN_NO_RESPONSE: +		ret = iscsit_send_unsolicited_nopin(cmd, conn, 0); +		if (ret < 0) +			goto err; +		break; +	default: +		pr_err("Unknown Opcode: 0x%02x ITT:" +		       " 0x%08x, i_state: %d on CID: %hu\n", +		       cmd->iscsi_opcode, cmd->init_task_tag, state, +		       conn->cid); +		goto err;  	}  	return 0; @@ -3558,128 +3703,143 @@ err:  	return -1;  } -static int handle_response_queue(struct iscsi_conn *conn) +static int +iscsit_handle_immediate_queue(struct iscsi_conn *conn)  { +	struct iscsit_transport *t = conn->conn_transport;  	struct iscsi_queue_req *qr;  	struct iscsi_cmd *cmd;  	u8 state;  	int ret; -	while ((qr = iscsit_get_cmd_from_response_queue(conn))) { +	while ((qr = iscsit_get_cmd_from_immediate_queue(conn))) { +		atomic_set(&conn->check_immediate_queue, 0);  		cmd = qr->cmd;  		state = qr->state;  		kmem_cache_free(lio_qr_cache, qr); -check_rsp_state: -		switch (state) { -		case ISTATE_SEND_DATAIN: -			ret = iscsit_send_data_in(cmd, conn); -			if (ret < 0) -				goto err; -			else if (!ret) -				/* more drs */ -				goto check_rsp_state; -			else if (ret == 1) { -				/* all done */ -				spin_lock_bh(&cmd->istate_lock); -				cmd->i_state = ISTATE_SENT_STATUS; -				spin_unlock_bh(&cmd->istate_lock); +		ret = t->iscsit_immediate_queue(conn, cmd, state); +		if (ret < 0) +			return ret; +	} -				if (atomic_read(&conn->check_immediate_queue)) -					return 1; +	return 0; +} -				continue; -			} else if (ret == 2) { -				/* Still must send status, -				   SCF_TRANSPORT_TASK_SENSE was set */ -				spin_lock_bh(&cmd->istate_lock); -				cmd->i_state = ISTATE_SEND_STATUS; -				spin_unlock_bh(&cmd->istate_lock); -				state = ISTATE_SEND_STATUS; -				goto check_rsp_state; -			} +static int +iscsit_response_queue(struct iscsi_conn *conn, struct iscsi_cmd *cmd, int state) +{ +	int ret; -			break; -		case ISTATE_SEND_STATUS: -		case ISTATE_SEND_STATUS_RECOVERY: -			ret = iscsit_send_status(cmd, conn); -			break; -		case ISTATE_SEND_LOGOUTRSP: -			ret = iscsit_send_logout_response(cmd, conn); -			break; -		case ISTATE_SEND_ASYNCMSG: -			ret = iscsit_send_conn_drop_async_message( -				cmd, conn); -			break; -		case ISTATE_SEND_NOPIN: -			ret = iscsit_send_nopin_response(cmd, conn); -			break; -		case ISTATE_SEND_REJECT: -			ret = iscsit_send_reject(cmd, conn); -			break; -		case ISTATE_SEND_TASKMGTRSP: -			ret = iscsit_send_task_mgt_rsp(cmd, conn); -			if (ret != 0) -				break; -			ret = iscsit_tmr_post_handler(cmd, conn); -			if (ret != 0) -				iscsit_fall_back_to_erl0(conn->sess); -			break; -		case ISTATE_SEND_TEXTRSP: -			ret = iscsit_send_text_rsp(cmd, conn); -			break; -		default: -			pr_err("Unknown Opcode: 0x%02x ITT:" -			       " 0x%08x, i_state: %d on CID: %hu\n", -			       cmd->iscsi_opcode, cmd->init_task_tag, -			       state, conn->cid); -			goto err; -		} +check_rsp_state: +	switch (state) { +	case ISTATE_SEND_DATAIN: +		ret = iscsit_send_datain(cmd, conn);  		if (ret < 0)  			goto err; +		else if (!ret) +			/* more drs */ +			goto check_rsp_state; +		else if (ret == 1) { +			/* all done */ +			spin_lock_bh(&cmd->istate_lock); +			cmd->i_state = ISTATE_SENT_STATUS; +			spin_unlock_bh(&cmd->istate_lock); -		if (iscsit_send_tx_data(cmd, conn, 1) < 0) { -			iscsit_tx_thread_wait_for_tcp(conn); -			iscsit_unmap_iovec(cmd); -			goto err; -		} -		iscsit_unmap_iovec(cmd); +			if (atomic_read(&conn->check_immediate_queue)) +				return 1; -		switch (state) { -		case ISTATE_SEND_LOGOUTRSP: -			if (!iscsit_logout_post_handler(cmd, conn)) -				goto restart; -			/* fall through */ -		case ISTATE_SEND_STATUS: -		case ISTATE_SEND_ASYNCMSG: -		case ISTATE_SEND_NOPIN: -		case ISTATE_SEND_STATUS_RECOVERY: -		case ISTATE_SEND_TEXTRSP: -		case ISTATE_SEND_TASKMGTRSP: +			return 0; +		} else if (ret == 2) { +			/* Still must send status, +			   SCF_TRANSPORT_TASK_SENSE was set */  			spin_lock_bh(&cmd->istate_lock); -			cmd->i_state = ISTATE_SENT_STATUS; +			cmd->i_state = ISTATE_SEND_STATUS;  			spin_unlock_bh(&cmd->istate_lock); +			state = ISTATE_SEND_STATUS; +			goto check_rsp_state; +		} + +		break; +	case ISTATE_SEND_STATUS: +	case ISTATE_SEND_STATUS_RECOVERY: +		ret = iscsit_send_response(cmd, conn); +		break; +	case ISTATE_SEND_LOGOUTRSP: +		ret = iscsit_send_logout(cmd, conn); +		break; +	case ISTATE_SEND_ASYNCMSG: +		ret = iscsit_send_conn_drop_async_message( +			cmd, conn); +		break; +	case ISTATE_SEND_NOPIN: +		ret = iscsit_send_nopin(cmd, conn); +		break; +	case ISTATE_SEND_REJECT: +		ret = iscsit_send_reject(cmd, conn); +		break; +	case ISTATE_SEND_TASKMGTRSP: +		ret = iscsit_send_task_mgt_rsp(cmd, conn); +		if (ret != 0)  			break; -		case ISTATE_SEND_REJECT: -			if (cmd->cmd_flags & ICF_REJECT_FAIL_CONN) { -				cmd->cmd_flags &= ~ICF_REJECT_FAIL_CONN; -				complete(&cmd->reject_comp); -				goto err; -			} +		ret = iscsit_tmr_post_handler(cmd, conn); +		if (ret != 0) +			iscsit_fall_back_to_erl0(conn->sess); +		break; +	case ISTATE_SEND_TEXTRSP: +		ret = iscsit_send_text_rsp(cmd, conn); +		break; +	default: +		pr_err("Unknown Opcode: 0x%02x ITT:" +		       " 0x%08x, i_state: %d on CID: %hu\n", +		       cmd->iscsi_opcode, cmd->init_task_tag, +		       state, conn->cid); +		goto err; +	} +	if (ret < 0) +		goto err; + +	if (iscsit_send_tx_data(cmd, conn, 1) < 0) { +		iscsit_tx_thread_wait_for_tcp(conn); +		iscsit_unmap_iovec(cmd); +		goto err; +	} +	iscsit_unmap_iovec(cmd); + +	switch (state) { +	case ISTATE_SEND_LOGOUTRSP: +		if (!iscsit_logout_post_handler(cmd, conn)) +			goto restart; +		/* fall through */ +	case ISTATE_SEND_STATUS: +	case ISTATE_SEND_ASYNCMSG: +	case ISTATE_SEND_NOPIN: +	case ISTATE_SEND_STATUS_RECOVERY: +	case ISTATE_SEND_TEXTRSP: +	case ISTATE_SEND_TASKMGTRSP: +		spin_lock_bh(&cmd->istate_lock); +		cmd->i_state = ISTATE_SENT_STATUS; +		spin_unlock_bh(&cmd->istate_lock); +		break; +	case ISTATE_SEND_REJECT: +		if (cmd->cmd_flags & ICF_REJECT_FAIL_CONN) { +			cmd->cmd_flags &= ~ICF_REJECT_FAIL_CONN;  			complete(&cmd->reject_comp); -			break; -		default: -			pr_err("Unknown Opcode: 0x%02x ITT:" -			       " 0x%08x, i_state: %d on CID: %hu\n", -			       cmd->iscsi_opcode, cmd->init_task_tag, -			       cmd->i_state, conn->cid);  			goto err;  		} - -		if (atomic_read(&conn->check_immediate_queue)) -			return 1; +		complete(&cmd->reject_comp); +		break; +	default: +		pr_err("Unknown Opcode: 0x%02x ITT:" +		       " 0x%08x, i_state: %d on CID: %hu\n", +		       cmd->iscsi_opcode, cmd->init_task_tag, +		       cmd->i_state, conn->cid); +		goto err;  	} +	if (atomic_read(&conn->check_immediate_queue)) +		return 1; +  	return 0;  err: @@ -3688,6 +3848,27 @@ restart:  	return -EAGAIN;  } +static int iscsit_handle_response_queue(struct iscsi_conn *conn) +{ +	struct iscsit_transport *t = conn->conn_transport; +	struct iscsi_queue_req *qr; +	struct iscsi_cmd *cmd; +	u8 state; +	int ret; + +	while ((qr = iscsit_get_cmd_from_response_queue(conn))) { +		cmd = qr->cmd; +		state = qr->state; +		kmem_cache_free(lio_qr_cache, qr); + +		ret = t->iscsit_response_queue(conn, cmd, state); +		if (ret == 1 || ret < 0) +			return ret; +	} + +	return 0; +} +  int iscsi_target_tx_thread(void *arg)  {  	int ret = 0; @@ -3722,11 +3903,11 @@ restart:  			goto transport_err;  get_immediate: -		ret = handle_immediate_queue(conn); +		ret = iscsit_handle_immediate_queue(conn);  		if (ret < 0)  			goto transport_err; -		ret = handle_response_queue(conn); +		ret = iscsit_handle_response_queue(conn);  		if (ret == 1)  			goto get_immediate;  		else if (ret == -EAGAIN) @@ -3742,6 +3923,83 @@ out:  	return 0;  } +static int iscsi_target_rx_opcode(struct iscsi_conn *conn, unsigned char *buf) +{ +	struct iscsi_hdr *hdr = (struct iscsi_hdr *)buf; +	struct iscsi_cmd *cmd; +	int ret = 0; + +	switch (hdr->opcode & ISCSI_OPCODE_MASK) { +	case ISCSI_OP_SCSI_CMD: +		cmd = iscsit_allocate_cmd(conn, GFP_KERNEL); +		if (!cmd) +			return iscsit_add_reject(ISCSI_REASON_BOOKMARK_NO_RESOURCES, +						1, buf, conn); + +		ret = iscsit_handle_scsi_cmd(conn, cmd, buf); +		break; +	case ISCSI_OP_SCSI_DATA_OUT: +		ret = iscsit_handle_data_out(conn, buf); +		break; +	case ISCSI_OP_NOOP_OUT: +		cmd = NULL; +		if (hdr->ttt == cpu_to_be32(0xFFFFFFFF)) { +			cmd = iscsit_allocate_cmd(conn, GFP_KERNEL); +			if (!cmd) +				return iscsit_add_reject(ISCSI_REASON_BOOKMARK_NO_RESOURCES, +						1, buf, conn); +		} +		ret = iscsit_handle_nop_out(conn, cmd, buf); +		break; +	case ISCSI_OP_SCSI_TMFUNC: +		cmd = iscsit_allocate_cmd(conn, GFP_KERNEL); +		if (!cmd) +			return iscsit_add_reject(ISCSI_REASON_BOOKMARK_NO_RESOURCES, +						1, buf, conn); + +		ret = iscsit_handle_task_mgt_cmd(conn, cmd, buf); +		break; +	case ISCSI_OP_TEXT: +		ret = iscsit_handle_text_cmd(conn, buf); +		break; +	case ISCSI_OP_LOGOUT: +		cmd = iscsit_allocate_cmd(conn, GFP_KERNEL); +		if (!cmd) +			return iscsit_add_reject(ISCSI_REASON_BOOKMARK_NO_RESOURCES, +						1, buf, conn); + +		ret = iscsit_handle_logout_cmd(conn, cmd, buf); +		if (ret > 0) +			wait_for_completion_timeout(&conn->conn_logout_comp, +					SECONDS_FOR_LOGOUT_COMP * HZ); +		break; +	case ISCSI_OP_SNACK: +		ret = iscsit_handle_snack(conn, buf); +		break; +	default: +		pr_err("Got unknown iSCSI OpCode: 0x%02x\n", hdr->opcode); +		if (!conn->sess->sess_ops->ErrorRecoveryLevel) { +			pr_err("Cannot recover from unknown" +			" opcode while ERL=0, closing iSCSI connection.\n"); +			return -1; +		} +		if (!conn->conn_ops->OFMarker) { +			pr_err("Unable to recover from unknown" +			" opcode while OFMarker=No, closing iSCSI" +				" connection.\n"); +			return -1; +		} +		if (iscsit_recover_from_unknown_opcode(conn) < 0) { +			pr_err("Unable to recover from unknown" +				" opcode, closing iSCSI connection.\n"); +			return -1; +		} +		break; +	} + +	return ret; +} +  int iscsi_target_rx_thread(void *arg)  {  	int ret; @@ -3761,6 +4019,18 @@ restart:  	if (!conn)  		goto out; +	if (conn->conn_transport->transport_type == ISCSI_INFINIBAND) { +		struct completion comp; +		int rc; + +		init_completion(&comp); +		rc = wait_for_completion_interruptible(&comp); +		if (rc < 0) +			goto transport_err; + +		goto out; +	} +  	while (!kthread_should_stop()) {  		/*  		 * Ensure that both TX and RX per connection kthreads @@ -3832,62 +4102,9 @@ restart:  			goto transport_err;  		} -		switch (opcode) { -		case ISCSI_OP_SCSI_CMD: -			if (iscsit_handle_scsi_cmd(conn, buffer) < 0) -				goto transport_err; -			break; -		case ISCSI_OP_SCSI_DATA_OUT: -			if (iscsit_handle_data_out(conn, buffer) < 0) -				goto transport_err; -			break; -		case ISCSI_OP_NOOP_OUT: -			if (iscsit_handle_nop_out(conn, buffer) < 0) -				goto transport_err; -			break; -		case ISCSI_OP_SCSI_TMFUNC: -			if (iscsit_handle_task_mgt_cmd(conn, buffer) < 0) -				goto transport_err; -			break; -		case ISCSI_OP_TEXT: -			if (iscsit_handle_text_cmd(conn, buffer) < 0) -				goto transport_err; -			break; -		case ISCSI_OP_LOGOUT: -			ret = iscsit_handle_logout_cmd(conn, buffer); -			if (ret > 0) { -				wait_for_completion_timeout(&conn->conn_logout_comp, -						SECONDS_FOR_LOGOUT_COMP * HZ); -				goto transport_err; -			} else if (ret < 0) -				goto transport_err; -			break; -		case ISCSI_OP_SNACK: -			if (iscsit_handle_snack(conn, buffer) < 0) -				goto transport_err; -			break; -		default: -			pr_err("Got unknown iSCSI OpCode: 0x%02x\n", -					opcode); -			if (!conn->sess->sess_ops->ErrorRecoveryLevel) { -				pr_err("Cannot recover from unknown" -				" opcode while ERL=0, closing iSCSI connection" -				".\n"); -				goto transport_err; -			} -			if (!conn->conn_ops->OFMarker) { -				pr_err("Unable to recover from unknown" -				" opcode while OFMarker=No, closing iSCSI" -					" connection.\n"); -				goto transport_err; -			} -			if (iscsit_recover_from_unknown_opcode(conn) < 0) { -				pr_err("Unable to recover from unknown" -					" opcode, closing iSCSI connection.\n"); -				goto transport_err; -			} -			break; -		} +		ret = iscsi_target_rx_opcode(conn, buffer); +		if (ret < 0) +			goto transport_err;  	}  transport_err: @@ -4053,6 +4270,12 @@ int iscsit_close_connection(  	if (conn->sock)  		sock_release(conn->sock); + +	if (conn->conn_transport->iscsit_free_conn) +		conn->conn_transport->iscsit_free_conn(conn); + +	iscsit_put_transport(conn->conn_transport); +  	conn->thread_set = NULL;  	pr_debug("Moving to TARG_CONN_STATE_FREE.\n"); @@ -4284,7 +4507,7 @@ static void iscsit_logout_post_handler_diffcid(  /*   *	Return of 0 causes the TX thread to restart.   */ -static int iscsit_logout_post_handler( +int iscsit_logout_post_handler(  	struct iscsi_cmd *cmd,  	struct iscsi_conn *conn)  { @@ -4342,6 +4565,7 @@ static int iscsit_logout_post_handler(  	}  	return ret;  } +EXPORT_SYMBOL(iscsit_logout_post_handler);  void iscsit_fail_session(struct iscsi_session *sess)  { diff --git a/drivers/target/iscsi/iscsi_target.h b/drivers/target/iscsi/iscsi_target.h index b1a1e635070..a0050b2f294 100644 --- a/drivers/target/iscsi/iscsi_target.h +++ b/drivers/target/iscsi/iscsi_target.h @@ -16,11 +16,12 @@ extern int iscsit_reset_np_thread(struct iscsi_np *, struct iscsi_tpg_np *,  				struct iscsi_portal_group *);  extern int iscsit_del_np(struct iscsi_np *);  extern int iscsit_add_reject_from_cmd(u8, int, int, unsigned char *, struct iscsi_cmd *); +extern void iscsit_set_unsoliticed_dataout(struct iscsi_cmd *);  extern int iscsit_logout_closesession(struct iscsi_cmd *, struct iscsi_conn *);  extern int iscsit_logout_closeconnection(struct iscsi_cmd *, struct iscsi_conn *);  extern int iscsit_logout_removeconnforrecovery(struct iscsi_cmd *, struct iscsi_conn *);  extern int iscsit_send_async_msg(struct iscsi_conn *, u16, u8, u8); -extern int iscsit_build_r2ts_for_cmd(struct iscsi_cmd *, struct iscsi_conn *, bool recovery); +extern int iscsit_build_r2ts_for_cmd(struct iscsi_conn *, struct iscsi_cmd *, bool recovery);  extern void iscsit_thread_get_cpumask(struct iscsi_conn *);  extern int iscsi_target_tx_thread(void *);  extern int iscsi_target_rx_thread(void *); diff --git a/drivers/target/iscsi/iscsi_target_auth.c b/drivers/target/iscsi/iscsi_target_auth.c index a0fc7b9eea6..cee17543278 100644 --- a/drivers/target/iscsi/iscsi_target_auth.c +++ b/drivers/target/iscsi/iscsi_target_auth.c @@ -49,32 +49,6 @@ static void chap_binaryhex_to_asciihex(char *dst, char *src, int src_len)  	}  } -static void chap_set_random(char *data, int length) -{ -	long r; -	unsigned n; - -	while (length > 0) { -		get_random_bytes(&r, sizeof(long)); -		r = r ^ (r >> 8); -		r = r ^ (r >> 4); -		n = r & 0x7; - -		get_random_bytes(&r, sizeof(long)); -		r = r ^ (r >> 8); -		r = r ^ (r >> 5); -		n = (n << 3) | (r & 0x7); - -		get_random_bytes(&r, sizeof(long)); -		r = r ^ (r >> 8); -		r = r ^ (r >> 5); -		n = (n << 2) | (r & 0x3); - -		*data++ = n; -		length--; -	} -} -  static void chap_gen_challenge(  	struct iscsi_conn *conn,  	int caller, @@ -86,7 +60,7 @@ static void chap_gen_challenge(  	memset(challenge_asciihex, 0, CHAP_CHALLENGE_LENGTH * 2 + 1); -	chap_set_random(chap->challenge, CHAP_CHALLENGE_LENGTH); +	get_random_bytes(chap->challenge, CHAP_CHALLENGE_LENGTH);  	chap_binaryhex_to_asciihex(challenge_asciihex, chap->challenge,  				CHAP_CHALLENGE_LENGTH);  	/* diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c index 78d75c8567d..13e9e715ad2 100644 --- a/drivers/target/iscsi/iscsi_target_configfs.c +++ b/drivers/target/iscsi/iscsi_target_configfs.c @@ -27,6 +27,7 @@  #include <target/target_core_fabric_configfs.h>  #include <target/target_core_configfs.h>  #include <target/configfs_macros.h> +#include <target/iscsi/iscsi_transport.h>  #include "iscsi_target_core.h"  #include "iscsi_target_parameters.h" @@ -124,8 +125,87 @@ out:  TF_NP_BASE_ATTR(lio_target, sctp, S_IRUGO | S_IWUSR); +static ssize_t lio_target_np_show_iser( +	struct se_tpg_np *se_tpg_np, +	char *page) +{ +	struct iscsi_tpg_np *tpg_np = container_of(se_tpg_np, +				struct iscsi_tpg_np, se_tpg_np); +	struct iscsi_tpg_np *tpg_np_iser; +	ssize_t rb; + +	tpg_np_iser = iscsit_tpg_locate_child_np(tpg_np, ISCSI_INFINIBAND); +	if (tpg_np_iser) +		rb = sprintf(page, "1\n"); +	else +		rb = sprintf(page, "0\n"); + +	return rb; +} + +static ssize_t lio_target_np_store_iser( +	struct se_tpg_np *se_tpg_np, +	const char *page, +	size_t count) +{ +	struct iscsi_np *np; +	struct iscsi_portal_group *tpg; +	struct iscsi_tpg_np *tpg_np = container_of(se_tpg_np, +				struct iscsi_tpg_np, se_tpg_np); +	struct iscsi_tpg_np *tpg_np_iser = NULL; +	char *endptr; +	u32 op; +	int rc; + +	op = simple_strtoul(page, &endptr, 0); +	if ((op != 1) && (op != 0)) { +		pr_err("Illegal value for tpg_enable: %u\n", op); +		return -EINVAL; +	} +	np = tpg_np->tpg_np; +	if (!np) { +		pr_err("Unable to locate struct iscsi_np from" +				" struct iscsi_tpg_np\n"); +		return -EINVAL; +	} + +	tpg = tpg_np->tpg; +	if (iscsit_get_tpg(tpg) < 0) +		return -EINVAL; + +	if (op) { +		int rc = request_module("ib_isert"); +		if (rc != 0) +			pr_warn("Unable to request_module for ib_isert\n"); + +		tpg_np_iser = iscsit_tpg_add_network_portal(tpg, &np->np_sockaddr, +				np->np_ip, tpg_np, ISCSI_INFINIBAND); +		if (!tpg_np_iser || IS_ERR(tpg_np_iser)) +			goto out; +	} else { +		tpg_np_iser = iscsit_tpg_locate_child_np(tpg_np, ISCSI_INFINIBAND); +		if (!tpg_np_iser) +			goto out; + +		rc = iscsit_tpg_del_network_portal(tpg, tpg_np_iser); +		if (rc < 0) +			goto out; +	} + +	printk("lio_target_np_store_iser() done, op: %d\n", op); + +	iscsit_put_tpg(tpg); +	return count; +out: +	iscsit_put_tpg(tpg); +	return -EINVAL; +} + +TF_NP_BASE_ATTR(lio_target, iser, S_IRUGO | S_IWUSR); +  static struct configfs_attribute *lio_target_portal_attrs[] = {  	&lio_target_np_sctp.attr, +	&lio_target_np_iser.attr,  	NULL,  }; @@ -1536,16 +1616,18 @@ static int lio_queue_data_in(struct se_cmd *se_cmd)  	struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);  	cmd->i_state = ISTATE_SEND_DATAIN; -	iscsit_add_cmd_to_response_queue(cmd, cmd->conn, cmd->i_state); +	cmd->conn->conn_transport->iscsit_queue_data_in(cmd->conn, cmd); +  	return 0;  }  static int lio_write_pending(struct se_cmd *se_cmd)  {  	struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd); +	struct iscsi_conn *conn = cmd->conn;  	if (!cmd->immediate_data && !cmd->unsolicited_data) -		return iscsit_build_r2ts_for_cmd(cmd, cmd->conn, false); +		return conn->conn_transport->iscsit_get_dataout(conn, cmd, false);  	return 0;  } @@ -1567,7 +1649,8 @@ static int lio_queue_status(struct se_cmd *se_cmd)  	struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);  	cmd->i_state = ISTATE_SEND_STATUS; -	iscsit_add_cmd_to_response_queue(cmd, cmd->conn, cmd->i_state); +	cmd->conn->conn_transport->iscsit_queue_status(cmd->conn, cmd); +  	return 0;  } @@ -1696,11 +1779,17 @@ static void lio_set_default_node_attributes(struct se_node_acl *se_acl)  	iscsit_set_default_node_attribues(acl);  } +static int lio_check_stop_free(struct se_cmd *se_cmd) +{ +	return target_put_sess_cmd(se_cmd->se_sess, se_cmd); +} +  static void lio_release_cmd(struct se_cmd *se_cmd)  {  	struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd); -	iscsit_release_cmd(cmd); +	pr_debug("Entering lio_release_cmd for se_cmd: %p\n", se_cmd); +	cmd->release_cmd(cmd);  }  /* End functions for target_core_fabric_ops */ @@ -1740,6 +1829,7 @@ int iscsi_target_register_configfs(void)  	fabric->tf_ops.tpg_alloc_fabric_acl = &lio_tpg_alloc_fabric_acl;  	fabric->tf_ops.tpg_release_fabric_acl = &lio_tpg_release_fabric_acl;  	fabric->tf_ops.tpg_get_inst_index = &lio_tpg_get_inst_index; +	fabric->tf_ops.check_stop_free = &lio_check_stop_free,  	fabric->tf_ops.release_cmd = &lio_release_cmd;  	fabric->tf_ops.shutdown_session = &lio_tpg_shutdown_session;  	fabric->tf_ops.close_session = &lio_tpg_close_session; diff --git a/drivers/target/iscsi/iscsi_target_core.h b/drivers/target/iscsi/iscsi_target_core.h index 7a333d28d9a..60ec4b92be0 100644 --- a/drivers/target/iscsi/iscsi_target_core.h +++ b/drivers/target/iscsi/iscsi_target_core.h @@ -60,7 +60,7 @@  #define ISCSI_IOV_DATA_BUFFER		5 -enum tpg_np_network_transport_table { +enum iscsit_transport_type {  	ISCSI_TCP				= 0,  	ISCSI_SCTP_TCP				= 1,  	ISCSI_SCTP_UDP				= 2, @@ -244,6 +244,11 @@ struct iscsi_conn_ops {  	u8	IFMarker;			/* [0,1] == [No,Yes] */  	u32	OFMarkInt;			/* [1..65535] */  	u32	IFMarkInt;			/* [1..65535] */ +	/* +	 * iSER specific connection parameters +	 */ +	u32	InitiatorRecvDataSegmentLength;	/* [512..2**24-1] */ +	u32	TargetRecvDataSegmentLength;	/* [512..2**24-1] */  };  struct iscsi_sess_ops { @@ -265,6 +270,10 @@ struct iscsi_sess_ops {  	u8	DataSequenceInOrder;		/* [0,1] == [No,Yes] */  	u8	ErrorRecoveryLevel;		/* [0..2] */  	u8	SessionType;			/* [0,1] == [Normal,Discovery]*/ +	/* +	 * iSER specific session parameters +	 */ +	u8	RDMAExtensions;			/* [0,1] == [No,Yes] */  };  struct iscsi_queue_req { @@ -284,6 +293,7 @@ struct iscsi_data_count {  };  struct iscsi_param_list { +	bool			iser;  	struct list_head	param_list;  	struct list_head	extra_response_list;  }; @@ -475,6 +485,7 @@ struct iscsi_cmd {  	u32			first_data_sg_off;  	u32			kmapped_nents;  	sense_reason_t		sense_reason; +	void (*release_cmd)(struct iscsi_cmd *);  }  ____cacheline_aligned;  struct iscsi_tmr_req { @@ -503,6 +514,7 @@ struct iscsi_conn {  	u16			login_port;  	u16			local_port;  	int			net_size; +	int			login_family;  	u32			auth_id;  	u32			conn_flags;  	/* Used for iscsi_tx_login_rsp() */ @@ -562,9 +574,12 @@ struct iscsi_conn {  	struct list_head	immed_queue_list;  	struct list_head	response_queue_list;  	struct iscsi_conn_ops	*conn_ops; +	struct iscsi_login	*conn_login; +	struct iscsit_transport *conn_transport;  	struct iscsi_param_list	*param_list;  	/* Used for per connection auth state machine */  	void			*auth_protocol; +	void			*context;  	struct iscsi_login_thread_s *login_thread;  	struct iscsi_portal_group *tpg;  	/* Pointer to parent session */ @@ -663,6 +678,8 @@ struct iscsi_login {  	u8 first_request;  	u8 version_min;  	u8 version_max; +	u8 login_complete; +	u8 login_failed;  	char isid[6];  	u32 cmd_sn;  	itt_t init_task_tag; @@ -670,10 +687,11 @@ struct iscsi_login {  	u32 rsp_length;  	u16 cid;  	u16 tsih; -	char *req; -	char *rsp; +	char req[ISCSI_HDR_LEN]; +	char rsp[ISCSI_HDR_LEN];  	char *req_buf;  	char *rsp_buf; +	struct iscsi_conn *conn;  } ____cacheline_aligned;  struct iscsi_node_attrib { @@ -754,6 +772,8 @@ struct iscsi_np {  	struct task_struct	*np_thread;  	struct timer_list	np_login_timer;  	struct iscsi_portal_group *np_login_tpg; +	void			*np_context; +	struct iscsit_transport *np_transport;  	struct list_head	np_list;  } ____cacheline_aligned; diff --git a/drivers/target/iscsi/iscsi_target_device.c b/drivers/target/iscsi/iscsi_target_device.c index bcc409853a6..1b74033510a 100644 --- a/drivers/target/iscsi/iscsi_target_device.c +++ b/drivers/target/iscsi/iscsi_target_device.c @@ -60,8 +60,13 @@ void iscsit_increment_maxcmdsn(struct iscsi_cmd *cmd, struct iscsi_session *sess  	cmd->maxcmdsn_inc = 1; -	mutex_lock(&sess->cmdsn_mutex); +	if (!mutex_trylock(&sess->cmdsn_mutex)) { +		sess->max_cmd_sn += 1; +		pr_debug("Updated MaxCmdSN to 0x%08x\n", sess->max_cmd_sn); +		return; +	}  	sess->max_cmd_sn += 1;  	pr_debug("Updated MaxCmdSN to 0x%08x\n", sess->max_cmd_sn);  	mutex_unlock(&sess->cmdsn_mutex);  } +EXPORT_SYMBOL(iscsit_increment_maxcmdsn); diff --git a/drivers/target/iscsi/iscsi_target_erl1.c b/drivers/target/iscsi/iscsi_target_erl1.c index 0b52a237130..7816af6cdd1 100644 --- a/drivers/target/iscsi/iscsi_target_erl1.c +++ b/drivers/target/iscsi/iscsi_target_erl1.c @@ -22,6 +22,7 @@  #include <scsi/iscsi_proto.h>  #include <target/target_core_base.h>  #include <target/target_core_fabric.h> +#include <target/iscsi/iscsi_transport.h>  #include "iscsi_target_core.h"  #include "iscsi_target_seq_pdu_list.h" @@ -53,6 +54,9 @@ int iscsit_dump_data_payload(  	u32 length, padding, offset = 0, size;  	struct kvec iov; +	if (conn->sess->sess_ops->RDMAExtensions) +		return 0; +  	length = (buf_len > OFFLOAD_BUF_SIZE) ? OFFLOAD_BUF_SIZE : buf_len;  	buf = kzalloc(length, GFP_ATOMIC); @@ -919,6 +923,7 @@ int iscsit_execute_ooo_cmdsns(struct iscsi_session *sess)  int iscsit_execute_cmd(struct iscsi_cmd *cmd, int ooo)  {  	struct se_cmd *se_cmd = &cmd->se_cmd; +	struct iscsi_conn *conn = cmd->conn;  	int lr = 0;  	spin_lock_bh(&cmd->istate_lock); @@ -981,7 +986,7 @@ int iscsit_execute_cmd(struct iscsi_cmd *cmd, int ooo)  					return 0;  				iscsit_set_dataout_sequence_values(cmd); -				iscsit_build_r2ts_for_cmd(cmd, cmd->conn, false); +				conn->conn_transport->iscsit_get_dataout(conn, cmd, false);  			}  			return 0;  		} @@ -999,10 +1004,7 @@ int iscsit_execute_cmd(struct iscsi_cmd *cmd, int ooo)  			if (transport_check_aborted_status(se_cmd, 1) != 0)  				return 0; -			iscsit_set_dataout_sequence_values(cmd); -			spin_lock_bh(&cmd->dataout_timeout_lock); -			iscsit_start_dataout_timer(cmd, cmd->conn); -			spin_unlock_bh(&cmd->dataout_timeout_lock); +			iscsit_set_unsoliticed_dataout(cmd);  		}  		return transport_handle_cdb_direct(&cmd->se_cmd); @@ -1290,3 +1292,4 @@ void iscsit_stop_dataout_timer(struct iscsi_cmd *cmd)  			cmd->init_task_tag);  	spin_unlock_bh(&cmd->dataout_timeout_lock);  } +EXPORT_SYMBOL(iscsit_stop_dataout_timer); diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index 2535d4d46c0..bb5d5c5bce6 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -39,8 +39,39 @@  #include "iscsi_target.h"  #include "iscsi_target_parameters.h" -static int iscsi_login_init_conn(struct iscsi_conn *conn) +#include <target/iscsi/iscsi_transport.h> + +static struct iscsi_login *iscsi_login_init_conn(struct iscsi_conn *conn)  { +	struct iscsi_login *login; + +	login = kzalloc(sizeof(struct iscsi_login), GFP_KERNEL); +	if (!login) { +		pr_err("Unable to allocate memory for struct iscsi_login.\n"); +		return NULL; +	} +	login->conn = conn; +	login->first_request = 1; + +	login->req_buf = kzalloc(MAX_KEY_VALUE_PAIRS, GFP_KERNEL); +	if (!login->req_buf) { +		pr_err("Unable to allocate memory for response buffer.\n"); +		goto out_login; +	} + +	login->rsp_buf = kzalloc(MAX_KEY_VALUE_PAIRS, GFP_KERNEL); +	if (!login->rsp_buf) { +		pr_err("Unable to allocate memory for request buffer.\n"); +		goto out_req_buf; +	} + +	conn->conn_ops = kzalloc(sizeof(struct iscsi_conn_ops), GFP_KERNEL); +	if (!conn->conn_ops) { +		pr_err("Unable to allocate memory for" +			" struct iscsi_conn_ops.\n"); +		goto out_rsp_buf; +	} +  	init_waitqueue_head(&conn->queues_wq);  	INIT_LIST_HEAD(&conn->conn_list);  	INIT_LIST_HEAD(&conn->conn_cmd_list); @@ -62,10 +93,21 @@ static int iscsi_login_init_conn(struct iscsi_conn *conn)  	if (!zalloc_cpumask_var(&conn->conn_cpumask, GFP_KERNEL)) {  		pr_err("Unable to allocate conn->conn_cpumask\n"); -		return -ENOMEM; +		goto out_conn_ops;  	} +	conn->conn_login = login; -	return 0; +	return login; + +out_conn_ops: +	kfree(conn->conn_ops); +out_rsp_buf: +	kfree(login->rsp_buf); +out_req_buf: +	kfree(login->req_buf); +out_login: +	kfree(login); +	return NULL;  }  /* @@ -298,6 +340,7 @@ static int iscsi_login_zero_tsih_s2(  	struct iscsi_node_attrib *na;  	struct iscsi_session *sess = conn->sess;  	unsigned char buf[32]; +	bool iser = false;  	sess->tpg = conn->tpg; @@ -319,7 +362,10 @@ static int iscsi_login_zero_tsih_s2(  		return -1;  	} -	iscsi_set_keys_to_negotiate(0, conn->param_list); +	if (conn->conn_transport->transport_type == ISCSI_INFINIBAND) +		iser = true; + +	iscsi_set_keys_to_negotiate(conn->param_list, iser);  	if (sess->sess_ops->SessionType)  		return iscsi_set_keys_irrelevant_for_discovery( @@ -357,6 +403,56 @@ static int iscsi_login_zero_tsih_s2(  	if (iscsi_login_disable_FIM_keys(conn->param_list, conn) < 0)  		return -1; +	/* +	 * Set RDMAExtensions=Yes by default for iSER enabled network portals +	 */ +	if (iser) { +		struct iscsi_param *param; +		unsigned long mrdsl, off; +		int rc; + +		sprintf(buf, "RDMAExtensions=Yes"); +		if (iscsi_change_param_value(buf, conn->param_list, 0) < 0) { +			iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, +				ISCSI_LOGIN_STATUS_NO_RESOURCES); +			return -1; +		} +		/* +		 * Make MaxRecvDataSegmentLength PAGE_SIZE aligned for +		 * Immediate Data + Unsolicitied Data-OUT if necessary.. +		 */ +		param = iscsi_find_param_from_key("MaxRecvDataSegmentLength", +						  conn->param_list); +		if (!param) { +			iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, +				ISCSI_LOGIN_STATUS_NO_RESOURCES); +			return -1; +		} +		rc = strict_strtoul(param->value, 0, &mrdsl); +		if (rc < 0) { +			iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, +				ISCSI_LOGIN_STATUS_NO_RESOURCES); +			return -1; +		} +		off = mrdsl % PAGE_SIZE; +		if (!off) +			return 0; + +		if (mrdsl < PAGE_SIZE) +			mrdsl = PAGE_SIZE; +		else +			mrdsl -= off; + +		pr_warn("Aligning ISER MaxRecvDataSegmentLength: %lu down" +			" to PAGE_SIZE\n", mrdsl); + +		sprintf(buf, "MaxRecvDataSegmentLength=%lu\n", mrdsl); +		if (iscsi_change_param_value(buf, conn->param_list, 0) < 0) { +			iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, +				ISCSI_LOGIN_STATUS_NO_RESOURCES); +			return -1; +		} +	}  	return 0;  } @@ -436,6 +532,7 @@ static int iscsi_login_non_zero_tsih_s2(  	struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;  	struct se_session *se_sess, *se_sess_tmp;  	struct iscsi_login_req *pdu = (struct iscsi_login_req *)buf; +	bool iser = false;  	spin_lock_bh(&se_tpg->session_lock);  	list_for_each_entry_safe(se_sess, se_sess_tmp, &se_tpg->tpg_sess_list, @@ -485,7 +582,10 @@ static int iscsi_login_non_zero_tsih_s2(  		return -1;  	} -	iscsi_set_keys_to_negotiate(0, conn->param_list); +	if (conn->conn_transport->transport_type == ISCSI_INFINIBAND) +		iser = true; + +	iscsi_set_keys_to_negotiate(conn->param_list, iser);  	/*  	 * Need to send TargetPortalGroupTag back in first login response  	 * on any iSCSI connection where the Initiator provides TargetName. @@ -574,6 +674,11 @@ int iscsi_login_post_auth_non_zero_tsih(  static void iscsi_post_login_start_timers(struct iscsi_conn *conn)  {  	struct iscsi_session *sess = conn->sess; +	/* +	 * FIXME: Unsolicitied NopIN support for ISER +	 */ +	if (conn->conn_transport->transport_type == ISCSI_INFINIBAND) +		return;  	if (!sess->sess_ops->SessionType)  		iscsit_start_nopin_timer(conn); @@ -632,6 +737,7 @@ static int iscsi_post_login_handler(  		spin_unlock_bh(&sess->conn_lock);  		iscsi_post_login_start_timers(conn); +  		iscsi_activate_thread_set(conn, ts);  		/*  		 * Determine CPU mask to ensure connection's RX and TX kthreads @@ -761,11 +867,11 @@ static void iscsi_stop_login_thread_timer(struct iscsi_np *np)  	spin_unlock_bh(&np->np_thread_lock);  } -int iscsi_target_setup_login_socket( +int iscsit_setup_np(  	struct iscsi_np *np,  	struct __kernel_sockaddr_storage *sockaddr)  { -	struct socket *sock; +	struct socket *sock = NULL;  	int backlog = 5, ret, opt = 0, len;  	switch (np->np_network_transport) { @@ -781,15 +887,15 @@ int iscsi_target_setup_login_socket(  		np->np_ip_proto = IPPROTO_SCTP;  		np->np_sock_type = SOCK_SEQPACKET;  		break; -	case ISCSI_IWARP_TCP: -	case ISCSI_IWARP_SCTP: -	case ISCSI_INFINIBAND:  	default:  		pr_err("Unsupported network_transport: %d\n",  				np->np_network_transport);  		return -EINVAL;  	} +	np->np_ip_proto = IPPROTO_TCP; +	np->np_sock_type = SOCK_STREAM; +  	ret = sock_create(sockaddr->ss_family, np->np_sock_type,  			np->np_ip_proto, &sock);  	if (ret < 0) { @@ -853,7 +959,6 @@ int iscsi_target_setup_login_socket(  	}  	return 0; -  fail:  	np->np_socket = NULL;  	if (sock) @@ -861,21 +966,169 @@ fail:  	return ret;  } +int iscsi_target_setup_login_socket( +	struct iscsi_np *np, +	struct __kernel_sockaddr_storage *sockaddr) +{ +	struct iscsit_transport *t; +	int rc; + +	t = iscsit_get_transport(np->np_network_transport); +	if (!t) +		return -EINVAL; + +	rc = t->iscsit_setup_np(np, sockaddr); +	if (rc < 0) { +		iscsit_put_transport(t); +		return rc; +	} + +	np->np_transport = t; +	printk("Set np->np_transport to %p -> %s\n", np->np_transport, +				np->np_transport->name); +	return 0; +} + +int iscsit_accept_np(struct iscsi_np *np, struct iscsi_conn *conn) +{ +	struct socket *new_sock, *sock = np->np_socket; +	struct sockaddr_in sock_in; +	struct sockaddr_in6 sock_in6; +	int rc, err; + +	rc = kernel_accept(sock, &new_sock, 0); +	if (rc < 0) +		return rc; + +	conn->sock = new_sock; +	conn->login_family = np->np_sockaddr.ss_family; +	printk("iSCSI/TCP: Setup conn->sock from new_sock: %p\n", new_sock); + +	if (np->np_sockaddr.ss_family == AF_INET6) { +		memset(&sock_in6, 0, sizeof(struct sockaddr_in6)); + +		rc = conn->sock->ops->getname(conn->sock, +				(struct sockaddr *)&sock_in6, &err, 1); +		if (!rc) { +			snprintf(conn->login_ip, sizeof(conn->login_ip), "%pI6c", +				&sock_in6.sin6_addr.in6_u); +			conn->login_port = ntohs(sock_in6.sin6_port); +		} + +		rc = conn->sock->ops->getname(conn->sock, +				(struct sockaddr *)&sock_in6, &err, 0); +		if (!rc) { +			snprintf(conn->local_ip, sizeof(conn->local_ip), "%pI6c", +				&sock_in6.sin6_addr.in6_u); +			conn->local_port = ntohs(sock_in6.sin6_port); +		} +	} else { +		memset(&sock_in, 0, sizeof(struct sockaddr_in)); + +		rc = conn->sock->ops->getname(conn->sock, +				(struct sockaddr *)&sock_in, &err, 1); +		if (!rc) { +			sprintf(conn->login_ip, "%pI4", +					&sock_in.sin_addr.s_addr); +			conn->login_port = ntohs(sock_in.sin_port); +		} + +		rc = conn->sock->ops->getname(conn->sock, +				(struct sockaddr *)&sock_in, &err, 0); +		if (!rc) { +			sprintf(conn->local_ip, "%pI4", +					&sock_in.sin_addr.s_addr); +			conn->local_port = ntohs(sock_in.sin_port); +		} +	} + +	return 0; +} + +int iscsit_get_login_rx(struct iscsi_conn *conn, struct iscsi_login *login) +{ +	struct iscsi_login_req *login_req; +	u32 padding = 0, payload_length; + +	if (iscsi_login_rx_data(conn, login->req, ISCSI_HDR_LEN) < 0) +		return -1; + +	login_req = (struct iscsi_login_req *)login->req; +	payload_length	= ntoh24(login_req->dlength); +	padding = ((-payload_length) & 3); + +	pr_debug("Got Login Command, Flags 0x%02x, ITT: 0x%08x," +		" CmdSN: 0x%08x, ExpStatSN: 0x%08x, CID: %hu, Length: %u\n", +		login_req->flags, login_req->itt, login_req->cmdsn, +		login_req->exp_statsn, login_req->cid, payload_length); +	/* +	 * Setup the initial iscsi_login values from the leading +	 * login request PDU. +	 */ +	if (login->first_request) { +		login_req = (struct iscsi_login_req *)login->req; +		login->leading_connection = (!login_req->tsih) ? 1 : 0; +		login->current_stage	= ISCSI_LOGIN_CURRENT_STAGE(login_req->flags); +		login->version_min	= login_req->min_version; +		login->version_max	= login_req->max_version; +		memcpy(login->isid, login_req->isid, 6); +		login->cmd_sn		= be32_to_cpu(login_req->cmdsn); +		login->init_task_tag	= login_req->itt; +		login->initial_exp_statsn = be32_to_cpu(login_req->exp_statsn); +		login->cid		= be16_to_cpu(login_req->cid); +		login->tsih		= be16_to_cpu(login_req->tsih); +	} + +	if (iscsi_target_check_login_request(conn, login) < 0) +		return -1; + +	memset(login->req_buf, 0, MAX_KEY_VALUE_PAIRS); +	if (iscsi_login_rx_data(conn, login->req_buf, +				payload_length + padding) < 0) +		return -1; + +	return 0; +} + +int iscsit_put_login_tx(struct iscsi_conn *conn, struct iscsi_login *login, +			u32 length) +{ +	if (iscsi_login_tx_data(conn, login->rsp, login->rsp_buf, length) < 0) +		return -1; + +	return 0; +} + +static int +iscsit_conn_set_transport(struct iscsi_conn *conn, struct iscsit_transport *t) +{ +	int rc; + +	if (!t->owner) { +		conn->conn_transport = t; +		return 0; +	} + +	rc = try_module_get(t->owner); +	if (!rc) { +		pr_err("try_module_get() failed for %s\n", t->name); +		return -EINVAL; +	} + +	conn->conn_transport = t; +	return 0; +} +  static int __iscsi_target_login_thread(struct iscsi_np *np)  { -	u8 buffer[ISCSI_HDR_LEN], iscsi_opcode, zero_tsih = 0; -	int err, ret = 0, stop; +	u8 *buffer, zero_tsih = 0; +	int ret = 0, rc, stop;  	struct iscsi_conn *conn = NULL;  	struct iscsi_login *login;  	struct iscsi_portal_group *tpg = NULL; -	struct socket *new_sock, *sock; -	struct kvec iov;  	struct iscsi_login_req *pdu; -	struct sockaddr_in sock_in; -	struct sockaddr_in6 sock_in6;  	flush_signals(current); -	sock = np->np_socket;  	spin_lock_bh(&np->np_thread_lock);  	if (np->np_thread_state == ISCSI_NP_THREAD_RESET) { @@ -886,75 +1139,76 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)  	}  	spin_unlock_bh(&np->np_thread_lock); -	if (kernel_accept(sock, &new_sock, 0) < 0) { -		spin_lock_bh(&np->np_thread_lock); -		if (np->np_thread_state == ISCSI_NP_THREAD_RESET) { -			spin_unlock_bh(&np->np_thread_lock); -			complete(&np->np_restart_comp); -			/* Get another socket */ -			return 1; -		} -		spin_unlock_bh(&np->np_thread_lock); -		goto out; -	} -	iscsi_start_login_thread_timer(np); -  	conn = kzalloc(sizeof(struct iscsi_conn), GFP_KERNEL);  	if (!conn) {  		pr_err("Could not allocate memory for"  			" new connection\n"); -		sock_release(new_sock);  		/* Get another socket */  		return 1;  	} -  	pr_debug("Moving to TARG_CONN_STATE_FREE.\n");  	conn->conn_state = TARG_CONN_STATE_FREE; -	conn->sock = new_sock; -	pr_debug("Moving to TARG_CONN_STATE_XPT_UP.\n"); -	conn->conn_state = TARG_CONN_STATE_XPT_UP; +	if (iscsit_conn_set_transport(conn, np->np_transport) < 0) { +		kfree(conn); +		return 1; +	} -	/* -	 * Allocate conn->conn_ops early as a failure calling -	 * iscsit_tx_login_rsp() below will call tx_data(). -	 */ -	conn->conn_ops = kzalloc(sizeof(struct iscsi_conn_ops), GFP_KERNEL); -	if (!conn->conn_ops) { -		pr_err("Unable to allocate memory for" -			" struct iscsi_conn_ops.\n"); -		goto new_sess_out; +	rc = np->np_transport->iscsit_accept_np(np, conn); +	if (rc == -ENOSYS) { +		complete(&np->np_restart_comp); +		iscsit_put_transport(conn->conn_transport); +		kfree(conn); +		conn = NULL; +		goto exit; +	} else if (rc < 0) { +		spin_lock_bh(&np->np_thread_lock); +		if (np->np_thread_state == ISCSI_NP_THREAD_RESET) { +			spin_unlock_bh(&np->np_thread_lock); +			complete(&np->np_restart_comp); +			if (ret == -ENODEV) { +				iscsit_put_transport(conn->conn_transport); +				kfree(conn); +				conn = NULL; +				goto out; +			} +			/* Get another socket */ +			return 1; +		} +		spin_unlock_bh(&np->np_thread_lock); +		iscsit_put_transport(conn->conn_transport); +		kfree(conn); +		conn = NULL; +		goto out;  	}  	/*  	 * Perform the remaining iSCSI connection initialization items..  	 */ -	if (iscsi_login_init_conn(conn) < 0) -		goto new_sess_out; - -	memset(buffer, 0, ISCSI_HDR_LEN); -	memset(&iov, 0, sizeof(struct kvec)); -	iov.iov_base	= buffer; -	iov.iov_len	= ISCSI_HDR_LEN; - -	if (rx_data(conn, &iov, 1, ISCSI_HDR_LEN) <= 0) { -		pr_err("rx_data() returned an error.\n"); +	login = iscsi_login_init_conn(conn); +	if (!login) {  		goto new_sess_out;  	} -	iscsi_opcode = (buffer[0] & ISCSI_OPCODE_MASK); -	if (!(iscsi_opcode & ISCSI_OP_LOGIN)) { -		pr_err("First opcode is not login request," -			" failing login request.\n"); -		goto new_sess_out; -	} +	iscsi_start_login_thread_timer(np); -	pdu			= (struct iscsi_login_req *) buffer; +	pr_debug("Moving to TARG_CONN_STATE_XPT_UP.\n"); +	conn->conn_state = TARG_CONN_STATE_XPT_UP; +	/* +	 * This will process the first login request + payload.. +	 */ +	rc = np->np_transport->iscsit_get_login_rx(conn, login); +	if (rc == 1) +		return 1; +	else if (rc < 0) +		goto new_sess_out; +	buffer = &login->req[0]; +	pdu = (struct iscsi_login_req *)buffer;  	/*  	 * Used by iscsit_tx_login_rsp() for Login Resonses PDUs  	 * when Status-Class != 0.  	*/ -	conn->login_itt		= pdu->itt; +	conn->login_itt	= pdu->itt;  	spin_lock_bh(&np->np_thread_lock);  	if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) { @@ -967,61 +1221,11 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)  	}  	spin_unlock_bh(&np->np_thread_lock); -	if (np->np_sockaddr.ss_family == AF_INET6) { -		memset(&sock_in6, 0, sizeof(struct sockaddr_in6)); - -		if (conn->sock->ops->getname(conn->sock, -				(struct sockaddr *)&sock_in6, &err, 1) < 0) { -			pr_err("sock_ops->getname() failed.\n"); -			iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, -					ISCSI_LOGIN_STATUS_TARGET_ERROR); -			goto new_sess_out; -		} -		snprintf(conn->login_ip, sizeof(conn->login_ip), "%pI6c", -				&sock_in6.sin6_addr.in6_u); -		conn->login_port = ntohs(sock_in6.sin6_port); - -		if (conn->sock->ops->getname(conn->sock, -				(struct sockaddr *)&sock_in6, &err, 0) < 0) { -			pr_err("sock_ops->getname() failed.\n"); -			iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, -					ISCSI_LOGIN_STATUS_TARGET_ERROR); -			goto new_sess_out; -		} -		snprintf(conn->local_ip, sizeof(conn->local_ip), "%pI6c", -				&sock_in6.sin6_addr.in6_u); -		conn->local_port = ntohs(sock_in6.sin6_port); - -	} else { -		memset(&sock_in, 0, sizeof(struct sockaddr_in)); - -		if (conn->sock->ops->getname(conn->sock, -				(struct sockaddr *)&sock_in, &err, 1) < 0) { -			pr_err("sock_ops->getname() failed.\n"); -			iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, -					ISCSI_LOGIN_STATUS_TARGET_ERROR); -			goto new_sess_out; -		} -		sprintf(conn->login_ip, "%pI4", &sock_in.sin_addr.s_addr); -		conn->login_port = ntohs(sock_in.sin_port); - -		if (conn->sock->ops->getname(conn->sock, -				(struct sockaddr *)&sock_in, &err, 0) < 0) { -			pr_err("sock_ops->getname() failed.\n"); -			iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, -					ISCSI_LOGIN_STATUS_TARGET_ERROR); -			goto new_sess_out; -		} -		sprintf(conn->local_ip, "%pI4", &sock_in.sin_addr.s_addr); -		conn->local_port = ntohs(sock_in.sin_port); -	} -  	conn->network_transport = np->np_network_transport;  	pr_debug("Received iSCSI login request from %s on %s Network" -			" Portal %s:%hu\n", conn->login_ip, -		(conn->network_transport == ISCSI_TCP) ? "TCP" : "SCTP", -			conn->local_ip, conn->local_port); +		" Portal %s:%hu\n", conn->login_ip, np->np_transport->name, +		conn->local_ip, conn->local_port);  	pr_debug("Moving to TARG_CONN_STATE_IN_LOGIN.\n");  	conn->conn_state	= TARG_CONN_STATE_IN_LOGIN; @@ -1050,13 +1254,17 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)  		if (iscsi_login_non_zero_tsih_s1(conn, buffer) < 0)  			goto new_sess_out;  	} -  	/* -	 * This will process the first login request, and call -	 * iscsi_target_locate_portal(), and return a valid struct iscsi_login. +	 * SessionType: Discovery +	 * +	 * 	Locates Default Portal +	 * +	 * SessionType: Normal +	 * +	 * 	Locates Target Portal from NP -> Target IQN  	 */ -	login = iscsi_target_init_negotiation(np, conn, buffer); -	if (!login) { +	rc = iscsi_target_locate_portal(np, conn, login); +	if (rc < 0) {  		tpg = conn->tpg;  		goto new_sess_out;  	} @@ -1068,15 +1276,11 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)  	}  	if (zero_tsih) { -		if (iscsi_login_zero_tsih_s2(conn) < 0) { -			iscsi_target_nego_release(login, conn); +		if (iscsi_login_zero_tsih_s2(conn) < 0)  			goto new_sess_out; -		}  	} else { -		if (iscsi_login_non_zero_tsih_s2(conn, buffer) < 0) { -			iscsi_target_nego_release(login, conn); +		if (iscsi_login_non_zero_tsih_s2(conn, buffer) < 0)  			goto old_sess_out; -		}  	}  	if (iscsi_target_start_negotiation(login, conn) < 0) @@ -1153,8 +1357,18 @@ old_sess_out:  		iscsi_release_param_list(conn->param_list);  		conn->param_list = NULL;  	} -	if (conn->sock) +	iscsi_target_nego_release(conn); + +	if (conn->sock) {  		sock_release(conn->sock); +		conn->sock = NULL; +	} + +	if (conn->conn_transport->iscsit_free_conn) +		conn->conn_transport->iscsit_free_conn(conn); + +	iscsit_put_transport(conn->conn_transport); +  	kfree(conn);  	if (tpg) { @@ -1172,11 +1386,13 @@ out:  	/* Wait for another socket.. */  	if (!stop)  		return 1; - +exit:  	iscsi_stop_login_thread_timer(np);  	spin_lock_bh(&np->np_thread_lock);  	np->np_thread_state = ISCSI_NP_THREAD_EXIT; +	np->np_thread = NULL;  	spin_unlock_bh(&np->np_thread_lock); +  	return 0;  } diff --git a/drivers/target/iscsi/iscsi_target_login.h b/drivers/target/iscsi/iscsi_target_login.h index 091dcae2532..63efd287845 100644 --- a/drivers/target/iscsi/iscsi_target_login.h +++ b/drivers/target/iscsi/iscsi_target_login.h @@ -4,8 +4,14 @@  extern int iscsi_login_setup_crypto(struct iscsi_conn *);  extern int iscsi_check_for_session_reinstatement(struct iscsi_conn *);  extern int iscsi_login_post_auth_non_zero_tsih(struct iscsi_conn *, u16, u32); +extern int iscsit_setup_np(struct iscsi_np *, +				struct __kernel_sockaddr_storage *);  extern int iscsi_target_setup_login_socket(struct iscsi_np *,  				struct __kernel_sockaddr_storage *); +extern int iscsit_accept_np(struct iscsi_np *, struct iscsi_conn *); +extern int iscsit_get_login_rx(struct iscsi_conn *, struct iscsi_login *); +extern int iscsit_put_login_tx(struct iscsi_conn *, struct iscsi_login *, u32); +extern void iscsit_free_conn(struct iscsi_np *, struct iscsi_conn *);  extern int iscsi_target_login_thread(void *);  extern int iscsi_login_disable_FIM_keys(struct iscsi_param_list *, struct iscsi_conn *); diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c index 9d902aefe01..7ad912060e2 100644 --- a/drivers/target/iscsi/iscsi_target_nego.c +++ b/drivers/target/iscsi/iscsi_target_nego.c @@ -22,6 +22,7 @@  #include <scsi/iscsi_proto.h>  #include <target/target_core_base.h>  #include <target/target_core_fabric.h> +#include <target/iscsi/iscsi_transport.h>  #include "iscsi_target_core.h"  #include "iscsi_target_parameters.h" @@ -169,7 +170,7 @@ static void iscsi_remove_failed_auth_entry(struct iscsi_conn *conn)  	kfree(conn->auth_protocol);  } -static int iscsi_target_check_login_request( +int iscsi_target_check_login_request(  	struct iscsi_conn *conn,  	struct iscsi_login *login)  { @@ -200,8 +201,8 @@ static int iscsi_target_check_login_request(  		return -1;  	} -	req_csg = (login_req->flags & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) >> 2; -	req_nsg = (login_req->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK); +	req_csg = ISCSI_LOGIN_CURRENT_STAGE(login_req->flags); +	req_nsg = ISCSI_LOGIN_NEXT_STAGE(login_req->flags);  	if (req_csg != login->current_stage) {  		pr_err("Initiator unexpectedly changed login stage" @@ -352,11 +353,8 @@ static int iscsi_target_do_tx_login_io(struct iscsi_conn *conn, struct iscsi_log  	padding = ((-login->rsp_length) & 3); -	if (iscsi_login_tx_data( -			conn, -			login->rsp, -			login->rsp_buf, -			login->rsp_length + padding) < 0) +	if (conn->conn_transport->iscsit_put_login_tx(conn, login, +					login->rsp_length + padding) < 0)  		return -1;  	login->rsp_length		= 0; @@ -368,72 +366,12 @@ static int iscsi_target_do_tx_login_io(struct iscsi_conn *conn, struct iscsi_log  	return 0;  } -static int iscsi_target_do_rx_login_io(struct iscsi_conn *conn, struct iscsi_login *login) -{ -	u32 padding = 0, payload_length; -	struct iscsi_login_req *login_req; - -	if (iscsi_login_rx_data(conn, login->req, ISCSI_HDR_LEN) < 0) -		return -1; - -	login_req = (struct iscsi_login_req *) login->req; -	payload_length			= ntoh24(login_req->dlength); - -	pr_debug("Got Login Command, Flags 0x%02x, ITT: 0x%08x," -		" CmdSN: 0x%08x, ExpStatSN: 0x%08x, CID: %hu, Length: %u\n", -		 login_req->flags, login_req->itt, login_req->cmdsn, -		 login_req->exp_statsn, login_req->cid, payload_length); - -	if (iscsi_target_check_login_request(conn, login) < 0) -		return -1; - -	padding = ((-payload_length) & 3); -	memset(login->req_buf, 0, MAX_KEY_VALUE_PAIRS); - -	if (iscsi_login_rx_data( -			conn, -			login->req_buf, -			payload_length + padding) < 0) -		return -1; - -	return 0; -} -  static int iscsi_target_do_login_io(struct iscsi_conn *conn, struct iscsi_login *login)  {  	if (iscsi_target_do_tx_login_io(conn, login) < 0)  		return -1; -	if (iscsi_target_do_rx_login_io(conn, login) < 0) -		return -1; - -	return 0; -} - -static int iscsi_target_get_initial_payload( -	struct iscsi_conn *conn, -	struct iscsi_login *login) -{ -	u32 padding = 0, payload_length; -	struct iscsi_login_req *login_req; - -	login_req = (struct iscsi_login_req *) login->req; -	payload_length = ntoh24(login_req->dlength); - -	pr_debug("Got Login Command, Flags 0x%02x, ITT: 0x%08x," -		" CmdSN: 0x%08x, ExpStatSN: 0x%08x, Length: %u\n", -		login_req->flags, login_req->itt, login_req->cmdsn, -		login_req->exp_statsn, payload_length); - -	if (iscsi_target_check_login_request(conn, login) < 0) -		return -1; - -	padding = ((-payload_length) & 3); - -	if (iscsi_login_rx_data( -			conn, -			login->req_buf, -			payload_length + padding) < 0) +	if (conn->conn_transport->iscsit_get_login_rx(conn, login) < 0)  		return -1;  	return 0; @@ -681,9 +619,9 @@ static int iscsi_target_do_login(struct iscsi_conn *conn, struct iscsi_login *lo  			return -1;  		} -		switch ((login_req->flags & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) >> 2) { +		switch (ISCSI_LOGIN_CURRENT_STAGE(login_req->flags)) {  		case 0: -			login_rsp->flags |= (0 & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK); +			login_rsp->flags &= ~ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK;  			if (iscsi_target_handle_csg_zero(conn, login) < 0)  				return -1;  			break; @@ -693,6 +631,7 @@ static int iscsi_target_do_login(struct iscsi_conn *conn, struct iscsi_login *lo  				return -1;  			if (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) {  				login->tsih = conn->sess->tsih; +				login->login_complete = 1;  				if (iscsi_target_do_tx_login_io(conn,  						login) < 0)  					return -1; @@ -702,8 +641,7 @@ static int iscsi_target_do_login(struct iscsi_conn *conn, struct iscsi_login *lo  		default:  			pr_err("Illegal CSG: %d received from"  				" Initiator, protocol error.\n", -				(login_req->flags & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) -				>> 2); +				ISCSI_LOGIN_CURRENT_STAGE(login_req->flags));  			break;  		} @@ -737,7 +675,7 @@ static void iscsi_initiatorname_tolower(  /*   * Processes the first Login Request..   */ -static int iscsi_target_locate_portal( +int iscsi_target_locate_portal(  	struct iscsi_np *np,  	struct iscsi_conn *conn,  	struct iscsi_login *login) @@ -753,22 +691,6 @@ static int iscsi_target_locate_portal(  	login_req = (struct iscsi_login_req *) login->req;  	payload_length = ntoh24(login_req->dlength); -	login->first_request	= 1; -	login->leading_connection = (!login_req->tsih) ? 1 : 0; -	login->current_stage	= -		(login_req->flags & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) >> 2; -	login->version_min	= login_req->min_version; -	login->version_max	= login_req->max_version; -	memcpy(login->isid, login_req->isid, 6); -	login->cmd_sn		= be32_to_cpu(login_req->cmdsn); -	login->init_task_tag	= login_req->itt; -	login->initial_exp_statsn = be32_to_cpu(login_req->exp_statsn); -	login->cid		= be16_to_cpu(login_req->cid); -	login->tsih		= be16_to_cpu(login_req->tsih); - -	if (iscsi_target_get_initial_payload(conn, login) < 0) -		return -1; -  	tmpbuf = kzalloc(payload_length + 1, GFP_KERNEL);  	if (!tmpbuf) {  		pr_err("Unable to allocate memory for tmpbuf.\n"); @@ -800,6 +722,8 @@ static int iscsi_target_locate_portal(  		start += strlen(key) + strlen(value) + 2;  	} +	printk("i_buf: %s, s_buf: %s, t_buf: %s\n", i_buf, s_buf, t_buf); +  	/*  	 * See 5.3.  Login Phase.  	 */ @@ -958,100 +882,30 @@ out:  	return ret;  } -struct iscsi_login *iscsi_target_init_negotiation( -	struct iscsi_np *np, -	struct iscsi_conn *conn, -	char *login_pdu) -{ -	struct iscsi_login *login; - -	login = kzalloc(sizeof(struct iscsi_login), GFP_KERNEL); -	if (!login) { -		pr_err("Unable to allocate memory for struct iscsi_login.\n"); -		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, -				ISCSI_LOGIN_STATUS_NO_RESOURCES); -		return NULL; -	} - -	login->req = kmemdup(login_pdu, ISCSI_HDR_LEN, GFP_KERNEL); -	if (!login->req) { -		pr_err("Unable to allocate memory for Login Request.\n"); -		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, -				ISCSI_LOGIN_STATUS_NO_RESOURCES); -		goto out; -	} - -	login->req_buf = kzalloc(MAX_KEY_VALUE_PAIRS, GFP_KERNEL); -	if (!login->req_buf) { -		pr_err("Unable to allocate memory for response buffer.\n"); -		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, -				ISCSI_LOGIN_STATUS_NO_RESOURCES); -		goto out; -	} -	/* -	 * SessionType: Discovery -	 * -	 *	Locates Default Portal -	 * -	 * SessionType: Normal -	 * -	 *	Locates Target Portal from NP -> Target IQN -	 */ -	if (iscsi_target_locate_portal(np, conn, login) < 0) { -		goto out; -	} - -	return login; -out: -	kfree(login->req); -	kfree(login->req_buf); -	kfree(login); - -	return NULL; -} -  int iscsi_target_start_negotiation(  	struct iscsi_login *login,  	struct iscsi_conn *conn)  { -	int ret = -1; - -	login->rsp = kzalloc(ISCSI_HDR_LEN, GFP_KERNEL); -	if (!login->rsp) { -		pr_err("Unable to allocate memory for" -				" Login Response.\n"); -		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, -				ISCSI_LOGIN_STATUS_NO_RESOURCES); -		ret = -1; -		goto out; -	} - -	login->rsp_buf = kzalloc(MAX_KEY_VALUE_PAIRS, GFP_KERNEL); -	if (!login->rsp_buf) { -		pr_err("Unable to allocate memory for" -			" request buffer.\n"); -		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, -				ISCSI_LOGIN_STATUS_NO_RESOURCES); -		ret = -1; -		goto out; -	} +	int ret;  	ret = iscsi_target_do_login(conn, login); -out:  	if (ret != 0)  		iscsi_remove_failed_auth_entry(conn); -	iscsi_target_nego_release(login, conn); +	iscsi_target_nego_release(conn);  	return ret;  } -void iscsi_target_nego_release( -	struct iscsi_login *login, -	struct iscsi_conn *conn) +void iscsi_target_nego_release(struct iscsi_conn *conn)  { -	kfree(login->req); -	kfree(login->rsp); +	struct iscsi_login *login = conn->conn_login; + +	if (!login) +		return; +  	kfree(login->req_buf);  	kfree(login->rsp_buf);  	kfree(login); + +	conn->conn_login = NULL;  } diff --git a/drivers/target/iscsi/iscsi_target_nego.h b/drivers/target/iscsi/iscsi_target_nego.h index 92e133a5158..f021cbd330e 100644 --- a/drivers/target/iscsi/iscsi_target_nego.h +++ b/drivers/target/iscsi/iscsi_target_nego.h @@ -7,11 +7,14 @@  extern void convert_null_to_semi(char *, int);  extern int extract_param(const char *, const char *, unsigned int, char *,  		unsigned char *); -extern struct iscsi_login *iscsi_target_init_negotiation( -		struct iscsi_np *, struct iscsi_conn *, char *); +extern int iscsi_target_check_login_request(struct iscsi_conn *, +		struct iscsi_login *); +extern int iscsi_target_get_initial_payload(struct iscsi_conn *, +		struct iscsi_login *); +extern int iscsi_target_locate_portal(struct iscsi_np *, struct iscsi_conn *, +		struct iscsi_login *);  extern int iscsi_target_start_negotiation(  		struct iscsi_login *, struct iscsi_conn *); -extern void iscsi_target_nego_release( -		struct iscsi_login *, struct iscsi_conn *); +extern void iscsi_target_nego_release(struct iscsi_conn *);  #endif /* ISCSI_TARGET_NEGO_H */ diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c index ca2be406f14..f690be9e529 100644 --- a/drivers/target/iscsi/iscsi_target_parameters.c +++ b/drivers/target/iscsi/iscsi_target_parameters.c @@ -59,7 +59,7 @@ int iscsi_login_tx_data(  	char *text_buf,  	int text_length)  { -	int length, tx_sent; +	int length, tx_sent, iov_cnt = 1;  	struct kvec iov[2];  	length = (ISCSI_HDR_LEN + text_length); @@ -67,8 +67,12 @@ int iscsi_login_tx_data(  	memset(&iov[0], 0, 2 * sizeof(struct kvec));  	iov[0].iov_len		= ISCSI_HDR_LEN;  	iov[0].iov_base		= pdu_buf; -	iov[1].iov_len		= text_length; -	iov[1].iov_base		= text_buf; + +	if (text_buf && text_length) { +		iov[1].iov_len	= text_length; +		iov[1].iov_base	= text_buf; +		iov_cnt++; +	}  	/*  	 * Initial Marker-less Interval. @@ -77,7 +81,7 @@ int iscsi_login_tx_data(  	 */  	conn->if_marker += length; -	tx_sent = tx_data(conn, &iov[0], 2, length); +	tx_sent = tx_data(conn, &iov[0], iov_cnt, length);  	if (tx_sent != length) {  		pr_err("tx_data returned %d, expecting %d.\n",  				tx_sent, length); @@ -429,6 +433,28 @@ int iscsi_create_default_params(struct iscsi_param_list **param_list_ptr)  			TYPERANGE_MARKINT, USE_INITIAL_ONLY);  	if (!param)  		goto out; +	/* +	 * Extra parameters for ISER from RFC-5046 +	 */ +	param = iscsi_set_default_param(pl, RDMAEXTENTIONS, INITIAL_RDMAEXTENTIONS, +			PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH, +			TYPERANGE_BOOL_AND, USE_LEADING_ONLY); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, INITIATORRECVDATASEGMENTLENGTH, +			INITIAL_INITIATORRECVDATASEGMENTLENGTH, +			PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH, +			TYPERANGE_512_TO_16777215, USE_ALL); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, TARGETRECVDATASEGMENTLENGTH, +			INITIAL_TARGETRECVDATASEGMENTLENGTH, +			PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH, +			TYPERANGE_512_TO_16777215, USE_ALL); +	if (!param) +		goto out;  	*param_list_ptr = pl;  	return 0; @@ -438,19 +464,23 @@ out:  }  int iscsi_set_keys_to_negotiate( -	int sessiontype, -	struct iscsi_param_list *param_list) +	struct iscsi_param_list *param_list, +	bool iser)  {  	struct iscsi_param *param; +	param_list->iser = iser; +  	list_for_each_entry(param, ¶m_list->param_list, p_list) {  		param->state = 0;  		if (!strcmp(param->name, AUTHMETHOD)) {  			SET_PSTATE_NEGOTIATE(param);  		} else if (!strcmp(param->name, HEADERDIGEST)) { -			SET_PSTATE_NEGOTIATE(param); +			if (iser == false) +				SET_PSTATE_NEGOTIATE(param);  		} else if (!strcmp(param->name, DATADIGEST)) { -			SET_PSTATE_NEGOTIATE(param); +			if (iser == false) +				SET_PSTATE_NEGOTIATE(param);  		} else if (!strcmp(param->name, MAXCONNECTIONS)) {  			SET_PSTATE_NEGOTIATE(param);  		} else if (!strcmp(param->name, TARGETNAME)) { @@ -469,7 +499,8 @@ int iscsi_set_keys_to_negotiate(  		} else if (!strcmp(param->name, IMMEDIATEDATA)) {  			SET_PSTATE_NEGOTIATE(param);  		} else if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH)) { -			SET_PSTATE_NEGOTIATE(param); +			if (iser == false) +				SET_PSTATE_NEGOTIATE(param);  		} else if (!strcmp(param->name, MAXXMITDATASEGMENTLENGTH)) {  			continue;  		} else if (!strcmp(param->name, MAXBURSTLENGTH)) { @@ -498,6 +529,15 @@ int iscsi_set_keys_to_negotiate(  			SET_PSTATE_NEGOTIATE(param);  		} else if (!strcmp(param->name, OFMARKINT)) {  			SET_PSTATE_NEGOTIATE(param); +		} else if (!strcmp(param->name, RDMAEXTENTIONS)) { +			if (iser == true) +				SET_PSTATE_NEGOTIATE(param); +		} else if (!strcmp(param->name, INITIATORRECVDATASEGMENTLENGTH)) { +			if (iser == true) +				SET_PSTATE_NEGOTIATE(param); +		} else if (!strcmp(param->name, TARGETRECVDATASEGMENTLENGTH)) { +			if (iser == true) +				SET_PSTATE_NEGOTIATE(param);  		}  	} @@ -540,6 +580,12 @@ int iscsi_set_keys_irrelevant_for_discovery(  			param->state &= ~PSTATE_NEGOTIATE;  		else if (!strcmp(param->name, OFMARKINT))  			param->state &= ~PSTATE_NEGOTIATE; +		else if (!strcmp(param->name, RDMAEXTENTIONS)) +			param->state &= ~PSTATE_NEGOTIATE; +		else if (!strcmp(param->name, INITIATORRECVDATASEGMENTLENGTH)) +			param->state &= ~PSTATE_NEGOTIATE; +		else if (!strcmp(param->name, TARGETRECVDATASEGMENTLENGTH)) +			param->state &= ~PSTATE_NEGOTIATE;  	}  	return 0; @@ -1755,6 +1801,9 @@ void iscsi_set_connection_parameters(  		 * this key is not sent over the wire.  		 */  		if (!strcmp(param->name, MAXXMITDATASEGMENTLENGTH)) { +			if (param_list->iser == true) +				continue; +  			ops->MaxXmitDataSegmentLength =  				simple_strtoul(param->value, &tmpptr, 0);  			pr_debug("MaxXmitDataSegmentLength:     %s\n", @@ -1800,6 +1849,22 @@ void iscsi_set_connection_parameters(  				simple_strtoul(param->value, &tmpptr, 0);  			pr_debug("IFMarkInt:                    %s\n",  				param->value); +		} else if (!strcmp(param->name, INITIATORRECVDATASEGMENTLENGTH)) { +			ops->InitiatorRecvDataSegmentLength = +				simple_strtoul(param->value, &tmpptr, 0); +			pr_debug("InitiatorRecvDataSegmentLength: %s\n", +				param->value); +			ops->MaxRecvDataSegmentLength = +					ops->InitiatorRecvDataSegmentLength; +			pr_debug("Set MRDSL from InitiatorRecvDataSegmentLength\n"); +		} else if (!strcmp(param->name, TARGETRECVDATASEGMENTLENGTH)) { +			ops->TargetRecvDataSegmentLength = +				simple_strtoul(param->value, &tmpptr, 0); +			pr_debug("TargetRecvDataSegmentLength:  %s\n", +				param->value); +			ops->MaxXmitDataSegmentLength = +					ops->TargetRecvDataSegmentLength; +			pr_debug("Set MXDSL from TargetRecvDataSegmentLength\n");  		}  	}  	pr_debug("----------------------------------------------------" @@ -1912,6 +1977,10 @@ void iscsi_set_session_parameters(  			ops->SessionType = !strcmp(param->value, DISCOVERY);  			pr_debug("SessionType:                  %s\n",  				param->value); +		} else if (!strcmp(param->name, RDMAEXTENTIONS)) { +			ops->RDMAExtensions = !strcmp(param->value, YES); +			pr_debug("RDMAExtensions:               %s\n", +				param->value);  		}  	}  	pr_debug("----------------------------------------------------" diff --git a/drivers/target/iscsi/iscsi_target_parameters.h b/drivers/target/iscsi/iscsi_target_parameters.h index 1e1b7504a76..f31b9c4b83f 100644 --- a/drivers/target/iscsi/iscsi_target_parameters.h +++ b/drivers/target/iscsi/iscsi_target_parameters.h @@ -27,7 +27,7 @@ extern void iscsi_dump_conn_ops(struct iscsi_conn_ops *);  extern void iscsi_dump_sess_ops(struct iscsi_sess_ops *);  extern void iscsi_print_params(struct iscsi_param_list *);  extern int iscsi_create_default_params(struct iscsi_param_list **); -extern int iscsi_set_keys_to_negotiate(int, struct iscsi_param_list *); +extern int iscsi_set_keys_to_negotiate(struct iscsi_param_list *, bool);  extern int iscsi_set_keys_irrelevant_for_discovery(struct iscsi_param_list *);  extern int iscsi_copy_param_list(struct iscsi_param_list **,  			struct iscsi_param_list *, int); @@ -89,6 +89,13 @@ extern void iscsi_set_session_parameters(struct iscsi_sess_ops *,  #define X_EXTENSIONKEY_CISCO_OLD	"X-com.cisco.iscsi.draft"  /* + * Parameter names of iSCSI Extentions for RDMA (iSER).  See RFC-5046 + */ +#define RDMAEXTENTIONS			"RDMAExtensions" +#define INITIATORRECVDATASEGMENTLENGTH	"InitiatorRecvDataSegmentLength" +#define TARGETRECVDATASEGMENTLENGTH	"TargetRecvDataSegmentLength" + +/*   * For AuthMethod.   */  #define KRB5				"KRB5" @@ -133,6 +140,13 @@ extern void iscsi_set_session_parameters(struct iscsi_sess_ops *,  #define INITIAL_OFMARKINT			"2048~65535"  /* + * Initial values for iSER parameters following RFC-5046 Section 6 + */ +#define INITIAL_RDMAEXTENTIONS			NO +#define INITIAL_INITIATORRECVDATASEGMENTLENGTH	"262144" +#define INITIAL_TARGETRECVDATASEGMENTLENGTH	"8192" + +/*   * For [Header,Data]Digests.   */  #define CRC32C				"CRC32C" diff --git a/drivers/target/iscsi/iscsi_target_tmr.c b/drivers/target/iscsi/iscsi_target_tmr.c index 9d4417aae92..b997e5da47d 100644 --- a/drivers/target/iscsi/iscsi_target_tmr.c +++ b/drivers/target/iscsi/iscsi_target_tmr.c @@ -23,6 +23,7 @@  #include <scsi/iscsi_proto.h>  #include <target/target_core_base.h>  #include <target/target_core_fabric.h> +#include <target/iscsi/iscsi_transport.h>  #include "iscsi_target_core.h"  #include "iscsi_target_seq_pdu_list.h" @@ -301,7 +302,7 @@ static int iscsit_task_reassign_complete_write(  	/*  	 * iscsit_build_r2ts_for_cmd() can handle the rest from here.  	 */ -	return iscsit_build_r2ts_for_cmd(cmd, conn, true); +	return conn->conn_transport->iscsit_get_dataout(conn, cmd, true);  }  static int iscsit_task_reassign_complete_read( @@ -471,6 +472,7 @@ int iscsit_tmr_post_handler(struct iscsi_cmd *cmd, struct iscsi_conn *conn)  	return 0;  } +EXPORT_SYMBOL(iscsit_tmr_post_handler);  /*   *	Nothing to do here, but leave it for good measure. :-) diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c index ee8f8c66248..439260b7d87 100644 --- a/drivers/target/iscsi/iscsi_target_tpg.c +++ b/drivers/target/iscsi/iscsi_target_tpg.c @@ -31,6 +31,8 @@  #include "iscsi_target.h"  #include "iscsi_target_parameters.h" +#include <target/iscsi/iscsi_transport.h> +  struct iscsi_portal_group *iscsit_alloc_portal_group(struct iscsi_tiqn *tiqn, u16 tpgt)  {  	struct iscsi_portal_group *tpg; @@ -508,7 +510,7 @@ struct iscsi_tpg_np *iscsit_tpg_add_network_portal(  	pr_debug("CORE[%s] - Added Network Portal: %s:%hu,%hu on %s\n",  		tpg->tpg_tiqn->tiqn, np->np_ip, np->np_port, tpg->tpgt, -		(np->np_network_transport == ISCSI_TCP) ? "TCP" : "SCTP"); +		np->np_transport->name);  	return tpg_np;  } @@ -522,7 +524,7 @@ static int iscsit_tpg_release_np(  	pr_debug("CORE[%s] - Removed Network Portal: %s:%hu,%hu on %s\n",  		tpg->tpg_tiqn->tiqn, np->np_ip, np->np_port, tpg->tpgt, -		(np->np_network_transport == ISCSI_TCP) ? "TCP" : "SCTP"); +		np->np_transport->name);  	tpg_np->tpg_np = NULL;  	tpg_np->tpg = NULL; diff --git a/drivers/target/iscsi/iscsi_target_transport.c b/drivers/target/iscsi/iscsi_target_transport.c new file mode 100644 index 00000000000..882728fac30 --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_transport.c @@ -0,0 +1,55 @@ +#include <linux/spinlock.h> +#include <linux/list.h> +#include <target/iscsi/iscsi_transport.h> + +static LIST_HEAD(g_transport_list); +static DEFINE_MUTEX(transport_mutex); + +struct iscsit_transport *iscsit_get_transport(int type) +{ +	struct iscsit_transport *t; + +	mutex_lock(&transport_mutex); +	list_for_each_entry(t, &g_transport_list, t_node) { +		if (t->transport_type == type) { +			if (t->owner && !try_module_get(t->owner)) { +				t = NULL; +			} +			mutex_unlock(&transport_mutex); +			return t; +		} +	} +	mutex_unlock(&transport_mutex); + +	return NULL; +} + +void iscsit_put_transport(struct iscsit_transport *t) +{ +	if (t->owner) +		module_put(t->owner); +} + +int iscsit_register_transport(struct iscsit_transport *t) +{ +	INIT_LIST_HEAD(&t->t_node); + +	mutex_lock(&transport_mutex); +	list_add_tail(&t->t_node, &g_transport_list); +	mutex_unlock(&transport_mutex); + +	pr_debug("Registered iSCSI transport: %s\n", t->name); + +	return 0; +} +EXPORT_SYMBOL(iscsit_register_transport); + +void iscsit_unregister_transport(struct iscsit_transport *t) +{ +	mutex_lock(&transport_mutex); +	list_del(&t->t_node); +	mutex_unlock(&transport_mutex); + +	pr_debug("Unregistered iSCSI transport: %s\n", t->name); +} +EXPORT_SYMBOL(iscsit_unregister_transport); diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index 7ce350578c8..2cc6c9a3ffb 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c @@ -24,6 +24,7 @@  #include <target/target_core_base.h>  #include <target/target_core_fabric.h>  #include <target/target_core_configfs.h> +#include <target/iscsi/iscsi_transport.h>  #include "iscsi_target_core.h"  #include "iscsi_target_parameters.h" @@ -148,6 +149,18 @@ void iscsit_free_r2ts_from_list(struct iscsi_cmd *cmd)  	spin_unlock_bh(&cmd->r2t_lock);  } +struct iscsi_cmd *iscsit_alloc_cmd(struct iscsi_conn *conn, gfp_t gfp_mask) +{ +	struct iscsi_cmd *cmd; + +	cmd = kmem_cache_zalloc(lio_cmd_cache, gfp_mask); +	if (!cmd) +		return NULL; + +	cmd->release_cmd = &iscsit_release_cmd; +	return cmd; +} +  /*   * May be called from software interrupt (timer) context for allocating   * iSCSI NopINs. @@ -156,13 +169,12 @@ struct iscsi_cmd *iscsit_allocate_cmd(struct iscsi_conn *conn, gfp_t gfp_mask)  {  	struct iscsi_cmd *cmd; -	cmd = kmem_cache_zalloc(lio_cmd_cache, gfp_mask); +	cmd = conn->conn_transport->iscsit_alloc_cmd(conn, gfp_mask);  	if (!cmd) {  		pr_err("Unable to allocate memory for struct iscsi_cmd.\n");  		return NULL;  	} - -	cmd->conn	= conn; +	cmd->conn = conn;  	INIT_LIST_HEAD(&cmd->i_conn_node);  	INIT_LIST_HEAD(&cmd->datain_list);  	INIT_LIST_HEAD(&cmd->cmd_r2t_list); @@ -175,6 +187,7 @@ struct iscsi_cmd *iscsit_allocate_cmd(struct iscsi_conn *conn, gfp_t gfp_mask)  	return cmd;  } +EXPORT_SYMBOL(iscsit_allocate_cmd);  struct iscsi_seq *iscsit_get_seq_holder_for_datain(  	struct iscsi_cmd *cmd, @@ -304,6 +317,7 @@ int iscsit_sequence_cmd(  	return ret;  } +EXPORT_SYMBOL(iscsit_sequence_cmd);  int iscsit_check_unsolicited_dataout(struct iscsi_cmd *cmd, unsigned char *buf)  { @@ -689,6 +703,11 @@ void iscsit_free_cmd(struct iscsi_cmd *cmd)  	 */  	switch (cmd->iscsi_opcode) {  	case ISCSI_OP_SCSI_CMD: +		if (cmd->data_direction == DMA_TO_DEVICE) +			iscsit_stop_dataout_timer(cmd); +		/* +		 * Fallthrough +		 */  	case ISCSI_OP_SCSI_TMFUNC:  		transport_generic_free_cmd(&cmd->se_cmd, 1);  		break; @@ -704,7 +723,7 @@ void iscsit_free_cmd(struct iscsi_cmd *cmd)  		}  		/* Fall-through */  	default: -		iscsit_release_cmd(cmd); +		cmd->release_cmd(cmd);  		break;  	}  } @@ -1226,34 +1245,19 @@ send_datacrc:   */  int iscsit_tx_login_rsp(struct iscsi_conn *conn, u8 status_class, u8 status_detail)  { -	u8 iscsi_hdr[ISCSI_HDR_LEN]; -	int err; -	struct kvec iov;  	struct iscsi_login_rsp *hdr; +	struct iscsi_login *login = conn->conn_login; +	login->login_failed = 1;  	iscsit_collect_login_stats(conn, status_class, status_detail); -	memset(&iov, 0, sizeof(struct kvec)); -	memset(&iscsi_hdr, 0x0, ISCSI_HDR_LEN); - -	hdr	= (struct iscsi_login_rsp *)&iscsi_hdr; +	hdr	= (struct iscsi_login_rsp *)&login->rsp[0];  	hdr->opcode		= ISCSI_OP_LOGIN_RSP;  	hdr->status_class	= status_class;  	hdr->status_detail	= status_detail;  	hdr->itt		= conn->login_itt; -	iov.iov_base		= &iscsi_hdr; -	iov.iov_len		= ISCSI_HDR_LEN; - -	PRINT_BUFF(iscsi_hdr, ISCSI_HDR_LEN); - -	err = tx_data(conn, &iov, 1, ISCSI_HDR_LEN); -	if (err != ISCSI_HDR_LEN) { -		pr_err("tx_data returned less than expected\n"); -		return -1; -	} - -	return 0; +	return conn->conn_transport->iscsit_put_login_tx(conn, login, 0);  }  void iscsit_print_session_params(struct iscsi_session *sess) @@ -1432,7 +1436,8 @@ void iscsit_collect_login_stats(  		strcpy(ls->last_intr_fail_name,  		       (intrname ? intrname->value : "Unknown")); -		ls->last_intr_fail_ip_family = conn->sock->sk->sk_family; +		ls->last_intr_fail_ip_family = conn->login_family; +  		snprintf(ls->last_intr_fail_ip_addr, IPV6_ADDRESS_SPACE,  				"%s", conn->login_ip);  		ls->last_fail_time = get_jiffies_64(); diff --git a/drivers/target/iscsi/iscsi_target_util.h b/drivers/target/iscsi/iscsi_target_util.h index 894d0f83792..4f8e01a4708 100644 --- a/drivers/target/iscsi/iscsi_target_util.h +++ b/drivers/target/iscsi/iscsi_target_util.h @@ -8,6 +8,7 @@ extern struct iscsi_r2t *iscsit_get_r2t_for_eos(struct iscsi_cmd *, u32, u32);  extern struct iscsi_r2t *iscsit_get_r2t_from_list(struct iscsi_cmd *);  extern void iscsit_free_r2t(struct iscsi_r2t *, struct iscsi_cmd *);  extern void iscsit_free_r2ts_from_list(struct iscsi_cmd *); +extern struct iscsi_cmd *iscsit_alloc_cmd(struct iscsi_conn *, gfp_t);  extern struct iscsi_cmd *iscsit_allocate_cmd(struct iscsi_conn *, gfp_t);  extern struct iscsi_seq *iscsit_get_seq_holder_for_datain(struct iscsi_cmd *, u32);  extern struct iscsi_seq *iscsit_get_seq_holder_for_r2t(struct iscsi_cmd *); diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index 2d444b1ccd3..7c908141cc8 100644 --- a/drivers/target/loopback/tcm_loop.c +++ b/drivers/target/loopback/tcm_loop.c @@ -79,11 +79,10 @@ static void tcm_loop_release_cmd(struct se_cmd *se_cmd)  	kmem_cache_free(tcm_loop_cmd_cache, tl_cmd);  } -static int tcm_loop_proc_info(struct Scsi_Host *host, char *buffer, -				char **start, off_t offset, -				int length, int inout) +static int tcm_loop_show_info(struct seq_file *m, struct Scsi_Host *host)  { -	return sprintf(buffer, "tcm_loop_proc_info()\n"); +	seq_printf(m, "tcm_loop_proc_info()\n"); +	return 0;  }  static int tcm_loop_driver_probe(struct device *); @@ -336,7 +335,7 @@ static int tcm_loop_slave_configure(struct scsi_device *sd)  }  static struct scsi_host_template tcm_loop_driver_template = { -	.proc_info		= tcm_loop_proc_info, +	.show_info		= tcm_loop_show_info,  	.proc_name		= "tcm_loopback",  	.name			= "TCM_Loopback",  	.queuecommand		= tcm_loop_queuecommand, diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c index ff1c5ee352c..cbe48ab4174 100644 --- a/drivers/target/target_core_alua.c +++ b/drivers/target/target_core_alua.c @@ -409,6 +409,7 @@ static inline int core_alua_state_standby(  	case REPORT_LUNS:  	case RECEIVE_DIAGNOSTIC:  	case SEND_DIAGNOSTIC: +		return 0;  	case MAINTENANCE_IN:  		switch (cdb[1] & 0x1f) {  		case MI_REPORT_TARGET_PGS: @@ -451,6 +452,7 @@ static inline int core_alua_state_unavailable(  	switch (cdb[0]) {  	case INQUIRY:  	case REPORT_LUNS: +		return 0;  	case MAINTENANCE_IN:  		switch (cdb[1] & 0x1f) {  		case MI_REPORT_TARGET_PGS: @@ -491,6 +493,7 @@ static inline int core_alua_state_transition(  	switch (cdb[0]) {  	case INQUIRY:  	case REPORT_LUNS: +		return 0;  	case MAINTENANCE_IN:  		switch (cdb[1] & 0x1f) {  		case MI_REPORT_TARGET_PGS: diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c index 17a6acbc3ab..58ed683e04a 100644 --- a/drivers/target/target_core_file.c +++ b/drivers/target/target_core_file.c @@ -30,8 +30,10 @@  #include <linux/slab.h>  #include <linux/spinlock.h>  #include <linux/module.h> +#include <linux/falloc.h>  #include <scsi/scsi.h>  #include <scsi/scsi_host.h> +#include <asm/unaligned.h>  #include <target/target_core_base.h>  #include <target/target_core_backend.h> @@ -166,6 +168,33 @@ static int fd_configure_device(struct se_device *dev)  			" block_device blocks: %llu logical_block_size: %d\n",  			dev_size, div_u64(dev_size, fd_dev->fd_block_size),  			fd_dev->fd_block_size); +		/* +		 * Check if the underlying struct block_device request_queue supports +		 * the QUEUE_FLAG_DISCARD bit for UNMAP/WRITE_SAME in SCSI + TRIM +		 * in ATA and we need to set TPE=1 +		 */ +		if (blk_queue_discard(q)) { +			dev->dev_attrib.max_unmap_lba_count = +				q->limits.max_discard_sectors; +			/* +			 * Currently hardcoded to 1 in Linux/SCSI code.. +			 */ +			dev->dev_attrib.max_unmap_block_desc_count = 1; +			dev->dev_attrib.unmap_granularity = +				q->limits.discard_granularity >> 9; +			dev->dev_attrib.unmap_granularity_alignment = +				q->limits.discard_alignment; +			pr_debug("IFILE: BLOCK Discard support available," +					" disabled by default\n"); +		} +		/* +		 * Enable write same emulation for IBLOCK and use 0xFFFF as +		 * the smaller WRITE_SAME(10) only has a two-byte block count. +		 */ +		dev->dev_attrib.max_write_same_len = 0xFFFF; + +		if (blk_queue_nonrot(q)) +			dev->dev_attrib.is_nonrot = 1;  	} else {  		if (!(fd_dev->fbd_flags & FBDF_HAS_SIZE)) {  			pr_err("FILEIO: Missing fd_dev_size=" @@ -176,6 +205,23 @@ static int fd_configure_device(struct se_device *dev)  		dev->dev_attrib.hw_block_size = FD_BLOCKSIZE;  		dev->dev_attrib.hw_max_sectors = FD_MAX_SECTORS; + +		/* +		 * Limit UNMAP emulation to 8k Number of LBAs (NoLB) +		 */ +		dev->dev_attrib.max_unmap_lba_count = 0x2000; +		/* +		 * Currently hardcoded to 1 in Linux/SCSI code.. +		 */ +		dev->dev_attrib.max_unmap_block_desc_count = 1; +		dev->dev_attrib.unmap_granularity = 1; +		dev->dev_attrib.unmap_granularity_alignment = 0; + +		/* +		 * Limit WRITE_SAME w/ UNMAP=0 emulation to 8k Number of LBAs (NoLB) +		 * based upon struct iovec limit for vfs_writev() +		 */ +		dev->dev_attrib.max_write_same_len = 0x1000;  	}  	fd_dev->fd_block_size = dev->dev_attrib.hw_block_size; @@ -190,11 +236,6 @@ static int fd_configure_device(struct se_device *dev)  	fd_dev->fd_dev_id = fd_host->fd_host_dev_id_count++;  	fd_dev->fd_queue_depth = dev->queue_depth; -	/* -	 * Limit WRITE_SAME w/ UNMAP=0 emulation to 8k Number of LBAs (NoLB) -	 * based upon struct iovec limit for vfs_writev() -	 */ -	dev->dev_attrib.max_write_same_len = 0x1000;  	pr_debug("CORE_FILE[%u] - Added TCM FILEIO Device ID: %u at %s,"  		" %llu total bytes\n", fd_host->fd_host_id, fd_dev->fd_dev_id, @@ -442,6 +483,75 @@ fd_execute_write_same(struct se_cmd *cmd)  }  static sense_reason_t +fd_do_unmap(struct se_cmd *cmd, void *priv, sector_t lba, sector_t nolb) +{ +	struct file *file = priv; +	struct inode *inode = file->f_mapping->host; +	int ret; + +	if (S_ISBLK(inode->i_mode)) { +		/* The backend is block device, use discard */ +		struct block_device *bdev = inode->i_bdev; + +		ret = blkdev_issue_discard(bdev, lba, +				nolb, GFP_KERNEL, 0); +		if (ret < 0) { +			pr_warn("FILEIO: blkdev_issue_discard() failed: %d\n", +				ret); +			return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; +		} +	} else { +		/* The backend is normal file, use fallocate */ +		struct se_device *se_dev = cmd->se_dev; +		loff_t pos = lba * se_dev->dev_attrib.block_size; +		unsigned int len = nolb * se_dev->dev_attrib.block_size; +		int mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE; + +		if (!file->f_op->fallocate) +			return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + +		ret = file->f_op->fallocate(file, mode, pos, len); +		if (ret < 0) { +			pr_warn("FILEIO: fallocate() failed: %d\n", ret); +			return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; +		} +	} + +	return 0; +} + +static sense_reason_t +fd_execute_write_same_unmap(struct se_cmd *cmd) +{ +	struct se_device *se_dev = cmd->se_dev; +	struct fd_dev *fd_dev = FD_DEV(se_dev); +	struct file *file = fd_dev->fd_file; +	sector_t lba = cmd->t_task_lba; +	sector_t nolb = sbc_get_write_same_sectors(cmd); +	int ret; + +	if (!nolb) { +		target_complete_cmd(cmd, SAM_STAT_GOOD); +		return 0; +	} + +	ret = fd_do_unmap(cmd, file, lba, nolb); +	if (ret) +		return ret; + +	target_complete_cmd(cmd, GOOD); +	return 0; +} + +static sense_reason_t +fd_execute_unmap(struct se_cmd *cmd) +{ +	struct file *file = FD_DEV(cmd->se_dev)->fd_file; + +	return sbc_execute_unmap(cmd, fd_do_unmap, file); +} + +static sense_reason_t  fd_execute_rw(struct se_cmd *cmd)  {  	struct scatterlist *sgl = cmd->t_data_sg; @@ -600,6 +710,8 @@ static struct sbc_ops fd_sbc_ops = {  	.execute_rw		= fd_execute_rw,  	.execute_sync_cache	= fd_execute_sync_cache,  	.execute_write_same	= fd_execute_write_same, +	.execute_write_same_unmap = fd_execute_write_same_unmap, +	.execute_unmap		= fd_execute_unmap,  };  static sense_reason_t diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index 8bcc514ec8b..07f5f94634b 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -380,104 +380,40 @@ iblock_execute_sync_cache(struct se_cmd *cmd)  }  static sense_reason_t -iblock_execute_unmap(struct se_cmd *cmd) +iblock_do_unmap(struct se_cmd *cmd, void *priv, +		sector_t lba, sector_t nolb)  { -	struct se_device *dev = cmd->se_dev; -	struct iblock_dev *ib_dev = IBLOCK_DEV(dev); -	unsigned char *buf, *ptr = NULL; -	sector_t lba; -	int size; -	u32 range; -	sense_reason_t ret = 0; -	int dl, bd_dl, err; - -	/* We never set ANC_SUP */ -	if (cmd->t_task_cdb[1]) -		return TCM_INVALID_CDB_FIELD; - -	if (cmd->data_length == 0) { -		target_complete_cmd(cmd, SAM_STAT_GOOD); -		return 0; -	} - -	if (cmd->data_length < 8) { -		pr_warn("UNMAP parameter list length %u too small\n", -			cmd->data_length); -		return TCM_PARAMETER_LIST_LENGTH_ERROR; -	} +	struct block_device *bdev = priv; +	int ret; -	buf = transport_kmap_data_sg(cmd); -	if (!buf) +	ret = blkdev_issue_discard(bdev, lba, nolb, GFP_KERNEL, 0); +	if (ret < 0) { +		pr_err("blkdev_issue_discard() failed: %d\n", ret);  		return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - -	dl = get_unaligned_be16(&buf[0]); -	bd_dl = get_unaligned_be16(&buf[2]); - -	size = cmd->data_length - 8; -	if (bd_dl > size) -		pr_warn("UNMAP parameter list length %u too small, ignoring bd_dl %u\n", -			cmd->data_length, bd_dl); -	else -		size = bd_dl; - -	if (size / 16 > dev->dev_attrib.max_unmap_block_desc_count) { -		ret = TCM_INVALID_PARAMETER_LIST; -		goto err;  	} -	/* First UNMAP block descriptor starts at 8 byte offset */ -	ptr = &buf[8]; -	pr_debug("UNMAP: Sub: %s Using dl: %u bd_dl: %u size: %u" -		" ptr: %p\n", dev->transport->name, dl, bd_dl, size, ptr); - -	while (size >= 16) { -		lba = get_unaligned_be64(&ptr[0]); -		range = get_unaligned_be32(&ptr[8]); -		pr_debug("UNMAP: Using lba: %llu and range: %u\n", -				 (unsigned long long)lba, range); - -		if (range > dev->dev_attrib.max_unmap_lba_count) { -			ret = TCM_INVALID_PARAMETER_LIST; -			goto err; -		} - -		if (lba + range > dev->transport->get_blocks(dev) + 1) { -			ret = TCM_ADDRESS_OUT_OF_RANGE; -			goto err; -		} - -		err = blkdev_issue_discard(ib_dev->ibd_bd, lba, range, -					   GFP_KERNEL, 0); -		if (err < 0) { -			pr_err("blkdev_issue_discard() failed: %d\n", -					err); -			ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; -			goto err; -		} +	return 0; +} -		ptr += 16; -		size -= 16; -	} +static sense_reason_t +iblock_execute_unmap(struct se_cmd *cmd) +{ +	struct block_device *bdev = IBLOCK_DEV(cmd->se_dev)->ibd_bd; -err: -	transport_kunmap_data_sg(cmd); -	if (!ret) -		target_complete_cmd(cmd, GOOD); -	return ret; +	return sbc_execute_unmap(cmd, iblock_do_unmap, bdev);  }  static sense_reason_t  iblock_execute_write_same_unmap(struct se_cmd *cmd)  { -	struct iblock_dev *ib_dev = IBLOCK_DEV(cmd->se_dev); -	int rc; +	struct block_device *bdev = IBLOCK_DEV(cmd->se_dev)->ibd_bd; +	sector_t lba = cmd->t_task_lba; +	sector_t nolb = sbc_get_write_same_sectors(cmd); +	int ret; -	rc = blkdev_issue_discard(ib_dev->ibd_bd, cmd->t_task_lba, -			sbc_get_write_same_sectors(cmd), GFP_KERNEL, 0); -	if (rc < 0) { -		pr_warn("blkdev_issue_discard() failed: %d\n", rc); -		return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; -	} +	ret = iblock_do_unmap(cmd, bdev, lba, nolb); +	if (ret) +		return ret;  	target_complete_cmd(cmd, GOOD);  	return 0; diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index 60d4b5185f3..bbc5b0ee2bd 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -596,3 +596,88 @@ u32 sbc_get_device_type(struct se_device *dev)  	return TYPE_DISK;  }  EXPORT_SYMBOL(sbc_get_device_type); + +sense_reason_t +sbc_execute_unmap(struct se_cmd *cmd, +	sense_reason_t (*do_unmap_fn)(struct se_cmd *, void *, +				      sector_t, sector_t), +	void *priv) +{ +	struct se_device *dev = cmd->se_dev; +	unsigned char *buf, *ptr = NULL; +	sector_t lba; +	int size; +	u32 range; +	sense_reason_t ret = 0; +	int dl, bd_dl; + +	/* We never set ANC_SUP */ +	if (cmd->t_task_cdb[1]) +		return TCM_INVALID_CDB_FIELD; + +	if (cmd->data_length == 0) { +		target_complete_cmd(cmd, SAM_STAT_GOOD); +		return 0; +	} + +	if (cmd->data_length < 8) { +		pr_warn("UNMAP parameter list length %u too small\n", +			cmd->data_length); +		return TCM_PARAMETER_LIST_LENGTH_ERROR; +	} + +	buf = transport_kmap_data_sg(cmd); +	if (!buf) +		return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + +	dl = get_unaligned_be16(&buf[0]); +	bd_dl = get_unaligned_be16(&buf[2]); + +	size = cmd->data_length - 8; +	if (bd_dl > size) +		pr_warn("UNMAP parameter list length %u too small, ignoring bd_dl %u\n", +			cmd->data_length, bd_dl); +	else +		size = bd_dl; + +	if (size / 16 > dev->dev_attrib.max_unmap_block_desc_count) { +		ret = TCM_INVALID_PARAMETER_LIST; +		goto err; +	} + +	/* First UNMAP block descriptor starts at 8 byte offset */ +	ptr = &buf[8]; +	pr_debug("UNMAP: Sub: %s Using dl: %u bd_dl: %u size: %u" +		" ptr: %p\n", dev->transport->name, dl, bd_dl, size, ptr); + +	while (size >= 16) { +		lba = get_unaligned_be64(&ptr[0]); +		range = get_unaligned_be32(&ptr[8]); +		pr_debug("UNMAP: Using lba: %llu and range: %u\n", +				 (unsigned long long)lba, range); + +		if (range > dev->dev_attrib.max_unmap_lba_count) { +			ret = TCM_INVALID_PARAMETER_LIST; +			goto err; +		} + +		if (lba + range > dev->transport->get_blocks(dev) + 1) { +			ret = TCM_ADDRESS_OUT_OF_RANGE; +			goto err; +		} + +		ret = do_unmap_fn(cmd, priv, lba, range); +		if (ret) +			goto err; + +		ptr += 16; +		size -= 16; +	} + +err: +	transport_kunmap_data_sg(cmd); +	if (!ret) +		target_complete_cmd(cmd, GOOD); +	return ret; +} +EXPORT_SYMBOL(sbc_execute_unmap); diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 3243ea790ea..f8388b4024a 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -65,7 +65,6 @@ static void transport_complete_task_attr(struct se_cmd *cmd);  static void transport_handle_queue_full(struct se_cmd *cmd,  		struct se_device *dev);  static int transport_generic_get_mem(struct se_cmd *cmd); -static int target_get_sess_cmd(struct se_session *, struct se_cmd *, bool);  static void transport_put_cmd(struct se_cmd *cmd);  static void target_complete_ok_work(struct work_struct *work); @@ -2179,7 +2178,7 @@ EXPORT_SYMBOL(transport_generic_free_cmd);   * @se_cmd:	command descriptor to add   * @ack_kref:	Signal that fabric will perform an ack target_put_sess_cmd()   */ -static int target_get_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd, +int target_get_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd,  			       bool ack_kref)  {  	unsigned long flags; @@ -2208,6 +2207,7 @@ out:  	spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);  	return ret;  } +EXPORT_SYMBOL(target_get_sess_cmd);  static void target_release_cmd_kref(struct kref *kref)  { @@ -2765,8 +2765,13 @@ transport_send_check_condition_and_sense(struct se_cmd *cmd,  		/* CURRENT ERROR */  		buffer[0] = 0x70;  		buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10; -		/* ILLEGAL REQUEST */ -		buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; +		/* +		 * Returning ILLEGAL REQUEST would cause immediate IO errors on +		 * Solaris initiators.  Returning NOT READY instead means the +		 * operations will be retried a finite number of times and we +		 * can survive intermittent errors. +		 */ +		buffer[SPC_SENSE_KEY_OFFSET] = NOT_READY;  		/* LOGICAL UNIT COMMUNICATION FAILURE */  		buffer[SPC_ASC_KEY_OFFSET] = 0x08;  		break; diff --git a/drivers/target/tcm_fc/tfc_io.c b/drivers/target/tcm_fc/tfc_io.c index b6fd4cf4284..e415af32115 100644 --- a/drivers/target/tcm_fc/tfc_io.c +++ b/drivers/target/tcm_fc/tfc_io.c @@ -103,6 +103,13 @@ int ft_queue_data_in(struct se_cmd *se_cmd)  	use_sg = !(remaining % 4);  	while (remaining) { +		struct fc_seq *seq = cmd->seq; + +		if (!seq) { +			pr_debug("%s: Command aborted, xid 0x%x\n", +				 __func__, ep->xid); +			break; +		}  		if (!mem_len) {  			sg = sg_next(sg);  			mem_len = min((size_t)sg->length, remaining); @@ -169,7 +176,7 @@ int ft_queue_data_in(struct se_cmd *se_cmd)  			f_ctl |= FC_FC_END_SEQ;  		fc_fill_fc_hdr(fp, FC_RCTL_DD_SOL_DATA, ep->did, ep->sid,  			       FC_TYPE_FCP, f_ctl, fh_off); -		error = lport->tt.seq_send(lport, cmd->seq, fp); +		error = lport->tt.seq_send(lport, seq, fp);  		if (error) {  			/* XXX For now, initiator will retry */  			pr_err_ratelimited("%s: Failed to send frame %p, " diff --git a/drivers/target/tcm_fc/tfc_sess.c b/drivers/target/tcm_fc/tfc_sess.c index 113f33598b9..4859505ae2e 100644 --- a/drivers/target/tcm_fc/tfc_sess.c +++ b/drivers/target/tcm_fc/tfc_sess.c @@ -428,19 +428,12 @@ static int ft_prli(struct fc_rport_priv *rdata, u32 spp_len,  	return ret;  } -static void ft_sess_rcu_free(struct rcu_head *rcu) -{ -	struct ft_sess *sess = container_of(rcu, struct ft_sess, rcu); - -	kfree(sess); -} -  static void ft_sess_free(struct kref *kref)  {  	struct ft_sess *sess = container_of(kref, struct ft_sess, kref);  	transport_deregister_session(sess->se_sess); -	call_rcu(&sess->rcu, ft_sess_rcu_free); +	kfree_rcu(sess, rcu);  }  void ft_sess_put(struct ft_sess *sess)  |