📄 initiator_tx.c
字号:
/* initiator/initiator_tx.c * * vi: set autoindent tabstop=8 shiftwidth=8 : * * This file contains the functions for iscsi initiator code that are * responsible for building and sending PDUS to a target. * * Copyright (C) 2001-2004 InterOperability Lab (IOL) * University of New Hampshire (UNH) * Durham, NH 03824 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * The name of IOL and/or UNH may not be used to endorse or promote * products derived from this software without specific prior * written permission.*/#include "iscsi_initiator.h"#include "initiator_utilities.h"#include "initiator_error_rec.h"/* * executed by rx thread or tx thread or command process. * the current_session->sess_lock MUST be held by the calling process/thread. * Called by drive_text_negotiate() and tx_nopout(). * add padding bytes, if necessary, to buffer which already has size bytes * update the command's iov and msghdr to point at this buffer * then compute and add data digest, if necessary * * Draft 20, Section 10.1 iSCSI PDU Length and Padding * "iSCSI PDUs are padded to the closest integer number of * four byte words. The padding bytes SHOULD be sent as 0." */static voidadd_padding(struct connection *current_connection, struct command *new_command, char *buffer, int size){ int n; struct iovec *iov; n = (-size) & 3; if (unlikely(n != 0)) { TRACE(TRACE_ISCSI_FULL, "Attach %u pad bytes\n", n); memset(buffer + size, 0, n); size += n; } /* get ptr to i/o vector which is already set up for header */ iov = new_command->tx_iov + new_command->tx_iovlen; iov->iov_base = buffer; iov->iov_len = size; new_command->tx_size += size; new_command->tx_iovlen++; /* compute the final data digest if we are using them */ if (current_connection->connection_flags & USE_DATADIGEST) { new_command->data_checksum = 0; do_crc(iov->iov_base, iov->iov_len, &new_command->data_checksum); iov++; iov->iov_base = &new_command->data_checksum; iov->iov_len = CRC_LEN; new_command->tx_size += CRC_LEN; new_command->tx_iovlen++; TRACE(TRACE_ISCSI_FULL, "Attach DataDigest\n"); }}/* * executed by rx thread or tx thread or command process. * the current_session->sess_lock MUST be held by the calling process/thread. * Called to send a NopOut to target as: * a new nop (nopin_cmd == NULL, size == 0 ) * or * a new ping (nopin_cmd == NULL, size != 0 ) * or * a response to a NopIn (nopin_cmd != NULL). * Buffer points to data to send, and size is number of bytes * of data to send. * If size > 0 then better have buffer != NULL. * Returns 1 if all ok, else 0. */inttx_nopout(struct connection *current_connection, __u32 i_bit, struct iscsi_targ_nopin *nopin_cmd, char **buffer, int size){ int need_itt, retval = 1; struct command *new_command; struct iscsi_init_nopout *nopout_cmd; TRACE(TRACE_ENTER_LEAVE, "Enter tx_nopout, size = %d\n", size); new_command = setup_command(NULL, current_connection); if (unlikely(new_command == NULL)) { /* will not be able to send the nop */ retval = 0; goto out; } /* set up the NopOut header (Draft 20, Section 10.18) * to be sent to the target */ nopout_cmd = (struct iscsi_init_nopout *)&new_command->iscsi_cmd; nopout_cmd->opcode = ISCSI_INIT_NOP_OUT | i_bit; nopout_cmd->flags = F_BIT; /* always set on NopOut */ if (size > 0) { /* we have data to send to target */ nopout_cmd->length = htonl(size); /* now add padding bytes and data digest, if necessary */ add_padding(current_connection, new_command, *buffer, size); /* mark that buffer will be freed in tx thread, * after NopOut is sent */ new_command->buffer_to_free = *buffer; *buffer = NULL; } /* Draft 20, Section 10.18.2 Target Transfer Tag * "The NOP-Out MUST only have the Target Transfer Tag set if it is * issued in response to a NOP-In with a valid Target Transfer Tag. * In this case, it copies the Target Transfer Tag from the NOP-In PDU. * Otherwise, the Target Transfer Tag MUST be set to 0xffffffff. * * When the Target Transfer Tag is set to a value other than 0xffffffff, * the LUN field MUST also be copied from the NOP-In." */ if (nopin_cmd != NULL) { /* this is a response to a NopIn from the target */ nopout_cmd->target_xfer_tag = nopin_cmd->target_xfer_tag; nopout_cmd->lun = nopin_cmd->lun; need_itt = 0; /* need to set ITT to 0xffffffff */ } else { /* this is a new nop or nop ping command to the target */ nopout_cmd->target_xfer_tag = ALL_ONES; /* Draft 20, Section 10.18.1 Initiator Task Tag * "The NOP-Out MUST have the Initiator Task Tag set to * a valid value only if a response in the form of * NOP-In is requested (i.e., the NOP-Out is used as a * ping request). Otherwise, the Initiator Task * Tag MUST be set to 0xffffffff." */ if (size == 0) { need_itt = 0; } else { need_itt = 1; /* set ITT to next in sequence */ } } /* attach the new NopOut command to end of pending commands list. * This also sets the ITT, the I bit if needed, and the CmdSN. */ attach_pending_command(new_command, current_connection, need_itt);out: TRACE(TRACE_ENTER_LEAVE, "Leave tx_nopout\n"); return retval;}static void deal_with_tx_timer(unsigned long data);/* * (re)start the tx timer -- period is always 1 second */static void __attribute__ ((no_instrument_function))restart_tx_timer(struct connection *conn){ if (likely(!timer_pending(&conn->tx_timer) && !(conn->connection_flags & TX_TIMER_OFF))) { TRACE(TRACE_TIMERS, "Start timer for %u ticks for conn %u\n", HZ, conn->connection_id); conn->tx_timer.expires = jiffies + HZ; conn->tx_timer.data = (__u32) conn; conn->tx_timer.function = deal_with_tx_timer; add_timer(&conn->tx_timer); } else { /* no timer or timer already going or timer should be * stopped, don't (re)start it now */ TRACE(TRACE_TIMERS, "Timer not restarted for conn %u\n", conn->connection_id); }}/* * Called by kernel when periodic session timer expires */static voiddeal_with_tx_timer(unsigned long data){ struct connection *conn; conn = (struct connection *) data; /* get the tx thread started, if necessary */ /* if( sem_getcount(&conn->tx_sem) <= 0 ) */ if (atomic_read(&conn->tx_sem.count) <= 0) { TRACE(TRACE_TIMERS, "tx_timer: wake up tx thread\n"); up(&conn->tx_sem); } /* mark that the timer went off and keep it going */ atomic_inc(&conn->tx_timer_went_off); restart_tx_timer(conn);}/* * executed by tx thread or command process. * the current_session->sess_lock MUST be held by the calling process/thread. * Called by do_test(), iscsi_initiator_tx_thread(), tx_retransmit_state2(). * drives sending of a nop out to the target * returns 0 on success, -1 on error */intdrive_nopout(struct connection *conn, struct session *current_session, __u32 i_bit, __u32 want_data){ char *buffer; int size; TRACE(TRACE_ENTER_LEAVE, "Enter drive_nopout\n"); if (!want_data) { /* don't want any data attached to this nop out */ buffer = NULL; if (i_bit) size = 0; /* send it immediate */ else size = -1; /* send it normal (forces empty ping) */ } else if ((buffer = my_kmalloc(32, "NopOut data")) == NULL) { size = 0; } else { /* send the '\0' at the end of the string too */ size = 1 + sprintf(buffer, "NopOut_Ping_Number=%u", conn->nop_counter++); TRACE(TRACE_DEBUG, "Send \"%s\", size %d\n", buffer, size); } tx_nopout(conn, i_bit, NULL, &buffer, size); TRACE(TRACE_ENTER_LEAVE, "Leave drive_nopout\n"); return 0;}/* * executed by tx thread or command process. * the current_session->sess_lock MUST be held by the calling process/thread. * Called by do_test(), create_session(). * drives a discovery session * returns 0 on success, 1 on failure */intdrive_text_negotiate(struct connection *conn, struct session *current_session, __u32 i_bit, __u32 discovery){ struct command *new_command; struct iscsi_init_text_cmnd *text_cmd; int retval, size; char *buffer; struct parameter_type *param; TRACE(TRACE_ENTER_LEAVE, "Enter drive_text_negotiate\n"); new_command = setup_command(NULL, conn); if (unlikely(new_command == NULL)) { /* will not be able to send the text request */ retval = 1; goto out; } /* set up the Text Request header (Draft 20, Section 10.10) * to be sent to the target */ text_cmd = (struct iscsi_init_text_cmnd *)&new_command->iscsi_cmd; text_cmd->opcode = ISCSI_INIT_TEXT_CMND | i_bit; text_cmd->flags = F_BIT; /* we have only 1 command */ text_cmd->target_xfer_tag = ALL_ONES; /* reset the target */ /* LUN is 0 by default */ if (discovery && (param = find_flag_parameter(SENDTARGETS_FLAG, *current_session-> session_params))) { /* send an '=' and the '\0' at the end of the string too */ size = 2 + strlen(param->parameter_name); if (param->str_value != NULL) size += strlen(param->str_value); /* must allocate an extra 3 bytes in case of padding */ buffer = my_kmalloc(size + 3, "Text data"); if (unlikely(buffer == NULL)) { /* no space for the text request, * free up the command structure */ endup_command(new_command, current_session); retval = 1; goto out; } /* save ptr to this buffer so we can free it in * rx_text_rsp when done */ new_command->buffer_to_free = buffer; strcpy(buffer, param->parameter_name); strcat(buffer, "="); if (param->str_value != NULL) strcat(buffer, param->str_value); text_cmd->length = htonl(size); /* now add padding bytes and data digest, if necessary */ add_padding(conn, new_command, buffer, size); } /* attach the new Text Request command to end of pending commands list. * this also sets the ITT and the CmdSN */ attach_pending_command(new_command, conn, 1); /* return success */ retval = 0;out: TRACE(TRACE_ENTER_LEAVE, "Leave drive_text_negotiate, retval = %d\n", retval); return retval;}/* * executed by tx thread or rx thread or init_recovery thread or command process * the current_session->sess_lock MUST be held by the calling process/thread. * Called by do_test(), iscsi_initiator_tx_thread(), init_recovery_thread(), * rx_async_msg(), rx_text_rsp() * drives the logout of a session * returns 1 on success, 0 on failure * Modified to include connection logout - SAI * for legal "reason" values, see Draft 20, Section 10.14.1 Reason Code */intdrive_logout(struct session *current_session, struct connection *conn, int reason){ struct command *new_command; struct iscsi_init_logout_cmnd *logout_cmd; int retval; TRACE(TRACE_ENTER_LEAVE, "Enter drive_logout\n"); new_command = setup_command(NULL, conn); if (unlikely(new_command == NULL)) { /* will not be able to send the logout request */ retval = 0; goto out; } /* set up the Logout Request header (Draft 20, Section 10.14) * to send to the target */ logout_cmd = (struct iscsi_init_logout_cmnd *)&new_command->iscsi_cmd; logout_cmd->opcode = ISCSI_INIT_LOGOUT_CMND | I_BIT; /* use the reason code, always with F bit set to 1 - SAI */ logout_cmd->flags = F_BIT | reason; /* RFC 3720 Section 10.14.3 CID * "This is the connection ID of the connection to be closed. * This field is only valid if the reason code is not 'close the * session'." */ if (reason != LOGOUT_CLOSE_SESSION) logout_cmd->cid = cpu_to_be16(conn->connection_id); /* attach the new Logout Request command to end of pending commands list * this also sets the ITT and the CmdSN */ attach_pending_command(new_command, conn, 1); /* return success */ retval = 1;out: TRACE(TRACE_ENTER_LEAVE, "Leave drive_logout, retval = %d\n", retval); return retval;}/* * executed only by tx thread. * the current_session->sess_lock MUST be held by the calling process/thread. * Called only by check_for_retransmit(). * Enter the xmit state of a command into state 1 * by rebuilding the cmd and setting up a resend */static inttx_retransmit_state1(struct session *current_session, struct connection* current_connection, struct command *current_command){ struct r2t_cookie *cookie; /* never got anything back from target, rexmit */ if (current_command->cmd_state & CMD_STATE_REXMIT_ORIG) { /* data out iovec overwrote original command iovec */ if (!list_empty(¤t_command->r2t_cookies)) { cookie = list_entry(current_command->r2t_cookies.next, struct r2t_cookie, link); free_r2t_cookie(current_connection, current_command, cookie); } build_write_command(current_session, current_connection, current_command); } TRACE(TRACE_ERROR_RECOVERY, "Re-Transmitting PDU, " "ITT %u, size %u\n", current_command->init_task_tag, current_command->retransmit_tx_size); current_command->tx_sent_so_far = 0; /* start again */ current_command->tx_size = current_command->retransmit_tx_size; current_command->tx_wait_to_send = jiffies; current_command->time_stamp = jiffies; /* RDR - don't come back here a second time */ current_command->activity_flg = 1; return 1;}/* * executed only by tx thread. * the current_session->sess_lock MUST be held by the calling process/thread. * Called only by check_for_retransmit(). * Enter the xmit state of a command into state 2 * by marking the time and sending another nop */static inttx_retransmit_state2(struct session *current_session, struct connection *current_connection, struct command *current_command){ struct init_error_rec err_rec; current_command->activity_flg = 2; current_command->time_stamp = jiffies; if (current_session->oper_param->ErrorRecoveryLevel > 0 && current_command->SCpnt->sc_data_direction == SCSI_DATA_WRITE) { /* a WRITE, assume lost R2T * RFC 3720 Section 10.16.6 BegRun * "BegRun 0 when used in conjunction with * RunLength 0 means resend all unacknowledged * Data-In, R2T or Response PDUS." */ err_rec.curr_conn = current_connection; err_rec.related_cmd = current_command; init_send_snack(&err_rec, 0, 0, DATA_R2T_SNACK); } else { drive_nopout(current_connection, current_session, I_BIT, 0); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -