📄 initiator_utilities.c
字号:
int retval; struct msghdr *msg; mm_segment_t oldfs; struct session *session = conn->my_session; char ip_string[INET6_ADDRSTRLEN+2], port_string[8];#if defined(CONFIG_ISCSI_DEBUG) struct iovec *iov, *iov_copy = NULL; int index;#endif TRACE(TRACE_ENTER_LEAVE, "Enter recv_iovector, orig_size %d, " "iov_count %d\n", orig_size, iov_count); if (conn->connection_flags & CONN_HIT_EOF) { /* previously hit EOF on this connection, don't read again */ retval = -ECONNRESET; goto out_lock_held; } /* set up this connection's reusable message header. * NOTE: sock_recvmsg() destroys some part of this header, * so it MUST be set up EACH TIME through here. */ msg = &conn->rx_msg; memset(msg, 0, sizeof(struct msghdr)); /* most fields are 0/NULL */ msg->msg_iovlen = iov_count; msg->msg_iov = conn->rx_iov; if (unlikely(max_iov_count < iov_count)) { /* keep track of max no. of iov slots ever used */ max_iov_count = iov_count; }#if defined(CONFIG_ISCSI_DEBUG) if (unlikely(TRACE_TEST(TRACE_BUF))) { TRACE(TRACE_BUF, "sock_recvmsg waiting for %d bytes\n", orig_size); iov = conn->rx_iov; for (index = 0; index < iov_count; index++ ) { TRACE(TRACE_DEBUG, "index %x iov base %p iov size %d \n", index, iov->iov_base, iov->iov_len); iov++; } index *= sizeof(struct iovec); iov_copy = my_kmalloc(index, "iov_copy"); if (likely(iov_copy != NULL)) { memcpy(iov_copy, conn->rx_iov, index); } }#endif /* allow other threads to run in case we block in the receive */ UNH_UNLOCK(&session->sess_lock, conn->rx_lock_flags); oldfs = get_fs(); set_fs(get_ds()); retval = sock_recvmsg(conn->sock, msg, orig_size, MSG_WAITALL); set_fs(oldfs); UNH_LOCK(&session->sess_lock, conn->rx_lock_flags); TRACE(TRACE_DEBUG, "sock_recvmsg got %d bytes\n", retval); if (unlikely(retval != orig_size)) { /* sock_recvmsg hit an error or end of file */ if (retval == 0 || retval == -ECONNRESET) { /* got an EOF indication, which means remote peer is gone */ if (cnv_inet_to_string(conn->ip_address, ip_string, port_string) > 0) { TRACE_ERROR("%s end-of-file from %s:%s\n", current->comm, ip_string, port_string); } conn->connection_flags |= CONN_HIT_EOF; retval = -ECONNRESET; goto out_lock_held; } if (retval != -ERESTARTSYS) { TRACE_ERROR("sock_recvmsg expected %d bytes, got %d\n", orig_size, retval); } if (retval > 0) { /* to tell caller "lock not held" */ retval = -ECONNRESET; } goto out_lock_held; }#if defined(CONFIG_ISCSI_DEBUG) if (unlikely(TRACE_TEST(TRACE_BUF))) { if ((iov = iov_copy)) { for (index = 0; index < iov_count; index++) { dump_buffer(iov->iov_base, iov->iov_len); iov++; } } }#endifout:#if defined(CONFIG_ISCSI_DEBUG) my_kfree((void **)&iov_copy, "iov_copy");#endif TRACE(TRACE_ENTER_LEAVE, "Leave recv_iovector, retval %d\n", retval); return retval;out_lock_held: UNH_UNLOCK(&session->sess_lock, conn->rx_lock_flags); goto out;}/* * executed only by rx thread. * the session->sess_lock MUST be held by the calling process/thread. * Called from init_recovery_thread(), iscsi_initiator_rx_thread(). * Reads an iscsi pdu header into conn->rx_buf from conn->sock. * The header length is 48 without header digests, 52 bytes with. * Uses rx_msg and rx_iov structures in "conn" to control the read. * Returns > 0 (header length 48 or 52) on success with session lock held, * < 0 on failure with session lock not held. */intrecv_pdu_header(struct connection *conn){ int size; struct iovec *iov; size = conn->basic_hdr_len; /* set up this connection's reusable i/o vector to point at base. * NOTE: sock_recvmsg() destroys some part of this header, * so it MUST be set up EACH TIME through here. */ iov = conn->rx_iov; iov->iov_len = size; iov->iov_base = conn->rx_buf; return recv_iovector(conn, size, 1);}/**************************************************************************** * executed only by rx thread. * the session->sess_lock MUST be held by the calling process/thread. * Called only by iscsi_initiator_rx_thread. * This function is responsible for making the checks for exp_cmd_sn and * max_cmd_sn values in the received iSCSI header from the target. * Parameters: exp_cmd_sn from the PDU (in host byte order) * max_cmd_sn from the PDU (in host byte order) * cur_session, pointing to the struct session * Return value: 0, if the exp_cmd_sn and max_cmd_sn are valid * -1, if either exp_cmd_sn or max_cmd_sn is invalid. ****************************************************************************/int __attribute__ ((no_instrument_function))check_and_update_cmd_sns(__u32 exp_cmd_sn, __u32 max_cmd_sn, struct session *cur_session){ int retval = 0, delta; struct connection *conn; TRACE(TRACE_ENTER_LEAVE, "Enter check_and_update_cmd_sns\n"); /* RFC 3720 Section 3.2.2.1 Command Numbering and Acknowledging * * "Comparisons and arithmetic on ExpCmdSN and MaxCmdSN MUST use * Serial Number Arithmetic as defined in [RFC1982] where * SERIAL_BITS = 32. * * The target MUST NOT transmit a MaxCmdSN that is less than ExpCmdSN-1. * ... * * MaxCmdSN and ExpCmdSN fields are processed by the initiator as * follows: * * - If the PDU MaxCmdSN is less than the PDU ExpCmdSN-1 (in Serial * Arithmetic Sense), they are both ignored. * * - If the PDU MaxCmdSN is greater than the local MaxCmdSN (in Serial * Arithmetic Sense), it updates the local MaxCmdSN; otherwise, * it is ignored. * * - If the PDU ExpCmdSN is greater than the local ExpCmdSN (in Serial * Arithmetic Sense), it updates the local ExpCmdSN; otherwise, * it is ignored. * * This sequence is required because updates may arrive out of order * (e.g., the updates are sent on different TCP connections). * * iSCSI initiators and targets MUST support the command numbering * scheme." */ delta = max_cmd_sn - (exp_cmd_sn - 1); if (delta < 0) { /* MaxCmdSN < ExpCmdSN - 1, ignore both PDU values */ TRACE(TRACE_ISCSI_FULL, "Ignoring PDU's ExpCmdSN %u and MaxCmdSN %u\n", exp_cmd_sn, max_cmd_sn); } else { /* values in the PDU are ok relative to each other */ delta = max_cmd_sn - cur_session->max_cmd_sn; if (delta < 0) { /* PDU.MaxCmdSN < local.MaxCmdSN, ignore PDU's MaxCmdSN */ TRACE(TRACE_ISCSI_FULL, "Ignoring PDU's MaxCmdSN %u\n", max_cmd_sn); } else if (delta > 0) { /* MaxCmdSN just increased, update the local MaxCmdSN */ cur_session->max_cmd_sn = max_cmd_sn; /* find tx_threads waiting for this limit to increase */ for (conn = cur_session->connection_head; conn != NULL; conn = conn->next) { if (conn->connection_flags & NEED_TX_WAKEUP) { /* yes, so wake it up!! */ if (atomic_read(&conn->tx_sem.count) <= 0) { TRACE(TRACE_ISCSI_FULL, "rx_thread: wake up tx_thread\n"); up(&conn->tx_sem); } } } } delta = exp_cmd_sn - cur_session->exp_cmd_sn; if (delta < 0) { /* PDU.ExpCmdSN < local.ExpCmdSN, ignore PDU's ExpCmdSN */ TRACE(TRACE_ISCSI_FULL, "Ignoring PDU's ExpCmdSN %u\n", exp_cmd_sn); } else if (delta > 0) { /* greater than, check it against local CmdSN */ delta = cur_session->cur_cmd_sn - exp_cmd_sn; if (delta < 0) { TRACE_ERROR ("Got ExpCmdSN %u, exceeds next CmdSN %u\n", exp_cmd_sn, cur_session->cur_cmd_sn); retval = -1; } else { /* not less than, update the local ExpCmdSN */ cur_session->exp_cmd_sn = exp_cmd_sn; } } } TRACE(TRACE_ENTER_LEAVE, "Leave check_and_update_cmd_sns, retval %d\n", retval); return retval;}/* * the session->sess_lock MUST be held by the calling process/thread. * Calls SCSI Mid-level's scsi_done callback with result, * (and with session->lock held). * Then frees up storage allocated to old command after * both tx_thread and tx_thread have completed using the command. * * Note - The use of RXDONE and TXDONE flags syncronizes the freeing * to the command structure in an SMP environment under the * protection of session->lock. */voidfree_pending_command(struct command *old_command, struct connection *current_connection){ struct scsi_cmnd *SCpnt;#if defined(CONFIG_ISCSI_DEBUG) __u32 local_itt = old_command->init_task_tag;#endif struct r2t_cookie *cookie; struct list_head *lptr, *next; TRACE(TRACE_ENTER_LEAVE, "Enter free_pending_command %p, ITT %u\n", old_command, local_itt); if ((old_command->cmd_state & (CMD_STATE_RXDONE | CMD_STATE_TXDONE)) != (CMD_STATE_RXDONE | CMD_STATE_TXDONE)) { /* both rx and tx threads have not yet signed * off on this command */ goto out; } /* remove this command from this connection's pending command list */ list_del(&old_command->link); /* release storage occupied by any range list used by this command */ free_range_list(&old_command->r2t_range_list); /* free up any r2t cookies still pending for this command */ list_for_each_safe(lptr, next, &old_command->r2t_cookies) { list_del(lptr); cookie = list_entry(lptr, struct r2t_cookie, link); uncreate_r2t_cookie(cookie, current_connection->my_session); } /* free up any buffer associated with this command */ my_kfree(&old_command->buffer_to_free, "NopOut/Text data"); /* get pointer to SCSI midlevel command */ SCpnt = old_command->SCpnt; /* put old command structure on end of session's free list */ endup_command(old_command, current_connection->my_session);#ifdef K26 /* free up kmappings we did * if we had scatter-gather list filled */ if (SCpnt != NULL && SCpnt->use_sg != 0) { /* We filled a scatter-gather list with kmap'ings */ int i = SCpnt->use_sg; struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer; while (i --) { TRACE(TRACE_DEBUG, "Unmapping %d page %p for sg_dma_address(sg) %x\n", i, sg->page,sg_dma_address(sg)); kunmap(sg->page); sg ++; } }#endif /* K26 */ if (SCpnt != NULL && SCpnt->scsi_done != NULL) { /* post to SCSI midlevel that this command completed */ call_back_scsi_done(SCpnt); TRACE(TRACE_DEBUG, "Posted completion, ITT %u\n", local_itt); }out: TRACE(TRACE_ENTER_LEAVE, "Leave free_pending_command\n");}voidalloc_inquiry_stuff(struct session *sess, __u32 lun, int size){ if ((sess->inquiry_buf[lun] = my_kmalloc(size, "inquiry_buf"))) { sess->inquiry_size[lun] = size; }}voidfree_inquiry_stuff(struct session *sess, __u32 lun){ my_kfree((void **)&sess->inquiry_buf[lun], "inquiry_buf"); sess->inquiry_size[lun] = 0;}/* we just successfully completed an inquiry command */voidsave_inquiry(struct session *sess, struct command *related_command, int missing){ int slength; __u32 lun; slength = related_command->SCpnt->request_bufflen; TRACE(TRACE_DEBUG, "completed inquiry, slength %d, missing %d\n", slength, missing); if (missing > 0) slength -= missing; if (slength > 0) {#ifdef K26 lun = related_command->SCpnt->device->lun;#else lun = related_command->SCpnt->lun;#endif if (sess->inquiry_size[lun] < slength) { free_inquiry_stuff(sess, lun); } if (!sess->inquiry_buf[lun]) { alloc_inquiry_stuff(sess, lun, slength); } if (sess->inquiry_buf[lun]) { memcpy(sess->inquiry_buf[lun], related_command->SCpnt->request_buffer, slength); if (unlikely(TRACE_TEST(TRACE_BUF))) { TRACE(TRACE_BUF, "inquiry_buf for lun %u, %d " "bytes\n", lun, slength); dump_buffer(sess->inquiry_buf[lun], slength); } } }}/* we just successfully completed a read capacity command */voidsave_capacity(struct session *sess, struct command *related_command, int missing){ int slength;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -