iscsi-session.c
来自「iSCSI协议在LINUX下的源码.源代码是IBM公布的.主要是结合其OSD设备」· C语言 代码 · 共 1,649 行 · 第 1/3 页
C
1,649 行
test_and_clear_bit(TX_WAKE, &session->control_bits)); for (req = 0; req < TX_WAKE; req++) { if (signal_pending(current)) return; /* * when a logout is in progress or about to be sent * we do not start new requests, but we continue to * respond to R2Ts and Nops. */ if (test_and_clear_bit(req, &session->control_bits)) { if (test_bit(SESSION_LOGOUT_REQUESTED, &session->control_bits) && req <= TX_SCSI_COMMAND) continue; tx_request_fns[req].request_fn(session); } }}/** * session_kthread_sleep - put a thread to sleep while waiting for shutdown. * @session: session. * * Description: * If for some reason we could not relogin into a session we sleep here * and and wait for someone to remove the session. Returns -EPERM to * indicate the thread should exit, or zero to indicate that the thread * can proceed with its normal action. **/static inline intsession_kthread_sleep(struct iscsi_session *session){ retest: set_current_state(TASK_INTERRUPTIBLE); if (kthread_should_stop()) { __set_current_state(TASK_RUNNING); return -EPERM; } /* * We fall into this sleep, when someone has broken us * out of the lower loops that process requests or log us in, * terminate the session (session drops will not sleep here), * but have not (yet) cleaned up the host and called kthread_stop()). */ if (test_bit(SESSION_TERMINATING, &session->control_bits)) { schedule(); if (signal_pending(current)) flush_signals(current); goto retest; } __set_current_state(TASK_RUNNING); return 0;}/* * move to session timeout handler when that is fixed?? */static voidreplacement_timed_out(unsigned long data){ struct iscsi_session *session = (struct iscsi_session *)data; iscsi_host_err(session, "replacement session time out after %d " "seconds, drop %lu, now %lu, failing all commands\n", session->replacement_timeout, session->session_drop_time, jiffies); spin_lock(&session->task_lock); if (test_bit(SESSION_ESTABLISHED, &session->control_bits) || test_and_set_bit(SESSION_REPLACEMENT_TIMEDOUT, &session->control_bits)) { spin_unlock(&session->task_lock); return; } /* * No point in letting the commands timeout, fail them. */ iscsi_flush_queues(session, ISCSI_MAX_LUNS, DID_BUS_BUSY); spin_unlock(&session->task_lock); wake_up_all(&session->login_wait_q);}/* * the writer thread TODO - we should look into * replacing this thread and wait_for_tx_requests * with a work queue */static intiscsi_tx_thread(void *data){ int rc; unsigned long tmo; struct iscsi_session *session = (struct iscsi_session *)data; current->flags |= PF_MEMALLOC; allow_signal(SIGHUP); /* * tell the rx thread that we're about to block, and that * it can safely call iscsi_sendmsg now as part of * the Login phase. */ up(&session->tx_blocked); while (!session_kthread_sleep(session)) { spin_lock(&session->portal_lock); tmo = session->replacement_timeout * HZ; if (tmo && session->session_drop_time) { del_timer_sync(&session->replacement_timer); session->replacement_timer.expires = jiffies + tmo; add_timer(&session->replacement_timer); } spin_unlock(&session->portal_lock); rc = iscsi_wait_for_session(session, 1); spin_lock(&session->portal_lock); del_timer_sync(&session->replacement_timer); spin_unlock(&session->portal_lock); if (!rc) continue; /* we're up and running with a new session */ down(&session->tx_blocked); /* make sure we start sending commands again */ set_bit(TX_PING, &session->control_bits); set_bit(TX_SCSI_COMMAND, &session->control_bits); set_bit(TX_WAKE, &session->control_bits); while (!signal_pending(current)) wait_for_tx_requests(session); flush_signals(current); up(&session->tx_blocked); } return 0;}static intrx_establish_session(struct iscsi_session *session, unsigned int login_delay){ int rc; unsigned long login_failures = 0; while (!test_bit(SESSION_ESTABLISHED, &session->control_bits)) { if (login_delay) { iscsi_host_notice(session, "Waiting %u seconds before " "next login attempt\n", login_delay); msleep_interruptible(login_delay * 1000); } /* * wait for the tx thread to block or exit, ignoring signals. * the rx thread needs to know that the tx thread is not * running before it can safely close the socket and start * a new login phase on a new socket, Also, tasks still in * use by the tx thread can't safely be completed on * a session drop. */ down(&session->tx_blocked); up(&session->tx_blocked); if (test_bit(SESSION_TERMINATING, &session->control_bits)) return 0; iscsi_disconnect(session); clear_bit(SESSION_LOGOUT_REQUESTED, &session->control_bits); clear_bit(SESSION_WINDOW_CLOSED, &session->control_bits); rc = establish_session(session); if (rc > 0) /* established or redirected */ login_failures = 0; else if (rc < 0) /* failed, retry */ login_failures++; else { /* failed, give up */ iscsi_host_err(session, "Session giving up\n"); set_bit(SESSION_TERMINATING, &session->control_bits); return 0; } /* slowly back off the frequency of login attempts */ if (login_failures == 0) login_delay = 0; else if (login_failures < 30) login_delay = 1; else if (login_failures < 48) login_delay = 5; else if (session->replacement_timeout && time_before_eq(session->session_drop_time + (HZ * session->replacement_timeout), jiffies)) login_delay = 10; else login_delay = 60; } return 1;}/** * get_time2wait - return iSCSI DefaultTime2Wait * @session: iscsi session * @short_sessions: number of short sessions * * Description: * Return DefaultTime2Wait. However, if the session dies really * quicky after we reach FFP, we'll not be interoperable due to bugs * in the target (or this driver) that send illegal opcodes, * or disagreements about how to do CRC calculations. To * avoid spinning, we track sessions with really short * lifetimes, and decrease the login frequency if we keep * getting session failures, like we do for login failures. **/static unsigned intget_time2wait(struct iscsi_session *session, unsigned long *short_sessions){ unsigned int login_delay = 0; if (session->time2wait >= 0) { login_delay = session->time2wait; session->time2wait = -1; } else login_delay = session->def_time2wait; if (time_before_eq(session->session_drop_time, session->session_established_time + (2 * HZ))) { (*short_sessions)++; if (*short_sessions < 30) login_delay = max_t(unsigned int, login_delay, 1); else if (*short_sessions < 48) login_delay = max_t(unsigned int, login_delay, 5); else if (session->replacement_timeout && time_before_eq(session->session_drop_time + (HZ * session->replacement_timeout), jiffies)) login_delay = max_t(unsigned int, login_delay, 10); else login_delay = max_t(unsigned int, login_delay, 60); iscsi_host_warn(session, "Session has ended quickly %lu times, " "login delay %u seconds\n", *short_sessions, login_delay); } else /* session lived long enough that the target is probably ok */ *short_sessions = 0; return login_delay;}static intiscsi_rx_thread(void *data){ struct iscsi_hdr hdr; unsigned int login_delay = 0; unsigned long short_sessions = 0; struct iscsi_session *session = (struct iscsi_session *)data; current->flags |= PF_MEMALLOC; allow_signal(SIGHUP); while (!session_kthread_sleep(session)) { if (!rx_establish_session(session, login_delay)) continue; while (!signal_pending(current)) iscsi_recv_pdu(session, &hdr, session->header_digest, session->rx_buffer, ISCSI_RXCTRL_SIZE, session->data_digest, 0); flush_signals(current); login_delay = get_time2wait(session, &short_sessions); /* * we need to wait for the tx thread to block before * trying to complete commands, since it may be using a * task at the moment, which means we can't complete it yet. * even if the session is terminating, we must wait for * the tx thread. */ down(&session->tx_blocked); up(&session->tx_blocked); spin_lock_bh(&session->task_lock); /* * session dropped unexpectedly, often due to * network problems */ iscsi_host_err(session, "Session dropped\n"); iscsi_flush_queues(session, ISCSI_MAX_LUNS, DID_BUS_BUSY); reinit_session(session); spin_unlock_bh(&session->task_lock); } /* * If there are any commands left this will remove them. */ spin_lock_bh(&session->task_lock); iscsi_flush_queues(session, ISCSI_MAX_LUNS, DID_NO_CONNECT); spin_unlock_bh(&session->task_lock); return 0;}static intstart_session_threads(struct iscsi_session *session){ session->tx_task = kthread_run(iscsi_tx_thread, session, "iscsi-tx"); if (IS_ERR(session->tx_task)) { iscsi_host_err(session, "Failed to start tx thread, terminating" " session\n"); goto fail; } session->rx_task = kthread_run(iscsi_rx_thread, session, "iscsi-rx"); if (IS_ERR(session->rx_task)) { iscsi_host_err(session, "Failed to start rx thread, terminating" " session\n"); goto shutdown_tx_thread; } init_timer(&session->session_timer); session->session_timer.data = (unsigned long)session; session->session_timer.function = session_timeout; /* * During periods of transition we use some safe value. */ session->session_timer.expires = DEF_SESSION_TIMEOUT; add_timer(&session->session_timer); return 0; shutdown_tx_thread: set_bit(SESSION_TERMINATING, &session->control_bits); kthread_stop(session->tx_task); fail: return -EAGAIN;}voidiscsi_destroy_session(struct iscsi_session *session){ set_bit(SESSION_TERMINATING, &session->control_bits); down(&iscsi_session_sem); list_del(&session->list); up(&iscsi_session_sem); clear_bit(SESSION_ESTABLISHED, &session->control_bits); session->session_drop_time = jiffies ? jiffies : 1; session->session_alive = 0; signal_iscsi_threads(session); kthread_stop(session->tx_task); kthread_stop(session->rx_task); if (session->socket) iscsi_disconnect(session); set_bit(SESSION_TERMINATED, &session->control_bits); /* * wake up any ioctls sleeping on the session * (this will be needed when update_session is fixed) */ wake_up(&session->login_wait_q); del_timer_sync(&session->session_timer); /* * grab the config mutex to make sure update_session is not * accessing the session fields we are going to free */ down(&session->config_mutex); free_session(session); up(&session->config_mutex);}intiscsi_create_session(struct iscsi_session *session, struct iscsi_session_ioctl *ioctld){ int rc; init_session_structure(session, ioctld); session->preallocated_task = kmem_cache_alloc(iscsi_task_cache, GFP_KERNEL); if (!session->preallocated_task) { iscsi_host_err(session, "Couldn't preallocate task\n"); rc = -ENOMEM; goto free_session; } session->mgmt_task = kmem_cache_alloc(iscsi_task_cache, GFP_KERNEL); if (!session->mgmt_task) { iscsi_host_err(session, "Couldn't preallocate mgmt task\n"); rc = -ENOMEM; goto free_session; } memset(session->mgmt_task, 0, sizeof(*session->mgmt_task)); iscsi_init_task(session->mgmt_task); rc = copy_iscsi_strings(session, ioctld); if (rc) goto free_session; memcpy(session->isid, ioctld->isid, sizeof(session->isid)); /* * FIXME: Do we have to check on both the username_in and * password_length_in. Same with iscsi_update_session as well? Smitha */ if (ioctld->username_in[0] || ioctld->password_length_in) session->bidirectional_auth = 1; else session->bidirectional_auth = 0; rc = alloc_auth_buffers(session); if (rc) goto free_session; memcpy(&session->portal, &ioctld->portal, sizeof(ioctld->portal)); iscsi_set_portal(session); /* * preallocate rx/tx_tfm, so that we do not have to possibly * call crypto_alloc_tfm (it uses GFP_KERNEL) while IO is queued. */ session->rx_tfm = crypto_alloc_tfm("crc32c", 0); if (!session->rx_tfm) { rc = -ENOMEM; goto free_session; } session->tx_tfm = crypto_alloc_tfm("crc32c", 0); if (!session->tx_tfm) { rc = -ENOMEM; goto free_session; } init_timer(&session->replacement_timer); session->replacement_timer.data = (unsigned long)session; session->replacement_timer.function = replacement_timed_out; rc = start_session_threads(session); up(&session->config_mutex); if (rc) goto free_session; down(&iscsi_session_sem); list_add_tail(&session->list, &iscsi_sessions); up(&iscsi_session_sem); wait_event_interruptible(session->login_wait_q, test_bit(SESSION_ESTABLISHED, &session->control_bits)); if (!test_bit(SESSION_ESTABLISHED, &session->control_bits)) { iscsi_destroy_session(session); return -ENOTCONN; } return 0; free_session: free_session(session); return rc;}struct iscsi_session *iscsi_find_session(const char *target_name, u8 isid[6], int tpgt){ struct iscsi_session *session; down(&iscsi_session_sem); list_for_each_entry(session, &iscsi_sessions, list) { if (!strcmp(session->target_name, target_name) && !memcmp(session->isid, isid, sizeof(session->isid)) && session->portal_group_tag == tpgt) { if (scsi_host_get(session->shost)) { up(&iscsi_session_sem); return session; } break; } } up(&iscsi_session_sem); return NULL;}intiscsi_update_address(struct iscsi_session *session, char *address){ struct sockaddr_in *addr; char *tag; char *port; int err = 1; tag = strrchr(address, ','); if (tag) { *tag = '\0'; tag++; } port = strrchr(address, ':'); if (port) { *port = '\0'; port++; } /* * Still only ipv4 is supported. No access to ipv6 * to test so feel free to implement it later... */ if (address[0] == '[') { iscsi_host_err(session, "Driver does not support ipv6 " "addresses\n"); err = 0; goto done; } addr = (struct sockaddr_in *)&session->addr; addr->sin_addr.s_addr = in_aton(address); if (port) addr->sin_port = htons(simple_strtoul(port, NULL, 0)); else addr->sin_port = htons(ISCSI_LISTEN_PORT); done: /* restore the original strings */ if (tag) { --tag; *tag = ','; } if (port) { --port; *port = ':'; } return err;}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?