diff options
Diffstat (limited to 'drivers/target/iscsi')
19 files changed, 1405 insertions, 860 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); - - if (atomic_read(&conn->check_immediate_queue)) - return 1; + ret = t->iscsit_immediate_queue(conn, cmd, state); + if (ret < 0) + return ret; + } - 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; - } + return 0; +} - 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; - } +static int +iscsit_response_queue(struct iscsi_conn *conn, struct iscsi_cmd *cmd, int state) +{ + int ret; + +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 *); |