📄 iscsi_target.c
字号:
if (conn->conn_socket != NULL) { /* Release socket */ TRACE(TRACE_ISCSI, "Release socket %p on conn %p, cid %d, session %p\n", conn->conn_socket, conn, conn->conn_id, conn->session); report_peer_up_down(conn->ip_address, "disconnected from"); sock_release(conn->conn_socket); conn->conn_socket = NULL; }}/* * iscsi_release_connection: releases all the stuff related to a conn. * Right now, it does not do anything for releasing commands specific * to a connection since commands are more specific to a session as * against a connection. It also dequeues the connection from the list */static intiscsi_release_connection(struct iscsi_conn *conn){ if (!conn) { TRACE_ERROR("%s Cannot release NULL connection/n", current->comm); return -1; } /* dequeue any unsent reject messages (do not send them) */ dequeue_reject(conn, 0); /* Kill the rx and tx thread */ if (conn->rx_thread) { /* Mike Christie mikenc@us.ibm.com */ TRACE(TRACE_ISCSI, "Releasing iscsi_rx_%d\n", conn->conn_id); send_sig(ISCSI_SHUTDOWN_SIGNAL, conn->rx_thread, 1); down_interruptible(&conn->kill_rx_sem); } if (conn->tx_thread) { TRACE(TRACE_ISCSI, "Releasing iscsi_tx_%d\n", conn->conn_id); /* Mike Christie mikenc@us.ibm.com */ send_sig(ISCSI_SHUTDOWN_SIGNAL, conn->tx_thread, 1); down_interruptible(&conn->kill_tx_sem); } /* Release socket */ iscsi_release_socket(conn); TRACE(TRACE_ISCSI_FULL, "Dequeue connection conn->cid %u\n", conn->conn_id); /* Dequeue connection */ list_del(&conn->conn_link); conn->session->nconn--; /* Free connection */ my_kfree((void **)&conn->local_ip_address, "local ip address"); my_kfree((void **)&conn->ip_address, "ip address"); del_timer_sync(&conn->nop_timer); my_kfree((void**)&conn, "conn"); return 0;}/* * executed only by the rx thread. * Allocates new reject item, fills it in with header of rejected pdu, * and enqueues it for tx thread on reject_list of this connection. */intenqueue_reject(struct iscsi_conn *conn, __u8 reason){ int err = 0; struct reject_item *new_item; TRACE(TRACE_ENTER_LEAVE, "Enter enqueue_reject, reason %u\n", reason); if ((new_item = (struct reject_item *)my_kmalloc(sizeof(struct reject_item), "reject item")) == NULL) { err = -1; goto out; } memcpy(new_item->bad_header, conn->bad_hdr, ISCSI_HDR_LEN); new_item->reason = reason; if (down_interruptible(&conn->reject_sem)) { my_kfree((void **)&new_item, "reject item"); err = -1; goto out; } list_add_tail(&new_item->reject_link, &conn->reject_list); up(&conn->reject_sem); if (atomic_read(&conn->tx_sem.count) <= 0) { up(&conn->tx_sem); }out: TRACE(TRACE_ENTER_LEAVE, "Leave enqueue_reject, err %d\n", err); return err;}/* * executed only by the tx thread. * locks connection's queue of pending rejects, then takes each item off * queue in order and uses it to generate and send a Reject pdu to initiator. */static intdequeue_reject(struct iscsi_conn *conn, int sendit){ int err = 0; struct reject_item *item; struct list_head *list_ptr, *list_temp; if (down_interruptible(&conn->reject_sem)) { err = -1; goto out; } list_for_each_safe(list_ptr, list_temp, &conn->reject_list) { item = list_entry(list_ptr, struct reject_item, reject_link); if (sendit) err = iscsi_tx_rjt(conn, item->bad_header, item->reason); list_del(list_ptr); my_kfree((void **)&item, "reject item"); if (err) break; } up(&conn->reject_sem);out: return err;}/* * iscsi_rx_thread: This thread is responsible for receiving iSCSI PDUs * and messages from the Initiator and then figuring out what to do with * them */intiscsi_rx_thread(void *param){ struct iscsi_conn *conn = (struct iscsi_conn *) param; struct iovec iov; /* for reading header and header digest */ struct targ_error_rec err_rec; __u8 buffer[ISCSI_HDR_LEN]; /* read 48-byte PDU header in here */ __u32 digest; /* read 4-byte header digest in here */ __u32 hdr_crc; /* compute PDU header digest in here */ __u32 opcode; /* extract 6-bit PDU opcode into here */ __u32 local_itt; /* convert 4-byte PDU ITT into here */ int err; lock_kernel();/* Ming Zhang, mingz@ele.uri.edu */#ifdef K26 daemonize(#else daemonize(); sprintf(current->comm,#endif "iscsi_rx_%d", conn->conn_id);#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 4, 18) /* * Arne Redlich, agr1@users.sourceforge.net: * This prevents the rx_thread from becoming a zombie after it exits. */ reparent_to_init();#endif siginitsetinv(¤t->blocked, ISCSI_SHUTDOWN_SIGBITS); /* mark that this rx thread is alive */ conn->rx_thread = current; unlock_kernel(); printk("%s Starting pid %d\n", current->comm, current->pid); report_peer_up_down(conn->ip_address, "connected to"); /* receive loop */ while (1) { iov.iov_len = ISCSI_HDR_LEN; iov.iov_base = buffer; /* receive iSCSI header */ err = iscsi_rx_data(conn, &iov, 1, ISCSI_HDR_LEN); if (err != ISCSI_HDR_LEN) { goto iscsi_rx_thread_out; } TRACE_BUFFER(TRACE_BUF, buffer, ISCSI_HDR_LEN, "%s Got PDU header\n", current->comm); opcode = buffer[0] & ISCSI_OPCODE; local_itt = ntohl(((struct generic_pdu *)buffer)->init_task_tag); if (!(conn->session->tsih)) { /* not in full feature phase yet, only accept Login request pdus */ if (opcode != ISCSI_INIT_LOGIN_CMND) { TRACE_ERROR("%s ITT %u has opcode 0x%02x, expected 0x%02x\n", current->comm, local_itt, opcode, ISCSI_INIT_LOGIN_CMND); /* MUST immediately terminate the connection */ goto iscsi_rx_thread_out; } TRACE(TRACE_ISCSI, "%s Got login request, ITT %u\n", current->comm, local_itt); if (handle_login(conn, buffer) < 0) { TRACE_ERROR("%s Trouble in handle_login\n", current->comm); goto iscsi_rx_thread_out; } continue; } /* keep a copy of this header in case we need it in a later reject */ memcpy(conn->bad_hdr, buffer, ISCSI_HDR_LEN); /* connection belongs to session that is in full feature phase */ if (conn->hdr_crc) { /* read and check header digest on this pdu */ TRACE(TRACE_DEBUG, "iscsi_rx_thread: Header digest check\n"); hdr_crc = 0; do_crc(buffer, ISCSI_HDR_LEN, &hdr_crc); iov.iov_len = CRC_LEN; iov.iov_base = &digest; if ((err = iscsi_rx_data(conn, &iov, 1, CRC_LEN)) != CRC_LEN) { goto iscsi_rx_thread_out; } if (hdr_crc != digest) { TRACE_ERROR("%s Expected header crc 0x%08x, got 0x%08x\n", current->comm, ntohl(hdr_crc), ntohl(digest)); /* Error Recovery Code Added by SAI */ TRACE(TRACE_ERROR_RECOVERY, "Start header digest error recovery\n"); err_rec.curr_conn = conn; err_rec.pdu_hdr = (struct generic_pdu *)buffer; err_rec.pdu_hdr->length = be32_to_cpu(err_rec.pdu_hdr->length); err_rec.err_type = HEADER_DIGERR; if ((err = targ_do_error_recovery(&err_rec)) < 0) goto iscsi_rx_thread_out; continue; } else { TRACE(TRACE_ISCSI_FULL, "Got header crc 0x%08x\n", ntohl(digest)); } } switch (opcode) { case ISCSI_INIT_LOGIN_CMND: { /* got Login request while in full feature phase */ TRACE_ERROR("%s Got login request ITT %u in full feature " "phase\n", current->comm, local_itt); goto iscsi_rx_thread_out; } case ISCSI_INIT_TEXT_CMND: { TRACE(TRACE_ISCSI, "%s Got text request, ITT %u\n", current->comm, local_itt); if (handle_text_request(conn, conn->session, buffer) < 0) { TRACE_ERROR("%s Trouble in handle_text_request, ITT %u\n", current->comm, local_itt); goto iscsi_rx_thread_out; } break; } case ISCSI_INIT_SCSI_CMND: { TRACE(TRACE_ISCSI, "%s Got SCSI command, CmdSN %u, ITT %u\n", current->comm, ntohl(((struct generic_pdu *)buffer)->cmd_sn), local_itt); if (handle_cmnd(conn, conn->session, buffer) < 0) { TRACE_ERROR("%s Trouble in handle_cmnd, ITT %u\n", current->comm, local_itt); goto iscsi_rx_thread_out; } break; } case ISCSI_INIT_SCSI_DATA_OUT: { TRACE(TRACE_ISCSI, "%s Got data-out, ITT %u, offset %u\n", current->comm, local_itt, ntohl(((struct generic_pdu *) buffer)->offset)); if (handle_data(conn, conn->session, buffer) < 0) { TRACE_ERROR("%s Trouble in handle_data, ITT %u\n", current->comm, local_itt); goto iscsi_rx_thread_out; } break; } case ISCSI_INIT_TASK_MGMT_CMND: { TRACE(TRACE_ISCSI, "%s Got task mgt command, ITT %u\n", current->comm, local_itt); if (handle_task_mgt_command(conn, conn->session, buffer) < 0) { TRACE_ERROR("%s Trouble in handle_task_mgt_cmnd, ITT %u\n", current->comm, local_itt); goto iscsi_rx_thread_out; } break; } case ISCSI_INIT_LOGOUT_CMND: { TRACE(TRACE_ISCSI, "%s Got logout request, ITT %u\n", current->comm, local_itt); if (handle_logout(conn, conn->session, buffer) < 0) { TRACE_ERROR("%s Trouble in handle_logout, ITT %u\n", current->comm, local_itt); goto iscsi_rx_thread_out; } break; } case ISCSI_INIT_NOP_OUT: { TRACE(TRACE_ISCSI, "%s Got NOP_OUT, ITT %u\n", current->comm, local_itt); if (handle_nopout(conn, conn->session, buffer) < 0) { TRACE_ERROR("%s Trouble in handle_nopout, ITT %u\n", current->comm, local_itt); goto iscsi_rx_thread_out; } break; } /* SNACK Handling by Target - SAI */ case ISCSI_INIT_SNACK: { TRACE(TRACE_ERROR_RECOVERY, "%s Got SNACK Request\n", current->comm); if (handle_snack(conn, conn->session, buffer) < 0) { TRACE_ERROR("%s Trouble in handle_snack\n", current->comm); goto iscsi_rx_thread_out; } break; } case ISCSI_TARG_NOP_IN: case ISCSI_TARG_SCSI_RSP: case ISCSI_TARG_TASK_MGMT_RSP: case ISCSI_TARG_LOGIN_RSP: case ISCSI_TARG_TEXT_RSP: case ISCSI_TARG_SCSI_DATA_IN: case ISCSI_TARG_LOGOUT_RSP: case ISCSI_TARG_R2T: case ISCSI_TARG_ASYNC_MSG: case ISCSI_TARG_RJT: { TRACE_ERROR("%s Initiator sent a target opcode %.2x, ITT %u\n", current->comm, opcode, local_itt); /***** TRACE_SET(TRACE_DEBUG | TRACE_ISCSI | TRACE_ERROR_RECOVERY | TRACE_ISCSI_FULL | TRACE_ENTER_LEAVE); *****/ /* Send a Reject PDU and escalate to session recovery */ enqueue_reject(conn, REASON_PROTOCOL_ERR); targ_session_recovery(conn); goto iscsi_rx_thread_out; } default: { TRACE_ERROR("%s Got unknown opcode %.2x, ITT %u\n", current->comm, opcode, local_itt); /***** TRACE_SET(TRACE_DEBUG | TRACE_ISCSI | TRACE_ERROR_RECOVERY | TRACE_ISCSI_FULL | TRACE_ENTER_LEAVE); *****/ /* Send a Reject PDU and escalate to session recovery */ enqueue_reject(conn, REASON_COMMAND_NOT_SUPPORTED); targ_session_recovery(conn); goto iscsi_rx_thread_out; } } /* switch */ /* in case there are any out-of-order commands that are now in-order */ check_queued_cmnd(conn->session); } /* while(1) */iscsi_rx_thread_out: conn->active = 0; conn->rx_thread = NULL; if (conn->tx_thread) { /* tx thread still going, shut it down */ send_sig(ISCSI_SHUTDOWN_SIGNAL, conn->tx_thread, 1); } else { /* tx thread has shut down, if socket is still open, release it */ iscsi_release_socket(conn); } up(&conn->kill_rx_sem); printk("%s Exiting pid %d\n", current->comm, current->pid); return 0;}static voiddeal_with_nop_timer(unsigned long data);static voidrestart_nop_timer(struct iscsi_conn *conn){ conn->nop_timer.expires = jiffies + conn->nop_period; conn->nop_timer.data = (unsigned long) conn; conn->nop_timer.function = deal_with_nop_timer; add_timer(&conn->nop_timer);}/* called by kernel when nop periodic timer expires on a connection * Always turn on the silence bit, * and also turn on the need nopin bit if silence bit was on before the set.*/static voiddeal_with_nop_timer(unsigned long data){ struct iscsi_conn *conn; conn = (struct iscsi_conn *)data; if (test_and_set_bit(SILENCE_BIT, &conn->control)) { set_bit(NEED_NOPIN_BIT, &conn->control); } restart_nop_timer(conn); if (atomic_read(&conn->tx_sem.count) <= 0) { up(&conn->tx_sem); }}/* * iscsi_tx_thread: This thread is responsible for the transmission of * responses and/or status information to the initiator */intiscsi_tx_thread(void *param){ struct iscsi_conn *conn = (struct iscsi_conn *) param; struct iscsi_session *session; struct iscsi_cmnd *cmnd, *prev_cmnd; int count, skipover;#if defined(CONFIG_ISCSI_DEBUG) __u32 save_mask;#endif lock_kernel();/* Ming Zhang, mingz@ele.uri.edu */#ifdef K26 daemonize(#else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -