📄 iscsi_main.c
字号:
# ifdef PROC_SCSI_ISCSI PROC_SCSI_ISCSI,# else PROC_SCSI_NOT_PRESENT,# endif 5, "iscsi", S_IFDIR|S_IRUGO|S_IXUGO, 2};#endif/* become a daemon kernel thread. Some kernels provide this functionality * already, and some even do it correctly */void iscsi_daemonize(void){ /* use the kernel's daemonize */ daemonize(); /* Reparent to init */ reparent_to_init(); /* increase priority like the md driver does for it's kernel threads */ wmb();}/* drop an iscsi session */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); if ((pid = session->tx_pid)) kill_proc(pid, SIGHUP, 1); if ((pid = session->rx_pid)) kill_proc(pid, SIGHUP, 1);}void iscsi_terminate_session(iscsi_session_t *session){ pid_t pid; DEBUG_INIT4("iSCSI: iscsi_terminate_session %p, rx %d, tx %d at %lu\n", session, session->rx_pid, session->tx_pid, jiffies); set_bit(SESSION_TERMINATING, &session->control_bits); clear_bit(SESSION_ESTABLISHED, &session->control_bits); 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;}/* 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);}/* 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; 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; mb(); 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 hba->session_list_tail = NULL; } else if (session == hba->session_list_tail) { hba->session_list_tail = session->prev; hba->session_list_tail->next = NULL; } else { /* we should always be in the middle, * but check pointers to make sure we don't crash the kernel * if the function is called for a session not on the hba. */ if (session->next && session->prev) { session->next->prev = session->prev; session->prev->next = session->next; } else { DEBUG_ERR2("iSCSI: failed to remove session %p from hba %p\n", session, hba); return 0; } } session->prev = NULL; session->next = NULL; return 1;}static iscsi_session_t *find_session_for_cmnd(Scsi_Cmnd *sc){ iscsi_session_t *session = NULL; iscsi_hba_t *hba; DECLARE_NOQUEUE_FLAGS; if (!sc->host) return NULL; if (!sc->host->hostdata) return NULL; hba = (iscsi_hba_t *)sc->host->hostdata; /* find the session for this command */ SPIN_LOCK_NOQUEUE(&hba->session_lock); session = hba->session_list_head; while (session && (session->channel != sc->channel || session->target_id != sc->target)) session = session->next; if (session) atomic_inc(&session->refcount); /* caller must use drop_reference when it's done with the session */ SPIN_UNLOCK_NOQUEUE(&hba->session_lock); return session;}/* decrement the session refcount, and remove it and free it if the refcount hit zero */static void drop_reference(iscsi_session_t *session){ iscsi_hba_t *hba = session->hba; DECLARE_NOQUEUE_FLAGS; SPIN_LOCK_NOQUEUE(&hba->session_lock); if (atomic_dec_and_test(&session->refcount)) { if (remove_session(hba, session)) { DEBUG_INIT1("iSCSI: terminated and deleted session %p\n", session); memset(session, 0, sizeof(*session)); kfree(session); } else { printk("iSCSI: bug - failed to remove unreferenced session %p\n", session); } } SPIN_UNLOCK_NOQUEUE(&hba->session_lock);}/* must hold the task_lock to call this */static iscsi_task_t *find_task(iscsi_task_collection_t *collection, uint32_t itt){ iscsi_task_t *task = collection->head; while (task) { if (task->itt == itt) { DEBUG_FLOW3("iSCSI: found itt %u, task %p, refcount %d\n", itt, task, atomic_read(&task->refcount)); return task; } task = task->next; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -