📄 iscsi-probe.c
字号:
/* * iSCSI driver for Linux * Copyright (C) 2001 Cisco Systems, Inc. * maintained by linux-iscsi@cisco.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * See the file COPYING included with this distribution for more details. * * $Id: iscsi-probe.c,v 1.14 2002/10/14 22:54:11 smferris Exp $ * */#include <linux/config.h>#include <linux/version.h>#include <linux/sched.h>#include <asm/io.h>#include <asm/byteorder.h>#include <linux/stddef.h>#include <linux/string.h>#include <linux/errno.h>#include <linux/file.h>#include <linux/kernel.h>#include <linux/ioport.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/proc_fs.h>#include <linux/blk.h>#include <linux/types.h>#include <linux/stat.h>#include <linux/config.h>#include <linux/poll.h>#include <linux/smp_lock.h>#include <linux/kernel.h>#include <linux/wait.h>#include <linux/net.h>#include <net/sock.h>#include <linux/socket.h>#include <linux/errno.h>#include <linux/unistd.h>#include <linux/timer.h>#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) )# include <asm/semaphore.h>#else# include <asm/spinlock.h>#endif#include <asm/uaccess.h>#include <scsi/sg.h>#include <sd.h>#include <scsi.h>#include <hosts.h>#include "iscsi-common.h"#include "iscsi-ioctl.h"#include "iscsi.h"/* LUN probing needs to be serialized across all HBA's, to keep a somewhat sane ordering */#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) )DECLARE_MUTEX(iscsi_lun_probe_mutex);#elsestruct semaphore iscsi_lun_probe_mutex = MUTEX;#endifspinlock_t iscsi_lun_probe_lock = SPIN_LOCK_UNLOCKED;static iscsi_session_t *iscsi_lun_probe_head = NULL;static iscsi_session_t *iscsi_lun_probe_tail = NULL; static iscsi_session_t *iscsi_currently_probing = NULL; static volatile int iscsi_next_probe = 0;volatile unsigned long iscsi_lun_probe_start = 0;struct dirent{ long d_ino; /* inode number */ off_t d_off; /* offset to next dirent */ unsigned short d_reclen; /* length of this dirent */ char d_name[1]; /* file name (null-terminated) */};/* we need to make some syscalls to create and destroy the device name tree. */static int errno = 0;static inline _syscall2(long, mkdir, const char *, dir, int, mode);static inline _syscall1(long, unlink, const char *, path);static inline _syscall2(long, symlink, const char *, oldname, const char *, newname);static inline _syscall3(int,open,const char *,file,int,flag,int,mode)static inline _syscall1(int,close,int,fd)static inline _syscall1(long, rmdir, const char *, path);static inline _syscall3(int, getdents, uint, fd, struct dirent *, dirp, uint, count);/* caller must hold iscsi_lun_probe_lock */static int enqueue_lun_probe(iscsi_session_t *session){ if (session->probe_next || session->probe_prev) { DEBUG_INIT1("iSCSI: session %p already queued for LUN probing\n", session); return 0; } if (iscsi_lun_probe_head) { if (session->probe_order < iscsi_lun_probe_head->probe_order) { /* insert before the current head */ session->probe_prev = NULL; session->probe_next = iscsi_lun_probe_head; iscsi_lun_probe_head->probe_prev = session; iscsi_lun_probe_head = session; } else if (session->probe_order >= iscsi_lun_probe_tail->probe_order) { /* insert after the tail */ session->probe_next = NULL; session->probe_prev = iscsi_lun_probe_tail; iscsi_lun_probe_tail->probe_next = session; iscsi_lun_probe_tail = session; } else { /* insert somewhere in the middle */ iscsi_session_t *search = iscsi_lun_probe_head; while (search && search->probe_next) { if (session->probe_order < search->probe_next->probe_order) { session->probe_next = search->probe_next; session->probe_prev = search; search->probe_next->probe_prev = session; search->probe_next = session; break; } search = search->probe_next; } } } else { /* become the only session in the queue */ session->probe_next = session->probe_prev = NULL; iscsi_lun_probe_head = iscsi_lun_probe_tail = session; } return 1;}/* caller must hold iscsi_lun_probe_lock */static void dequeue_lun_probe(iscsi_session_t *session){ if (iscsi_currently_probing == session) { /* the timer may have tried to start us probing just before we gave up */ iscsi_currently_probing = NULL; } else { if (iscsi_lun_probe_head == session) { if ((iscsi_lun_probe_head = iscsi_lun_probe_head->probe_next)) iscsi_lun_probe_head->probe_prev = NULL; else iscsi_lun_probe_tail = NULL; } else if (iscsi_lun_probe_tail == session) { iscsi_lun_probe_tail = iscsi_lun_probe_tail->probe_prev; iscsi_lun_probe_tail->probe_next = NULL; } else { /* in the middle */ if (session->probe_next && session->probe_prev) { session->probe_prev->probe_next = session->probe_next; session->probe_next->probe_prev = session->probe_prev; } else { printk("iSCSI: bug - dequeue_lun_probe %p, prev %p, next %p\n", session, session->probe_prev, session->probe_next); } } }}static int wait_for_probe_order(iscsi_session_t *session){ spin_lock(&iscsi_lun_probe_lock); if ((iscsi_currently_probing == session) || session->probe_next || session->probe_prev) { /* we're already probing or queued to be probed, ignore the 2nd probe request */ DEBUG_INIT2("iSCSI: session %p to %s ignoring duplicate probe request\n", session, session->log_name); spin_unlock(&iscsi_lun_probe_lock); return 0; } else if ((iscsi_currently_probing == NULL) && (session->probe_order <= iscsi_next_probe)) { /* if there's no LUN being probed, and our probe_order can go now, start probing */ DEBUG_INIT4("iSCSI: session %p to %s, probe_order %d <= next %d, not waiting\n", session, session->log_name, session->probe_order, iscsi_next_probe); iscsi_currently_probing = session; /* let the timer know another sessions became ready for LUN probing. */ iscsi_lun_probe_start = (jiffies + (3 * HZ)) ? (jiffies + (3 * HZ)) : 1; wmb(); spin_unlock(&iscsi_lun_probe_lock); return 1; } else if (enqueue_lun_probe(session)) { /* otherwise queue up based on our probe order */ /* tell the timer when to start the LUN probing, to handle gaps in the probe_order */ iscsi_lun_probe_start = (jiffies + (3 * HZ)) ? (jiffies + (3 * HZ)) : 1; wmb(); DEBUG_INIT3("iSCSI: queued session %p for LUN probing, probe_order %d, probe_start at %lu\n", session, session->probe_order, iscsi_lun_probe_start); spin_unlock(&iscsi_lun_probe_lock); /* and wait for either the timer or the currently probing session to wake us up */ if (down_interruptible(&session->probe_sem)) { printk("iSCSI: session %p to %s interrupted while waiting to probe LUNs\n", session, session->log_name); /* give up and take ourselves out of the lun probing data structures */ spin_lock(&iscsi_lun_probe_lock); dequeue_lun_probe(session); spin_unlock(&iscsi_lun_probe_lock); return 0; } /* give up if the session is terminating */ if (test_bit(SESSION_TERMINATING, &session->control_bits)) { printk("iSCSI: session %p to %s terminated while waiting to probe LUNs\n", session, session->log_name); /* give up and take ourselves out of the lun probing data structures */ spin_lock(&iscsi_lun_probe_lock); dequeue_lun_probe(session); spin_unlock(&iscsi_lun_probe_lock); return 0; } #ifdef DEBUG /* we should be out of the queue, and in iscsi_currently_probing */ spin_lock(&iscsi_lun_probe_lock); if (iscsi_currently_probing != session) printk("iSCSI: bug - currently probing should be %p, not %p\n", session, iscsi_currently_probing); spin_unlock(&iscsi_lun_probe_lock);#endif DEBUG_INIT1("iSCSI: wait_for_probe_order %p returning 1\n", session); return 1; } /* silently fail, since the enqueue attempt will have logged any detailed messages needed */ spin_unlock(&iscsi_lun_probe_lock); return 0;}/* caller must hold iscsi_lun_probe_lock */static void start_next_lun_probe(void){ /* if we're not probing anything currently, start probing the head of the queue */ if (iscsi_lun_probe_head && (iscsi_currently_probing == NULL)) { /* pop one off the queue, and tell it to start probing */ iscsi_currently_probing = iscsi_lun_probe_head; if ((iscsi_lun_probe_head = iscsi_currently_probing->probe_next)) iscsi_lun_probe_head->probe_prev = NULL; else iscsi_lun_probe_tail = NULL; /* it's out of the queue now */ iscsi_currently_probing->probe_next = NULL; iscsi_currently_probing->probe_prev = NULL; /* skip over any gaps in the probe order */ if (iscsi_next_probe < iscsi_currently_probing->probe_order) { DEBUG_INIT2("iSCSI: LUN probe_order skipping from %d to %d\n", iscsi_next_probe, iscsi_currently_probing->probe_order); iscsi_next_probe = iscsi_currently_probing->probe_order; wmb(); } /* wake up the ioctl which is waiting to do a probe */ DEBUG_INIT2("iSCSI: starting LUN probe for session %p to %s\n", iscsi_currently_probing, iscsi_currently_probing->log_name); up(&iscsi_currently_probing->probe_sem); } else { if (!iscsi_lun_probe_head) { /* if there is nothing else queued, then we don't need the timer to keep checking */ iscsi_lun_probe_start = 0; wmb(); } DEBUG_INIT6("iSCSI: ignoring start_next_lun_probe at %lu, next %d, head %p, tail %p, current %p, start time %lu\n", jiffies, iscsi_next_probe, iscsi_lun_probe_head, iscsi_lun_probe_tail, iscsi_currently_probing, iscsi_lun_probe_start); }}void iscsi_possibly_start_lun_probing(void) { spin_lock(&iscsi_lun_probe_lock); if (!iscsi_currently_probing) { /* if we're not probing already, make sure we start */ DEBUG_INIT1("iSCSI: timer starting LUN probing at %lu\n", jiffies); start_next_lun_probe(); } spin_unlock(&iscsi_lun_probe_lock);}static void iscsi_probe_finished(iscsi_session_t *session){ spin_lock(&iscsi_lun_probe_lock); if (iscsi_currently_probing == session) { iscsi_currently_probing = NULL; DEBUG_INIT3("iSCSI: session %p to %s finished probing LUNs at %lu\n", session, session->log_name, jiffies); /* continue through the probe order */ if (iscsi_next_probe == session->probe_order) iscsi_next_probe++; /* and possibly start another session probing */ if (iscsi_lun_probe_head && ((iscsi_lun_probe_head->probe_order <= iscsi_next_probe) || (iscsi_lun_probe_start && time_before_eq(iscsi_lun_probe_start, jiffies)))) { start_next_lun_probe(); } else {#if DEBUG_INIT if (LOG_ENABLED(ISCSI_LOG_INIT)) printk("iSCSI: iscsi_probe_finished can't start_next_lun_probe at %lu, next %d, head %p (%d), tail %p (%d), current %p, start time %lu\n", jiffies, iscsi_next_probe, iscsi_lun_probe_head, iscsi_lun_probe_head ? iscsi_lun_probe_head->probe_order : -1, iscsi_lun_probe_tail, iscsi_lun_probe_tail ? iscsi_lun_probe_tail->probe_order : -1, iscsi_currently_probing, iscsi_lun_probe_start);#endif } } else { /* should be impossible */ printk("iSCSI: bug - session %p in iscsi_probe_finished, but currently probing %p\n", session, iscsi_currently_probing); } spin_unlock(&iscsi_lun_probe_lock);}/* try to write to /proc/scsi/scsi */static int write_proc_scsi_scsi(iscsi_session_t *session, char *str){ struct file *filp = NULL; loff_t offset = 0; int rc = 0; mm_segment_t oldfs = get_fs(); set_fs( get_ds() ); filp = filp_open("/proc/scsi/scsi", O_WRONLY, 0); if (IS_ERR(filp)) { printk("iSCSI: session %p couldn't open /proc/scsi/scsi\n", session); set_fs(oldfs); return -ENOENT; } rc = filp->f_op->write(filp, str, strlen(str), &offset); filp_close(filp, 0); set_fs(oldfs); if (rc >= 0) { /* assume it worked, since the non-negative return codes aren't set very reliably. * wait for 20 ms to avoid deadlocks on SMP systems. * FIXME: figure out why the SMP systems need this wait, and fix the kernel. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -