📄 iscsi.c
字号:
* symbols like child_reaper aren't exported to modules */ /* increase priority like the md driver does for it's kernel threads */ this_task->policy = SCHED_NORMAL; this_task->nice = -20; wmb(); # else /* 2.2.18 and later has daemonize(), but it's not always correct, so we do it ourselves. */ struct fs_struct *fs; lock_kernel(); /* * If we were started as result of loading a module, close all of the * user space pages. We don't need them, and if we didn't close them * they would be locked into memory. */ exit_mm(this_task); this_task->session = 1; this_task->pgrp = 1; this_task->tty = NULL; /* Become as one with the init task */ exit_files(this_task); this_task->files = init_task.files; atomic_inc(&this_task->files->count); exit_fs(this_task); /* this_task->fs->count--; */ fs = init_task.fs; this_task->fs = fs; atomic_inc(&fs->count); /* We'd like to reparent to init, but don't have a function to do it, and * symbols like child_reaper aren't exported to modules. */ /* increase priority like the md driver does for it's kernel threads */ this_task->policy = SCHED_NORMAL; this_task->priority = 40; wmb(); unlock_kernel();# endif}/* wake up the tx_thread without ever losing the wakeup event */static void wake_tx_thread(int control_bit, iscsi_session_t *session){ /* tell the tx thread what to do when it wakes up. */ set_bit(control_bit, &session->control_bits); /* We make a condition variable out of a wait queue and atomic test&clear. * May get spurious wake-ups, but no wakeups will be lost. * this is cv_signal(). wait_event_interruptible is cv_wait(). */ set_bit(TX_WAKE, &session->control_bits); wake_up(&session->tx_wait_q);}/* drop an iscsi session */static void iscsi_drop_session(iscsi_session_t *session){ pid_t pid; DEBUG_INIT4("iSCSI: iscsi_drop_session %p, rx %d, tx %d at %lu\n", session, session->rx_pid, session->tx_pid, jiffies); clear_bit(SESSION_ESTABLISHED, &session->control_bits); set_bit(SESSION_DROPPED, &session->control_bits); /* so we know whether to abort the connection */ if ((pid = session->tx_pid)) kill_proc(pid, SIGHUP, 1); if ((pid = session->rx_pid)) kill_proc(pid, SIGHUP, 1);}/* caller must hold session->task_lock */static void iscsi_request_logout(iscsi_session_t *session, int logout, int logout_response){ if (atomic_read(&session->num_active_tasks) == 0) { DEBUG_INIT2("iSCSI: session %p currently has no active tasks, queueing logout at %lu\n", session, jiffies); session->logout_response_deadline = jiffies + (logout_response * HZ); if (session->logout_response_deadline == 0) session->logout_response_deadline = 1; wmb(); set_bit(SESSION_LOGOUT_REQUESTED, &session->control_bits); wake_tx_thread(TX_LOGOUT, session); } else { session->logout_deadline = jiffies + (logout * HZ); if (session->logout_deadline == 0) session->logout_deadline = 1; session->logout_response_deadline = session->logout_deadline + (logout_response * HZ); if (session->logout_response_deadline == 0) session->logout_response_deadline = 1; wmb(); set_bit(SESSION_LOGOUT_REQUESTED, &session->control_bits); }}static void iscsi_terminate_session(iscsi_session_t *session){ pid_t pid; if ((test_and_set_bit(SESSION_TERMINATING, &session->control_bits) == 0) && test_bit(SESSION_ESTABLISHED, &session->control_bits)) { DEBUG_INIT2("iSCSI: iscsi_terminate_session %p, requesting logout at %lu\n", session, jiffies); /* on the first terminate request while the session is up, request a logout in the next 3 seconds */ spin_lock(&session->task_lock); iscsi_request_logout(session, 3, session->active_timeout); spin_unlock(&session->task_lock); } else { /* either we've already tried to terminate once, or the the session is down. just kill everything. */ clear_bit(SESSION_ESTABLISHED, &session->control_bits); DEBUG_INIT4("iSCSI: iscsi_terminate_session %p, killing rx %d, tx %d at %lu\n", session, session->rx_pid, session->tx_pid, jiffies); /* kill the session's threads */ if ((pid = session->tx_pid)) kill_proc(pid, SIGKILL, 1); if ((pid = session->rx_pid)) kill_proc(pid, SIGKILL, 1); }}/* if a signal is pending, deal with it, and return 1. * Otherwise, return 0. */static int iscsi_handle_signals(iscsi_session_t *session){ pid_t pid; int ret = 0; /* if we got SIGHUP, try to establish a replacement session. * if we got SIGKILL, terminate this session. */ if (signal_pending(current)) { spin_lock_irq(¤t->sighand->siglock); /* iscsi_drop_session and iscsi_terminate_session signal both * threads, but someone logged in as root may not. So, we * make sure whichever process gets signalled first propagates * the signal when it looks like only one thread got * signalled. */ /* on SIGKILL, terminate the session */ if (SIGNAL_IS_PENDING(SIGKILL)) { if (!test_and_set_bit(SESSION_TERMINATING, &session->control_bits)) { if ((pid = session->tx_pid) && (pid != current->pid)) { printk("iSCSI: rx thread %d received SIGKILL, killing tx thread %d\n", current->pid, pid); kill_proc(pid, SIGKILL, 1); } if ((pid = session->rx_pid) && (pid != current->pid)) { printk("iSCSI: tx thread %d received SIGKILL, killing rx thread %d\n", current->pid, pid); kill_proc(pid, SIGKILL, 1); } } ret = 1; } /* on SIGHUP, drop the session, and try to establish a replacement session */ if (SIGNAL_IS_PENDING(SIGHUP)) { if (test_and_clear_bit(SESSION_ESTABLISHED, &session->control_bits)) { if ((pid = session->tx_pid) && (pid != current->pid)) { printk("iSCSI: rx thread %d received SIGHUP, signaling tx thread %d\n", current->pid, pid); kill_proc(pid, SIGHUP, 1); } if ((pid = session->rx_pid) && (pid != current->pid)) { printk("iSCSI: tx thread %d received SIGHUP, signaling rx thread %d\n", current->pid, pid); kill_proc(pid, SIGHUP, 1); } } ret = 1; } /* we don't care about any other signals */ flush_signals(current); spin_unlock_irq(¤t->sighand->siglock); } return ret;}unsigned int iscsi_expected_data_length(Scsi_Cmnd *sc){ unsigned int length = 0; switch (sc->cmnd[0]) { case INQUIRY: case REQUEST_SENSE: length = sc->cmnd[4]; return length; case REPORT_LUNS: length |= sc->cmnd[6] << 24; length |= sc->cmnd[7] << 16; length |= sc->cmnd[8] << 8; length |= sc->cmnd[9]; return length; default: return sc->request_bufflen; }}/* compare against 2^31 */#define SNA32_CHECK 2147483648UL/* Serial Number Arithmetic, 32 bits, less than, RFC1982 */static int sna_lt(uint32_t n1, uint32_t n2){ return ((n1 != n2) && (((n1 < n2) && ((n2 - n1) < SNA32_CHECK)) || ((n1 > n2) && ((n2 - n1) < SNA32_CHECK))));}/* Serial Number Arithmetic, 32 bits, less than, RFC1982 */static int sna_lte(uint32_t n1, uint32_t n2){ return ((n1 == n2) || (((n1 < n2) && ((n2 - n1) < SNA32_CHECK)) || ((n1 > n2) && ((n2 - n1) < SNA32_CHECK))));}/* difference isn't really a defined operation in SNA, but we'd like it so that * we can determine how many commands can be queued to a session. */static int cmdsn_window_size(uint32_t expected, uint32_t max){ if ((expected <= max) && ((max - expected) < SNA32_CHECK)) { return (max - expected + 1); } else if ((expected > max) && ((expected - max) < SNA32_CHECK)) { /* window wraps around */ return ((UINT32_MAX - expected) + 1 + max + 1); } else { /* window closed, or numbers bogus */ return 0; }}/* remember old peak cmdsn window size, and report the largest */static int max_tasks_for_session(iscsi_session_t *session){ if (session->ExpCmdSn == session->MaxCmdSn + 1) /* if the window is closed, report nothing, regardless of what it was in the past */ return 0; else if (session->last_peak_window_size < session->current_peak_window_size) /* window increasing, so report the current peak size */ return MIN(session->current_peak_window_size, ISCSI_CMDS_PER_LUN * session->num_luns); else /* window decreasing. report the previous peak size, in case it's * a temporary decrease caused by the commands we're sending. * we want to keep the right number of commands queued in the driver, * ready to go as soon as they can. */ return MIN(session->last_peak_window_size, ISCSI_CMDS_PER_LUN * session->num_luns);}/* possibly update the ExpCmdSN and MaxCmdSN, and peak window sizes */static void updateSN(iscsi_session_t *session, UINT32 expcmdsn, UINT32 maxcmdsn) { int window_size; /* standard specifies this check for when to update expected and max sequence numbers */ if (!sna_lt(maxcmdsn, expcmdsn - 1)) { if ((expcmdsn != session->ExpCmdSn) && !sna_lt(expcmdsn, session->ExpCmdSn)) { session->ExpCmdSn = expcmdsn; } if ((maxcmdsn != session->MaxCmdSn) && !sna_lt(maxcmdsn, session->MaxCmdSn)) { session->MaxCmdSn = maxcmdsn; /* look for the peak window size */ window_size = cmdsn_window_size(expcmdsn, maxcmdsn); if (window_size > session->current_peak_window_size) session->current_peak_window_size = window_size; /* age peak window size info */ if (time_before(session->window_peak_check + (15 * HZ), jiffies)) { session->last_peak_window_size = session->current_peak_window_size; session->current_peak_window_size = window_size; session->window_peak_check = jiffies; } /* memory barrier for all of that */ mb(); /* wake the tx thread to try sending more commands */ wake_tx_thread(TX_SCSI_COMMAND, session); } /* record whether or not the command window for this session has closed, * so that we can ping the target periodically to ensure we eventually * find out that the window has re-opened. */ if (maxcmdsn == expcmdsn - 1) { session->current_peak_window_size = 0; set_bit(SESSION_WINDOW_CLOSED, &session->control_bits); } else clear_bit(SESSION_WINDOW_CLOSED, &session->control_bits); DEBUG_FLOW3("iSCSI: session %p - ExpCmdSN %u, MaxCmdSN %u\n", session, session->ExpCmdSn, session->MaxCmdSn); }}/* add a session from an HBA's collection of sessions. * caller must hold the HBA's session lock. */static int add_session(iscsi_hba_t *hba, iscsi_session_t *session){ iscsi_session_t *prior, *next; if (!hba) return 0; if (test_bit(ISCSI_HBA_SHUTTING_DOWN, &hba->flags)) { printk("iSCSI: HBA %p is shutting down, can't add session %p\n", hba, session); return 0; } prior = NULL; next = hba->session_list_head; while (next && (next->channel < session->channel)) { prior = next; next = prior->next; } while (next && (next->channel == session->channel) && (next->target_id < session->target_id)) { prior = next; next = prior->next; } /* same Linux SCSI address? */ if (next && (next->channel == session->channel) && (next->target_id == session->target_id)) { if (strcmp(next->TargetName, session->TargetName) == 0) { /* already have a session running to this target */ printk("iSCSI: session to %s already exists\n", session->TargetName); } else { printk("iSCSI: error - TargetName %s cannot claim bus %d id %d, already in use by %s\n", session->TargetName, session->iscsi_bus, next->target_id, next->TargetName); } return 0; } else { /* insert the session into the list */ if ((session->next = next)) next->prev = session; else hba->session_list_tail = session; if ((session->prev = prior)) prior->next = session; else hba->session_list_head = session; session->hba = hba; wmb(); DEBUG_INIT2("iSCSI: added session %p to hba %p\n", session, hba); return 1; }}/* remove a session from an HBA's collection of sessions. * caller must hold the HBA's session lock. */static int remove_session(iscsi_hba_t *hba, iscsi_session_t *session){ if (session->hba && (hba != session->hba)) { printk("iSCSI: tried to remove session %p from hba %p, but session->hba is %p\n", session, hba, session->hba); return 0; } /* remove the session from the HBA */ if (session == hba->session_list_head) { if ((hba->session_list_head = session->next)) hba->session_list_head->prev = NULL; else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -