📄 iscsi_target.c
字号:
daemonize(); snprintf(current->comm, sizeof(current->comm),#endif "iscsi_tx_%d", conn->conn_id);#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 4, 18) /* * Arne Redlich, agr1@users.sourceforge.net: * This prevents the tx_thread from becoming a zombie after it exits. */ reparent_to_init();#endif siginitsetinv(¤t->blocked, ISCSI_SHUTDOWN_SIGBITS); /* mark that this tx thread is alive */ conn->tx_thread = current; unlock_kernel(); printk("%s Starting pid %d\n", current->comm, current->pid); if (conn->nop_period) { /* set up to start the nopin timer first time through the main loop */ set_bit(NEED_NOPIN_BIT, &conn->control); set_bit(SILENCE_BIT, &conn->control); } TRACE_GET(save_mask); while (1) { /* block until somebody adds/changes a command for this conn */ if (down_interruptible(&conn->tx_sem)) goto iscsi_tx_thread_out; /* can't set session sooner because it is not set up in conn until * AFTER login is complete on this connection. But nothing will wake * up this tx thread until AFTER that login is complete, so we are safe. */ session = conn->session; TRACE(TRACE_DEBUG, "%s awake, session %p, conn %p\n", current->comm, session, conn); skipover = 0; /* don't skip anything yet */ TRACE_GET(save_mask); if (test_and_clear_bit(NEED_NOPIN_BIT, &conn->control)) { /* bit was set, either first time through * or timer expired after an interval of silence from initiator */ if (!timer_pending(&conn->nop_timer)) { /* first time thru here -- timer not going yet, start it up */ restart_nop_timer(conn); } else { /* not first time thru here, timer must have expired */ if ((count = atomic_read(&conn->outstanding_nopins))) { TRACE_ERROR("%s No reply to %u outstanding NopIns\n", current->comm, count); if (count >= MAX_OUTSTANDING_NOPINS) { TRACE_ERROR("%s Max outstanding NopIns exceeded\n", current->comm); goto iscsi_tx_thread_out; } TRACE_SET(save_mask | TRACE_DEBUG | TRACE_ISCSI | TRACE_ISCSI_FULL | TRACE_ENTER_LEAVE); } if (generate_nopin(conn, session) < 0) goto iscsi_tx_thread_out; atomic_inc(&conn->outstanding_nopins); } }restart_after_dequeue: /* back here after dequeueing a command from the list, * at which time skipover will be set so we can skip * ahead to our place in the list (which may have been * changed by other tx threads while we were busy). */ if (!list_empty(&conn->reject_list)) { /* process connection's queue of pending rejects */ if (dequeue_reject(conn, 1)) goto iscsi_tx_thread_out; } /* lock the session-wide list of commands */ if (down_interruptible(&session->cmnd_sem)) goto iscsi_tx_thread_out; /* look at and count each command belonging to this session */ count = 0; prev_cmnd = NULL; cmnd = session->cmnd_list; while (cmnd != NULL) { TRACE(TRACE_DEBUG, "%s pick up cmnd %p\n", current->comm, cmnd); if (cmnd->conn == conn && ++count >= skipover) { /* this command is for this connection, handle it */ if (!(conn->connection_flags & CONN_LOGGED_OUT)) { TRACE(TRACE_DEBUG, "%s handle cmnd no. %d, ITT %u, " "opcode 0x%02x, state %d\n", current->comm, count, cmnd->init_task_tag, cmnd->opcode_byte, cmnd->state); TRACE(TRACE_DEBUG, "%s ImmData %u, UnsolData %u, data_len %u, " "data_done %u, r2t_data %d\n", current->comm, cmnd->immediate_data_present, cmnd->unsolicited_data_present, cmnd->data_length, cmnd->data_done, cmnd->r2t_data); switch (cmnd->state) { case ISCSI_SEND_TEXT_RESPONSE: { up(&session->cmnd_sem); if (handle_discovery_rsp(cmnd, conn, session) < 0) { TRACE_ERROR("%s Trouble in handle_discovery_rsp\n", current->comm); goto iscsi_tx_thread_out; } if (down_interruptible(&session->cmnd_sem)) goto iscsi_tx_thread_out; break; } case ISCSI_ASK_FOR_MORE_TEXT: { up(&session->cmnd_sem); if (ask_for_more_text(cmnd, conn, session) < 0) { TRACE_ERROR("%s Trouble in ask_for_more_text\n", current->comm); goto iscsi_tx_thread_out; } if (down_interruptible(&session->cmnd_sem)) goto iscsi_tx_thread_out; break; } case ISCSI_LOGOUT: { /* take this command out of the session-wide list */ if (prev_cmnd == NULL) session->cmnd_list = cmnd->next; else prev_cmnd->next = cmnd->next; up(&session->cmnd_sem); if (handle_logout_rsp(cmnd, conn, session) < 0) { TRACE_ERROR("%s Trouble in handle_logout_rsp\n", current->comm); } /* always exit the tx thread after a logout response */ iscsi_dequeue(cmnd, conn); goto iscsi_tx_thread_out; } case ISCSI_PING: { up(&session->cmnd_sem); if (handle_nopin(cmnd, conn, session) < 0) { TRACE_ERROR("%s Trouble in handle_nopin\n", current->comm); goto iscsi_tx_thread_out; } if (down_interruptible(&session->cmnd_sem)) goto iscsi_tx_thread_out; break; } case ISCSI_DONE: { up(&session->cmnd_sem); if (handle_iscsi_done(cmnd, conn, session) < 0) { TRACE_ERROR("%s Trouble in handle_iscsi_done\n", current->comm); goto iscsi_tx_thread_out; } if (down_interruptible(&session->cmnd_sem)) goto iscsi_tx_thread_out; break; } case ISCSI_RESEND_STATUS: { up(&session->cmnd_sem); if (send_iscsi_response(cmnd, conn, session) < 0) { TRACE_ERROR("%s Trouble in send_iscsi_response\n", current->comm); goto iscsi_tx_thread_out; } if (down_interruptible(&session->cmnd_sem)) goto iscsi_tx_thread_out; break; } case ISCSI_MGT_FN_DONE: { up(&session->cmnd_sem); if (handle_iscsi_mgt_fn_done(cmnd, conn, session) < 0) { TRACE_ERROR("%s Trouble in iscsi_mgt_fn_done\n", current->comm); goto iscsi_tx_thread_out; } if (down_interruptible(&session->cmnd_sem)) goto iscsi_tx_thread_out; break; } case ISCSI_BUFFER_RDY: { up(&session->cmnd_sem); if (iscsi_tx_r2t(cmnd, conn, session) < 0) { TRACE_ERROR("%s Trouble in iscsi_tx_r2t\n", current->comm); goto iscsi_tx_thread_out; } if (down_interruptible(&session->cmnd_sem)) goto iscsi_tx_thread_out; break; } case ISCSI_DEQUEUE: { TRACE(TRACE_DEBUG, "dequeue command, ITT %u, CmndSN %u," " count %u, skipover %u\n", cmnd->init_task_tag, cmnd->cmd_sn, count, skipover); /* take this command out of the session-wide list */ if (prev_cmnd == NULL) session->cmnd_list = cmnd->next; else prev_cmnd->next = cmnd->next; up(&session->cmnd_sem); iscsi_dequeue(cmnd, conn); skipover = count; goto restart_after_dequeue; } case ISCSI_QUEUE_CMND_RDY: { if (send_unsolicited_data(cmnd, conn, session) < 0) { TRACE_ERROR("%s Trouble in send_unsolicited_data\n", current->comm); up(&session->cmnd_sem); goto iscsi_tx_thread_out; } break; } case ISCSI_QUEUE_CMND: case ISCSI_QUEUE_OTHER: case ISCSI_CMND_RECEIVED: case ISCSI_NEW_CMND: case ISCSI_SENT: case ISCSI_NOPIN_SENT: case ISCSI_ALL_R2TS_SENT: case ISCSI_IMMEDIATE_DATA_IN: case ISCSI_UNSOLICITED_DATA_IN: case ISCSI_DATA_IN: case ISCSI_BLOCKED_SENDING_TEXT: case ISCSI_AWAIT_MORE_TEXT: { /* Not much to do */ break; } default: /* NEVER HERE */ { up(&session->cmnd_sem); TRACE_ERROR("%s Unknown command state %u\n", current->comm, cmnd->state); goto iscsi_tx_thread_out; } } /* switch */ } } /* if */ prev_cmnd = cmnd; cmnd = cmnd->next; } /* while */ /* unlock the session-wide list of commands */ up(&session->cmnd_sem); TRACE(TRACE_DEBUG, "%s handled %d commands\n", current->comm, count); TRACE_SET(save_mask); /* * Moved check to see if connection is active here so that the * tx_thread can transmit one last packet, i.e., the logout response */ if (!conn->active) goto iscsi_tx_thread_out; } /* while */iscsi_tx_thread_out: TRACE_SET(save_mask); conn->active = 0; conn->tx_thread = NULL; del_timer_sync(&conn->nop_timer); if (conn->rx_thread) { /* rx thread still going, shut it down */ send_sig(ISCSI_SHUTDOWN_SIGNAL, conn->rx_thread, 1); } else { /* rx thread has shut down, if socket is still open, release it */ iscsi_release_socket(conn); } up(&conn->kill_tx_sem); printk("%s Exiting pid %d\n", current->comm, current->pid); return 0;}/* called after reading unsolicited data attached to a WRITE SCSI command pdu, * or solicited data attached to a DataOut pdu. */static void __attribute__ ((no_instrument_function))update_after_read(struct generic_pdu *hdr, struct iscsi_cmnd *cmnd){ cmnd->data_done += hdr->length; cmnd->immediate_data_present = 0; if (hdr->flags & F_BIT) { /* end of this (solicited or unsolicited) sequence, * reset the counters for the next solicited burst (if any) */ cmnd->data_sn = 0; cmnd->unsolicited_data_present = 0; }}/* * iscsi_rx_data: will receive a fixed amount of data. * INPUT: struct iscsi_conn (what connection), struct iovec, number of iovecs, * total amount of data in bytes * OUTPUT: > 0 total bytes read if everything is okay * < 0 if trouble (-ECONNRESET means end-of-file) * = 0 for PAYLOAD_DIGERR */intiscsi_rx_data(struct iscsi_conn *conn, struct iovec *iov, int niov, int data){ struct msghdr msg; int msg_iovlen; int rx_loop, total_rx; __u32 checksum, data_crc; int i; struct iovec *iov_copy, *iov_ptr; mm_segment_t oldfs; total_rx = 0; TRACE(TRACE_ENTER_LEAVE, "Enter iscsi_rx_data, niov %d, data %d\n", niov, data); if (!conn->conn_socket) { TRACE_ERROR("%s Transport endpoint is not connected\n", current->comm); total_rx = -ENOTCONN; /* Transport endpoint is not connected */ goto out; } iov_copy = (struct iovec *)my_kmalloc(niov * sizeof(struct iovec), "iovec"); if (iov_copy == NULL) { total_rx = -ENOMEM; goto out; } while (total_rx < data) { /* get a clean copy of the original io vector to work with */ memcpy(iov_copy, iov, niov * sizeof(struct iovec)); msg_iovlen = niov; iov_ptr = iov_copy; if ((rx_loop = total_rx)) { /* have done only partial read, recalculate iov and niov -- cdeng */ TRACE(TRACE_ISCSI, "iscsi_rx_data: data %d, received so far %d, recompute iov\n", data, total_rx); do { if (iov_ptr->iov_len <= rx_loop) { rx_loop -= iov_ptr->iov_len; iov_ptr++; msg_iovlen--; } else { iov_ptr->iov_base += rx_loop; iov_ptr->iov_len -= rx_loop; rx_loop = 0; } } while (rx_loop); } memset(&msg, 0, sizeof(struct msghdr)); msg.msg_iovlen = msg_iovlen; msg.msg_iov = iov_ptr; oldfs = get_fs(); set_fs(get_ds()); rx_loop = sock_recvmsg(conn->conn_socket, &msg, (data - total_rx), MSG_WAITALL); set_fs(oldfs); /* this receive from initiator broke the silence */ clear_bit(SILENCE_BIT, &conn->control); if (rx_loop <= 0) { my_kfree((void **)&iov_copy, "iovec"); total_rx = -ECONNRESET; /* Connection reset by peer */ goto out; } total_rx += rx_loop; TRACE(TRACE_DEBUG, "iscsi_rx_data: rx_loop %d total_rx %d\n", rx_loop, total_rx); } my_kfree((void**)&iov_copy, "iovec"); if (niov > 1 && conn->data_crc) { /* this really is for a data segment, and data digests are in effect */ data_crc = 0; for (i = 0; i < niov - 1; i++) do_crc(iov[i].iov_base, iov[i].iov_len, &data_crc); checksum = *(__u32 *) (iov[niov - 1].iov_base); if (checksum != data_crc) { TRACE_ERROR("%s Got data crc 0x%08x, expected 0x%08x\n", current->comm, ntohl(checksum), ntohl(data_crc)); /* Data Digest Error Recovery - SAI */ total_rx = PAYLOAD_DIGERR; goto out; } else { TRACE(TRACE_ISCSI_FULL, "Got data crc 0x%08x\n", ntohl(checksum)); } }out: TRACE(TRACE_ENTER_LEAVE, "Leave iscsi_rx_data, total_rx %d\n", total_rx); return total_rx;}/* read data segment from cmd's connection into a single buffer * that is newly allocated to hold bufsize bytes of data * returns > 0 total number of bytes read (including pad & crc) if all ok, * = 0 if recovered from payload digest error ok * < 0 error * if ok, sets result to newly allocated buffer, else leaves result unchanged */static intread_single_data_seg(__u8 *buffer, struct iscsi_cmnd *cmd, int bufsize, char **result){ struct iovec iov[3]; int size, niov = 1, padding, err; __u32 digest, pad_bytes; char *data_buf; struct targ_error_rec err_rec; size = bufsize; padding = (-size) & 3; if (padding) { iov[niov].iov_base = &pad_bytes; iov[niov].iov_len = padding; size += padding; niov++; } if (cmd->conn->data_crc) { iov[niov].iov_base = &digest; iov[niov].iov_len = CRC_LEN; size += CRC_LEN; niov++; } if ((data_buf = (char *)my_kmalloc(bufsize, "data_buf"))) { iov[0].iov_base = data_buf; iov[0].iov_len = bufsize; err = iscsi_rx_data(cmd->conn, iov, niov, size); if (err != size) { /* Payload Digest Error Recovery - SAI */ if (err == PAYLOAD_DIGERR) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -