📄 initiator_utilities.c
字号:
/* initiator/initiator_utilities.c * * vi: set autoindent tabstop=8 shiftwidth=8 : * * This file contains auxilliary functions for iscsi initiator code * that are responsible for connecting and maintaining sessions * between initiator and 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"#include "../common/lun_packing.h"/* holds bytes of zeros that are sent as pad bytes when needed */static __u32 pad_bytes = 0;/* allocates a new r2t cookie for this session * but only initializes the order_link thread in this structure */struct r2t_cookie * __attribute__ ((no_instrument_function))create_r2t_cookie(struct session *this_session){ struct r2t_cookie *cookie; if (unlikely(list_empty(&this_session->free_r2t_cookies))) { /* allocate a new r2t cookie */ cookie = (struct r2t_cookie *) my_kmalloc(sizeof(struct r2t_cookie), "r2t cookie"); if (unlikely(cookie == NULL)) goto out; } else { /* remove an old r2t cookie from the free list and reuse it */ cookie = list_entry(this_session->free_r2t_cookies.next, struct r2t_cookie, link); list_del(&cookie->link); } INIT_LIST_HEAD(&cookie->order_link);out: return cookie;}/* finishes initializing an r2t cookie and adds it to end of command's * cookie list */void __attribute__ ((no_instrument_function))hookup_r2t_cookie(struct r2t_cookie *cookie, struct session *this_session, struct connection *this_connection, struct command *this_command, __u32 local_ttt){ cookie->target_xfer_tag = local_ttt; if (local_ttt != ALL_ONES) { /* this is a "real" (not implied) r2t. * count it and check number outstanding. */ this_command->n_r2t_cookies++; if (unlikely(this_command->n_r2t_cookies > this_session->oper_param->MaxOutstandingR2T)) { /* too many outstanding R2Ts */ TRACE_ERROR("Target sent %u outstanding R2Ts, " "but MaxOutstandingR2T is %u\n", this_command->n_r2t_cookies, this_session->oper_param-> MaxOutstandingR2T); /* now ignore this, since we can handle it ok */ } } /* Draft 20, Section 10.7.5 DataSN * "For output (write) data PDUs, the DataSN is the Data-Out PDU number * within the current output sequence. The current output sequence is * either identified by the Initiator Task Tag (for unsolicited data) or * is a data sequence generated for one R2T (for data solicited through * R2T)." * * Therefore, each R2T resets data_out_sn to 0. */ cookie->data_out_sn = 0; /* Draft 20, Section 10.8 Ready to Transfer (R2T) * "The target may send several R2T PDUs. It, therefore, can have a * number of pending data transfers. The number of outstanding R2T * PDUs [per task] are limited by the value of the negotiated key * MaxOutstandingR2T. Within a connection, outstanding R2Ts MUST be * fulfilled by the initiator in the order in which they were received." * * Therefore, each R2T gets put on ordered list within its connection. */ list_add_tail(&cookie->order_link, &this_connection->r2t_list); /* Update command offset to that expected in next in-order R2T */ this_command->data_offset += cookie->r2t_xfer_length; if (unlikely(this_session->oper_param->DataSequenceInOrder == 0)) { /* DataSequenceInOrder==No */ merge_offset_length(&this_command->r2t_range_list, cookie->offset, cookie->r2t_xfer_length); } if (unlikely(this_session->oper_param->DataPDUInOrder == 0)) { /* DataPDUInOrder==No, send Data Out PDUs backwards */ cookie->offset += cookie->r2t_xfer_length; /* set limit */ } /* add this to end of cookie list for this command */ list_add_tail(&cookie->link, &this_command->r2t_cookies);}/* put a no longer used cookie structure on available list for session */void __attribute__ ((no_instrument_function))uncreate_r2t_cookie(struct r2t_cookie *cookie, struct session *this_session){ /* remove this cookie from the ordered list of cookies * remaining to process on this connection */ list_del(&cookie->order_link); list_add_tail(&cookie->link, &this_session->free_r2t_cookies);}/* removes cookie from this command's and this connection's cookie lists */void __attribute__ ((no_instrument_function))free_r2t_cookie(struct connection *this_connection, struct command *this_command, struct r2t_cookie *cookie){ if (cookie->target_xfer_tag != ALL_ONES) { /* this is a "real" (not implied) r2t. reduce number still outstanding */ /* Draft 20 Section 12.17 MaxOutstandingR2T * "An R2T is considered outstanding until the last data * PDU (with the F bit set to 1) is transferred, or a * sequence reception timeout (Section 6.1.4.1 Recovery * Within-command) is encountered for that data * sequence." */ this_command->n_r2t_cookies--; } /* remove cookie from list of r2t cookies for this command */ list_del(&cookie->link); /* then add it to front of list of available cookies */ uncreate_r2t_cookie(cookie, this_connection->my_session);}/* info - debug * keeps track of maximum number of io vector slots actually used */int max_iov_count = 0;/* * executed by init_recovery thread, tx thread, scsi midlevel process. * the session->sess_lock MUST be held by the calling process/thread. * set up the transmit i/o vector for the iscsi header */static void __attribute__ ((no_instrument_function))setup_header_iovec(struct connection *conn, struct command *this_command){ this_command->data_offset = 0; this_command->data_checksum = 0; this_command->tx_size = conn->basic_hdr_len; this_command->tx_iov->iov_base = &this_command->iscsi_cmd; this_command->tx_iov->iov_len = conn->basic_hdr_len; this_command->tx_iovlen = 1;}/* * executed only by tx thread. * the session->sess_lock MUST be held by the calling process/thread. * Called by iscsi_initiator_tx_thread() and tx_retransmit_state1(). * build a SCSI Command with write data to be sent */voidbuild_write_command(struct session *current_session, struct connection *current_connection, struct command *new_command){ struct iovec *iov; struct iscsi_init_scsi_cmnd *iscsi_cmd; struct scsi_cmnd *Cmnd; struct r2t_cookie *cookie; int len, count = 1; int negotiated_immediate_data_length; int actual_immediate_data_length = 0; int space_left; Cmnd = new_command->SCpnt; if (unlikely(Cmnd == NULL)) { /* should never happen */ TRACE_ERROR("No SCpnt for command %p\n", new_command); goto out; } /* first, set up the transmit i/o vector for the iscsi header */ setup_header_iovec(current_connection, new_command); iscsi_cmd = &new_command->iscsi_cmd; iscsi_cmd->flags = W_BIT; if (Cmnd->request_bufflen && current_session->oper_param->ImmediateData) { /* We are allowed to send immediate data. * get the smaller of target's MaxRecvDataSegmentLength or * FirstBurstLength */ negotiated_immediate_data_length = current_connection->max_send_length; if (negotiated_immediate_data_length > current_session->oper_param->FirstBurstLength) { negotiated_immediate_data_length = current_session->oper_param->FirstBurstLength; } /* Actual datalength to be sent as immediate data is minimum of * what command wants to send and the negotiated mimimum allowed * for immediate data */ actual_immediate_data_length = Cmnd->request_bufflen; if (actual_immediate_data_length > negotiated_immediate_data_length) actual_immediate_data_length = negotiated_immediate_data_length; TRACE(TRACE_DEBUG, "Sending immediate data %d\n", actual_immediate_data_length); /* get pointer to tx i/o vector (which is already set up * for header) */ iov = new_command->tx_iov; if (Cmnd->use_sg > 0) { /* we have a scatter gather list to be sent */ int immediate_data_to_be_filled = actual_immediate_data_length; struct scatterlist *sg = (struct scatterlist *) Cmnd->request_buffer;#ifdef K26 if (use_sendpage(current_connection, new_command)) { new_command->pindex = 0; new_command->padding = 0; }#endif while (immediate_data_to_be_filled > 0) { /* set up iovector to send immediate_data */ count++; iov++;#ifdef K26 /* Convert page struct passed in SG * to kernel vaddr. We can sleep on this * call so have to release our lock. */ UNH_UNLOCK(¤t_session->sess_lock, current_connection->tx_lock_flags); sg_dma_address(sg) = (dma_addr_t)kmap(sg->page); UNH_LOCK(¤t_session->sess_lock, current_connection->tx_lock_flags); sg_dma_address(sg) += sg->offset; TRACE(TRACE_DEBUG, "sg %p sg_address %x sg_length %d " "immediate_data_filled %d\n", sg, sg_dma_address(sg), sg_dma_len(sg), immediate_data_to_be_filled); iov->iov_base = (unsigned long *)sg_dma_address(sg); iov->iov_len = sg_dma_len(sg); immediate_data_to_be_filled -= sg_dma_len(sg); if (use_sendpage(current_connection, new_command)) { new_command->sg[new_command->pindex] = *sg; new_command->pindex++; }#else TRACE(TRACE_DEBUG, "sg_address %p sg_length %d " "immediate_data_filled %d\n", sg->address, sg->length, immediate_data_to_be_filled); iov->iov_base = sg->address; iov->iov_len = sg->length; immediate_data_to_be_filled -= sg->length;#endif if (immediate_data_to_be_filled < 0) { /* went too far, last element * needs to be shortened */ iov->iov_len += immediate_data_to_be_filled; } /* compute the partial data digest if we * are using them */ if (current_connection->connection_flags & USE_DATADIGEST) { do_crc(iov->iov_base, iov->iov_len, &new_command->data_checksum); } sg++; } } else { /* we have single buffer passed from SCSI Mid-level */ count++; iov++; iov->iov_len = actual_immediate_data_length; iov->iov_base = Cmnd->request_buffer; /* compute the data digest if we are using them */ if (current_connection->connection_flags & USE_DATADIGEST) { do_crc(iov->iov_base, iov->iov_len, &new_command->data_checksum); } } iscsi_cmd->length = htonl(actual_immediate_data_length); new_command->tx_data_length = actual_immediate_data_length; new_command->tx_size += actual_immediate_data_length; new_command->data_offset = actual_immediate_data_length; if (unlikely(current_session->oper_param->DataSequenceInOrder == 0)) { /* DataSequenceInOrder==No */ merge_offset_length(&new_command->r2t_range_list, 0, actual_immediate_data_length); } /* now deal with any padding that is needed * * 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." */ len = (-actual_immediate_data_length) & 3; if (unlikely(len != 0)) { /* send len pad bytes in another element in iovector */ count++; iov++; iov->iov_base = &pad_bytes; iov->iov_len = len; new_command->tx_size += len; /* now a multiple of 4*/ TRACE(TRACE_ISCSI_FULL, "Attach %d pad bytes\n", len); /* compute partial data digest if we are using them */ if (current_connection->connection_flags & USE_DATADIGEST) { do_crc(iov->iov_base, iov->iov_len, &new_command->data_checksum); }#ifdef K26 if (use_sendpage(current_connection, new_command)) new_command->padding = len;#endif } /* if data digests are in use, be sure to set it up now */ if (current_connection->connection_flags & USE_DATADIGEST) { count++; iov++; iov->iov_base = &new_command->data_checksum; iov->iov_len = CRC_LEN; new_command->tx_size += CRC_LEN; TRACE(TRACE_ISCSI_FULL, "Attach DataDigest\n"); } } /* total number of data bytes left to send by this write command */ new_command->still_to_send = Cmnd->request_bufflen - actual_immediate_data_length; /* number of bytes that could go into first burst as unsolicited */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -