* [iscsi 2/2] iscsi-probe.c
@ 2003-09-23 15:23 Jeff Garzik
2003-09-23 15:53 ` Christoph Hellwig
0 siblings, 1 reply; 13+ messages in thread
From: Jeff Garzik @ 2003-09-23 15:23 UTC (permalink / raw)
To: linux-scsi
For review only.
/*
* 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.24.2.1 2003/09/09 11:52:33 smhatre 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/dirent.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>
# include <asm/semaphore.h>
#include <asm/uaccess.h>
#include <scsi/sg.h>
#include <endian.h>
/* these are from $(TOPDIR)/drivers/scsi, not $(TOPDIR)/include */
#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 */
DECLARE_MUTEX(iscsi_lun_probe_mutex);
spinlock_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;
/* 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_INIT
("iSCSI: session for bus_id id %d target_id %d already queued for LUN probing\n",
session->iscsi_bus, session->target_id);
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 session for bus %d target %d , prev %p, next %p\n",
session->iscsi_bus, session->target_id,
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_INIT
("iSCSI: session for bus %d target %d to %s ignoring duplicate probe request\n",
session->iscsi_bus, session->target_id, 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_INIT
("iSCSI: session for bus %d target %d to %s, probe_order %d <= next %d, not waiting\n",
session->iscsi_bus, session->target_id, session->log_name,
session->probe_order, iscsi_next_probe);
iscsi_currently_probing = session;
/* let the timer know another session became ready for LUN probing. */
iscsi_lun_probe_start = (jiffies + (3 * HZ));
if (iscsi_lun_probe_start == 0)
iscsi_lun_probe_start = 1;
smp_mb();
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;
smp_mb();
DEBUG_INIT
("iSCSI: queued session for bus %d target %d for LUN probing, probe_order %d, probe_start at %lu\n",
session->iscsi_bus, session->target_id,
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)) {
logmsg(AS_INFO,
"iSCSI: session for bus %d target %d to %s interrupted while waiting to probe LUNs\n",
session->iscsi_bus, session->target_id,
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)) {
logmsg(AS_INFO,
"iSCSI: session for bus %d target %d to %s terminated while waiting to probe LUNs\n",
session->iscsi_bus, session->target_id,
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 (bus %d target %d) , not (bus %d target %d\n",
session->iscsi_bus, session->target_id,
iscsi_currently_probing->iscsi_bus,
iscsi_currently_probing->target_id);
spin_unlock(&iscsi_lun_probe_lock);
#endif
DEBUG_INIT
("iSCSI: wait_for_probe_order (bus %d target %d) returning 1\n",
session->iscsi_bus, session->target_id);
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 (iscsi_currently_probing) {
printk
("iSCSI: bug - start_next_lun_probe called while currently probing on (bus %d target %d at %lu\n",
iscsi_currently_probing->iscsi_bus,
iscsi_currently_probing->target_id, jiffies);
} else if (iscsi_lun_probe_head) {
/* 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_INIT
("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;
smp_mb();
}
/* wake up the ioctl which is waiting to do a probe */
DEBUG_INIT
("iSCSI: starting LUN probe for session (bus %d target %d) to %s\n",
iscsi_currently_probing->iscsi_bus,
iscsi_currently_probing->
target_id iscsi_currently_probing->log_name);
up(&iscsi_currently_probing->probe_sem);
} else {
/* if there is nothing else queued, then we don't need the timer to keep checking,
* and we want to reset the probe order so that future LUN probes get queued,
* and maintain the proper relative order amonst themselves, even if the global
* order may have been lost.
*/
DEBUG_INIT
("iSCSI: start_next_lun_probe has nothing to start, resetting next LUN probe from %d to 0 at %lu\n",
iscsi_next_probe, jiffies);
iscsi_lun_probe_start = 0;
iscsi_next_probe = 0;
smp_mb();
}
}
void
iscsi_possibly_start_lun_probing(void)
{
spin_lock(&iscsi_lun_probe_lock);
if (iscsi_currently_probing == NULL) {
/* if we're not probing already, make sure we start */
DEBUG_INIT("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_INIT
("iSCSI: session (bus %d target %d) to %s finished probing LUNs at %lu\n",
session->iscsi_bus, session->target_id, 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 == NULL) {
/* nothing is queued, reset LUN probing */
DEBUG_INIT
("iSCSI: probe_finished has nothing to start, resetting next LUN probe from %d to 0 at %lu\n",
iscsi_next_probe, jiffies);
iscsi_next_probe = 0;
iscsi_lun_probe_start = 0;
smp_mb();
} else
if ((iscsi_lun_probe_head->probe_order <= iscsi_next_probe)
|| (iscsi_lun_probe_start
&& time_before_eq(iscsi_lun_probe_start,
jiffies))) {
/* next in order is up, or the timer has expired, start probing */
start_next_lun_probe();
} else {
DEBUG_INIT
("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);
}
} else {
/* should be impossible */
printk
("iSCSI: bug - session (bus %d target %d) in iscsi_probe_finished, but currently probing (bus %d target %d)\n",
session->iscsi_bus, session->target_id,
iscsi_currently_probing->iscsi_bus,
iscsi_currently_probing->target_id);
}
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 (bus %d target %d)couldn't open /proc/scsi/scsi\n",
session->iscsi_bus, session->target_id);
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.
*/
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(MSECS_TO_JIFFIES(20));
return 1;
}
return rc;
}
/* caller must hold the iscsi_lun_probe_mutex */
static int
iscsi_probe_lun(iscsi_session_t * session, int lun)
{
char str[80];
int rc;
if (lun >= ISCSI_MAX_LUN)
return 0;
sprintf(str, "scsi add-single-device %d %d %d %d\n",
session->host_no, session->channel, session->target_id, lun);
str[sizeof (str) - 1] = '\0';
rc = write_proc_scsi_scsi(session, str);
if (rc < 0) {
/* clear the newline */
str[strlen(str) - 1] = '\0';
logmsg
(AS_ERROR,
"iSCSI: session (bus %d target %d) error %d writing '%s' to /proc/scsi/scsi\n",
session->iscsi_bus, session->target_id, rc, str);
return 0;
}
return rc;
}
static int
iscsi_remove_lun(iscsi_session_t * session, int lun)
{
char str[88];
int rc = 0;
sprintf(str, "scsi remove-single-device %d %d %d %d\n",
session->host_no, session->channel, session->target_id, lun);
str[sizeof (str) - 1] = '\0';
rc = write_proc_scsi_scsi(session, str);
if (rc < 0) {
/* clear the newline */
str[strlen(str) - 1] = '\0';
printk
("iSCSI: session (bus %d target %d) error %d writing '%s' to /proc/scsi/scsi\n",
session->iscsi_bus, session->target_id, rc, str);
return 0;
} else {
/* removed it */
clear_bit(lun, session->luns_activated);
clear_bit(lun, session->luns_detected);
return 1;
}
return rc;
}
static void
empty_directory(char *dir, char *data, int size)
{
int fd;
struct dirent dent;
int rc, processed;
char *name = dir + strlen(dir);
/* there should only be directories in the target dir */
if ((fd = open(dir, O_DIRECTORY | O_RDONLY, 0)) >= 0) {
/* loop doing getdents, and unlinking files */
do {
rc = getdents(fd, (struct dirent *) data, size);
DEBUG_FLOW("iSCSI: getdents %s, size %d, returned %d\n",
dir, size, rc);
processed = 0;
while (processed < rc) {
memcpy(&dent, &data[processed], sizeof (dent));
strcpy(name,
&data[processed] +
offsetof(struct dirent, d_name));
if (strcmp(name, ".") && strcmp(name, "..")) {
DEBUG_FLOW("iSCSI: unlink %s\n", dir);
unlink(dir);
}
processed += dent.d_reclen;
}
} while (rc > 0);
name[0] = '\0';
close(fd);
}
}
void
iscsi_remove_luns(iscsi_session_t * session)
{
int l;
mm_segment_t oldfs;
char *data = session->rx_buffer;
int size = sizeof (session->rx_buffer) - 1;
char *lun_dir =
session->target_link_dir + strlen(session->target_link_dir);
char *bus_dir = lun_dir - 2; /* before the slash */
char c;
/* try to release the kernel's SCSI device structures for every LUN */
down(&iscsi_lun_probe_mutex);
oldfs = get_fs();
set_fs(get_ds());
for (l = 0; l < ISCSI_MAX_LUN; l++) {
if (session->target_link_dir[0] == '/') {
sprintf(lun_dir, "lun%d/", l);
/* this assumes the session isn't using the rx_buffer right now */
empty_directory(session->target_link_dir, data, size);
rmdir(session->target_link_dir);
}
if (test_bit(l, session->luns_activated)) {
/* tell Linux to release the Scsi_Devices */
iscsi_remove_lun(session, l);
}
}
if (session->target_link_dir[0] == '/') {
/* and get rid of the target dir itself */
*lun_dir = '\0';
DEBUG_FLOW("iSCSI: rmdir %s\n", session->target_link_dir);
rmdir(session->target_link_dir);
/* if the bus dir is empty now, get rid of it too, but don't corrupt the session's target dir */
while (*bus_dir != '/')
bus_dir--;
bus_dir++; /* leave the slash */
c = *bus_dir;
*bus_dir = '\0';
DEBUG_FLOW("iSCSI: rmdir %s\n", session->target_link_dir);
rmdir(session->target_link_dir);
*bus_dir = c;
}
set_fs(oldfs);
up(&iscsi_lun_probe_mutex);
}
void
iscsi_remove_lun_complete(iscsi_session_t * session, int lun_id)
{
mm_segment_t oldfs;
char data[sizeof (struct dirent) + 1];
int size = sizeof (data) - 1;
char *lun_dir =
session->target_link_dir + strlen(session->target_link_dir);
char *bus_dir = lun_dir - 2; /* before the slash */
char c;
/* try to release the kernel's SCSI device structures for every LUN */
down(&iscsi_lun_probe_mutex);
oldfs = get_fs();
set_fs(get_ds());
if (session->target_link_dir[0] == '/') {
sprintf(lun_dir, "lun%d/", lun_id);
/* this assumes the session isn't using the rx_buffer right now */
empty_directory(session->target_link_dir, data, size);
rmdir(session->target_link_dir);
}
if (test_bit(lun_id, session->luns_activated)) {
/* tell Linux to release the Scsi_Devices */
iscsi_remove_lun(session, lun_id);
}
if (session->target_link_dir[0] == '/') {
/* and get rid of the target dir itself */
*lun_dir = '\0';
DEBUG_FLOW("iSCSI: rmdir %s\n", session->target_link_dir);
rmdir(session->target_link_dir);
/* if the bus dir is empty now, get rid of it too, but don't corrupt the session's target dir */
while (*bus_dir != '/')
bus_dir--;
bus_dir++; /* leave the slash */
c = *bus_dir;
*bus_dir = '\0';
DEBUG_FLOW("iSCSI: rmdir %s\n", session->target_link_dir);
rmdir(session->target_link_dir);
*bus_dir = c;
}
set_fs(oldfs);
up(&iscsi_lun_probe_mutex);
}
/* find all dir prefixes of pathname, and make them all if they don't exist */
static void
ensure_directories_exist(char *pathname, mode_t dir_mode)
{
char *end = pathname;
/* skip leading slashes */
while (end && *end && (*end == '/'))
end++;
while (end && (*end != '\0')) {
/* if there is another slash, make the dir.
* FIXME: we ought to ignore errors when the directory exists,
* but report errors where the directory doesn't exist and
* we failed to create it.
*/
while ((*end != '/') && (*end != '\0'))
end++;
if (*end == '/') {
*end = '\0';
mkdir(pathname, dir_mode);
*end = '/';
end++;
}
}
}
static int
get_device_scsi_quad(char *device_name, int *host, int *channel, int *target,
int *lun)
{
int ret = 0;
u_long info[2];
struct file *filp = NULL;
struct inode *inode = NULL;
filp = filp_open(device_name, O_RDONLY | O_NONBLOCK, 0);
if (IS_ERR(filp)) {
return 0;
}
memset(info, 0, sizeof (info));
inode = filp->f_dentry->d_inode;
if (filp->f_op->
ioctl(inode, filp, SCSI_IOCTL_GET_IDLUN,
(unsigned long) info) == 0) {
if (target)
*target = info[0] & 0xff;
if (lun)
*lun = (info[0] >> 8) & 0xff;
if (channel)
*channel = (info[0] >> 16) & 0xff;
/* 2.4 kernels give us all the info we need with that ioctl. */
if (host)
*host = ((info[0] >> 24) & 0xff);
ret = 1;
}
filp_close(filp, 0);
return ret;
}
static void
iscsi_update_disk_links(iscsi_session_t * session, int max_sd_devices,
int max_sd_partitions, mode_t dir_mode)
{
int i;
char devname[20];
/* we've reserved enough space in session->target_link_dir so that we can use it to build pathnames */
char *lun_dir =
session->target_link_dir + strlen(session->target_link_dir);
/* FIXME: can we get the number of devices supported from the running kernel? */
for (i = 0; i < max_sd_devices; i++) {
int host = -1, channel = -1, id = -1, lun = -1;
if (i < 26) {
sprintf(devname, "/dev/sd%c", 'a' + i);
} else {
/* double char names for disknum 26+ */
sprintf(devname, "/dev/sd%c%c", 'a' + (i / 26) - 1,
'a' + (i % 26));
}
if (get_device_scsi_quad(devname, &host, &channel, &id, &lun)) {
if ((host == session->host_no)
&& (channel == session->channel)
&& (id == session->target_id)) {
char *partition = devname + strlen(devname);
char *link;
int p;
DEBUG_INIT
("iSCSI: disk device node %s = bus %d target %d LUN %d\n",
devname, session->iscsi_bus, id, lun);
/* ensure the LUN dir exists */
sprintf(lun_dir, "lun%d/", lun);
ensure_directories_exist(session->
target_link_dir,
dir_mode);
link = lun_dir + strlen(lun_dir);
/* symlink the whole-disk device */
strcpy(link, "disk");
unlink(session->target_link_dir); /* remove any existing symlink */
symlink(devname, session->target_link_dir); /* make a new symlink */
/* and make links for each possible disk partition as well,
* since we don't want to have to track what partitions get added or removed.
* This works just like the normal partition device nodes, which
* are always present, but may or may not be openable.
*/
for (p = 1; p <= max_sd_partitions; p++) {
sprintf(partition, "%d", p);
sprintf(link, "part%d", p);
unlink(session->target_link_dir);
symlink(devname,
session->target_link_dir);
}
}
}
}
/* restore the session's target dir */
*lun_dir = '\0';
}
static void
iscsi_update_tape_links(iscsi_session_t * session, int max_st_devices,
mode_t dir_mode)
{
int i;
char devname[20];
/* we've reserved enough space in session->target_link_dir so that we can use it to build pathnames */
char *lun_dir =
session->target_link_dir + strlen(session->target_link_dir);
/* FIXME: can we get the number of devices supported from the running kernel? */
for (i = 0; i < max_st_devices; i++) {
int host = -1, channel = -1, id = -1, lun = -1;
/* we check the no-rewind device to avoid having side-effects */
sprintf(devname, "/dev/nst%d", i);
if (get_device_scsi_quad(devname, &host, &channel, &id, &lun)) {
if ((host == session->host_no)
&& (channel == session->channel)
&& (id == session->target_id)) {
char *link;
DEBUG_INIT
("iSCSI: tape device node %s = bus %d target %d LUN %d\n",
devname, session->iscsi_bus, id, lun);
/* ensure the LUN dir exists */
sprintf(lun_dir, "lun%d/", lun);
ensure_directories_exist(session->
target_link_dir,
dir_mode);
link = lun_dir + strlen(lun_dir);
/* auto-rewind nodes */
strcpy(link, "mt");
unlink(session->target_link_dir); /* remove any existing symlink */
sprintf(devname, "/dev/st%d", i);
symlink(devname, session->target_link_dir); /* make a new symlink */
strcpy(link, "mtl");
unlink(session->target_link_dir); /* remove any existing symlink */
sprintf(devname, "/dev/st%dl", i);
symlink(devname, session->target_link_dir); /* make a new symlink */
strcpy(link, "mtm");
unlink(session->target_link_dir); /* remove any existing symlink */
sprintf(devname, "/dev/st%dm", i);
symlink(devname, session->target_link_dir); /* make a new symlink */
strcpy(link, "mta");
unlink(session->target_link_dir); /* remove any existing symlink */
sprintf(devname, "/dev/st%da", i);
symlink(devname, session->target_link_dir); /* make a new symlink */
/* no rewind nodes */
strcpy(link, "mtn");
unlink(session->target_link_dir); /* remove any existing symlink */
sprintf(devname, "/dev/nst%d", i);
symlink(devname, session->target_link_dir); /* make a new symlink */
strcpy(link, "mtln");
unlink(session->target_link_dir); /* remove any existing symlink */
sprintf(devname, "/dev/nst%dl", i);
symlink(devname, session->target_link_dir); /* make a new symlink */
strcpy(link, "mtmn");
unlink(session->target_link_dir); /* remove any existing symlink */
sprintf(devname, "/dev/nst%dm", i);
symlink(devname, session->target_link_dir); /* make a new symlink */
strcpy(link, "mtan");
unlink(session->target_link_dir); /* remove any existing symlink */
sprintf(devname, "/dev/nst%da", i);
symlink(devname, session->target_link_dir); /* make a new symlink */
}
}
}
/* restore the session's target dir */
*lun_dir = '\0';
}
static void
iscsi_update_generic_links(iscsi_session_t * session, int max_sg_devices,
mode_t dir_mode)
{
int i;
char devname[20];
/* we've reserved enough space in session->target_link_dir so that we can use it to build pathnames */
char *lun_dir =
session->target_link_dir + strlen(session->target_link_dir);
char *link;
/* FIXME: can we get the number of devices supported from the running kernel? */
for (i = 0; i < max_sg_devices; i++) {
int host = -1, channel = -1, id = -1, lun = -1;
sprintf(devname, "/dev/sg%d", i);
if (get_device_scsi_quad(devname, &host, &channel, &id, &lun)) {
if ((host == session->host_no)
&& (channel == session->channel)
&& (id == session->target_id)) {
DEBUG_INIT
("iSCSI: generic device node %s = bus %d target %d LUN %d\n",
devname, session->iscsi_bus, id, lun);
/* ensure the LUN dir exists */
sprintf(lun_dir, "lun%d/", lun);
ensure_directories_exist(session->
target_link_dir,
dir_mode);
link = lun_dir + strlen(lun_dir);
strcpy(link, "generic");
unlink(session->target_link_dir); /* remove any existing symlink */
symlink(devname, session->target_link_dir); /* make a new symlink */
}
}
}
/* restore the session's target dir */
*lun_dir = '\0';
}
static void
iscsi_update_cd_links(iscsi_session_t * session, int max_sr_devices,
mode_t dir_mode)
{
int i;
char devname[20];
/* we've reserved enough space in session->target_link_dir so that we can use it to build pathnames */
char *lun_dir =
session->target_link_dir + strlen(session->target_link_dir);
char *link;
/* FIXME: can we get the number of devices supported from the running kernel? */
for (i = 0; i < max_sr_devices; i++) {
int host = -1, channel = -1, id = -1, lun = -1;
/* FIXME: the distribution may be using /dev/sr instead of /dev/scd */
sprintf(devname, "/dev/scd%d", i);
if (get_device_scsi_quad(devname, &host, &channel, &id, &lun)) {
if ((host == session->host_no)
&& (channel == session->channel)
&& (id == session->target_id)) {
DEBUG_INIT
("iSCSI: cdrom device node %s = bus %d target %d LUN %d\n",
devname, session->iscsi_bus, id, lun);
/* ensure the LUN dir exists */
sprintf(lun_dir, "lun%d/", lun);
ensure_directories_exist(session->
target_link_dir,
dir_mode);
link = lun_dir + strlen(lun_dir);
strcpy(link, "cd");
unlink(session->target_link_dir); /* remove any existing symlink */
symlink(devname, session->target_link_dir); /* make a new symlink */
}
}
}
/* restore the session's target dir */
*lun_dir = '\0';
}
/* compute the intersection of the LUNS detected and configured, and probe each LUN */
void
iscsi_probe_luns(iscsi_session_t * session, uint32_t * lun_bitmap,
scsi_device_info_t * device_info)
{
int l;
int detected = 0;
int probed = 0;
int activated = 0;
/* try wait for our turn to probe, to keep the device node ordering as repeatable as possible */
DEBUG_INIT
("iSCSI: session (bus %d target %d) to %s waiting to probe LUNs at %lu, probe order %d\n",
session->iscsi_bus, session->target_id, session->log_name, jiffies,
session->probe_order);
if (!wait_for_probe_order(session)) {
DEBUG_INIT
("iSCSI: session (bus %d target %d) to %s couldn't probe LUNs, error waiting for probe order\n",
session->iscsi_bus, session->target_id, session->log_name);
return;
}
if (test_bit(SESSION_TERMINATING, &session->control_bits)) {
printk
("iSCSI: session (bus %d target %d)to %s terminated while waiting to probe LUNs\n",
session->iscsi_bus, session->target_id, session->log_name);
goto done;
}
if (signal_pending(current)) {
printk
("iSCSI: session (bus %d target %d) ioctl killed while waiting to probe LUNs\n",
session->iscsi_bus, session->target_id);
goto done;
}
/* make sure we're the only driver process trying to add or remove LUNs */
if (down_interruptible(&iscsi_lun_probe_mutex)) {
printk
("iSCSI: session (bus %d target %d) to %s interrupted while probing LUNs\n",
session->iscsi_bus, session->target_id, session->log_name);
goto done;
}
/* need to set the host's max_channel, max_id, max_lun, since we
* zero them in iscsi_detect in order to disable the scan that
* occurs during scsi_register_host.
*/
session->hba->host->max_id = ISCSI_MAX_TARGET_IDS_PER_BUS;
session->hba->host->max_lun = ISCSI_MAX_LUNS_PER_TARGET;
session->hba->host->max_channel = ISCSI_MAX_CHANNELS_PER_HBA - 1; /* convert from count to index */
smp_mb();
DEBUG_INIT
("iSCSI: probing LUNs for session (bus %d target %d) to %s at %lu, probe_order %d at %lu\n",
session->iscsi_bus, session->target_id, session->log_name, jiffies,
session->probe_order, jiffies);
for (l = 0; l < ISCSI_MAX_LUN; l++) {
if (test_bit(SESSION_TERMINATING, &session->control_bits))
goto give_up;
if (signal_pending(current))
goto give_up;
if (test_bit(l, session->luns_detected)) {
/* Check if lun has been removed */
if (!test_bit(l, session->luns_found)) {
if (iscsi_remove_lun(session, l) != 0) {
char buffer[sizeof (struct dirent) + 1],
c;
mm_segment_t oldfs;
int size = sizeof (buffer) - 1;
char *lun_dir =
session->target_link_dir +
strlen(session->target_link_dir);
char *bus_dir = lun_dir - 2; /* before the slash */
oldfs = get_fs();
set_fs(get_ds());
if (session->target_link_dir[0] == '/') {
sprintf(lun_dir, "lun%d/", l);
empty_directory(session->
target_link_dir,
buffer, size);
rmdir(session->target_link_dir);
}
/* If all luns on this target have been deleted.
* remove the target entry.
*
*/
if (session->target_link_dir[0] == '/') {
/* and get rid of the target dir itself */
*lun_dir = '\0';
DEBUG_FLOW("iSCSI: rmdir %s\n",
session->
target_link_dir);
rmdir(session->target_link_dir);
/* if the bus dir is empty now, get rid of it too, but don't corrupt the session's target dir */
while (*bus_dir != '/')
bus_dir--;
bus_dir++;
c = *bus_dir;
*bus_dir = '\0';
DEBUG_FLOW("iSCSI: rmdir %s\n",
session->
target_link_dir);
rmdir(session->target_link_dir);
*bus_dir = c;
}
set_fs(oldfs);
}
} else {
detected++;
/* if allowed and not already activated (successfully probed), probe it */
if ((lun_bitmap[l / 32] & (1 << (l % 32)))
&& !test_bit(l, session->luns_activated)) {
DEBUG_FLOW
("iSCSI: session (bus %d target %d) probing LUN %d at %lu\n",
session->iscsi_bus,
session->target_id, l, jiffies);
iscsi_probe_lun(session, l);
probed++;
if (test_bit
(l, session->luns_activated))
activated++;
}
}
} else {
if (test_bit(l, session->luns_activated)) {
if (iscsi_remove_lun(session, l) != 0) {
char buffer[sizeof (struct dirent) + 1],
c;
mm_segment_t oldfs;
int size = sizeof (buffer) - 1;
char *lun_dir =
session->target_link_dir +
strlen(session->target_link_dir);
char *bus_dir = lun_dir - 2; /* before the slash */
oldfs = get_fs();
set_fs(get_ds());
if (session->target_link_dir[0] == '/') {
sprintf(lun_dir, "lun%d/", l);
empty_directory(session->
target_link_dir,
buffer, size);
rmdir(session->target_link_dir);
}
/* If all luns on this target have been deleted.
* remove the target entry.
*
*/
if (session->target_link_dir[0] == '/') {
/* and get rid of the target dir itself */
*lun_dir = '\0';
DEBUG_FLOW("iSCSI: rmdir %s\n",
session->
target_link_dir);
rmdir(session->target_link_dir);
/* if the bus dir is empty now, get rid of it too, but don't corrupt the session's target dir */
while (*bus_dir != '/')
bus_dir--;
bus_dir++;
c = *bus_dir;
*bus_dir = '\0';
DEBUG_FLOW("iSCSI: rmdir %s\n",
session->
target_link_dir);
rmdir(session->target_link_dir);
*bus_dir = c;
}
set_fs(oldfs);
}
}
}
}
if (detected == 0) {
printk
("iSCSI: no LUNs detected for session (bus %d target %d) to %s\n",
session->iscsi_bus, session->target_id, session->log_name);
} else if (LOG_ENABLED(ISCSI_LOG_INIT)) {
printk
("iSCSI: session (bus %d target %d) to %s probed %d of %d LUNs detected, %d new LUNs activated\n",
session->iscsi_bus, session->target_id, session->log_name,
probed, detected, activated);
}
/* optionally set up a symlink tree. We do this in the kernel so that we
* can guard it with the lun_probe_mutex. The high-level SCSI drivers in Linux tend
* to crash if a device node is opened while the Scsi_Device is still being
* initialized, so we want to make sure we're not doing any probes when we open
* lots of device nodes.
*/
if (session->target_link_dir[0] == '/') {
mm_segment_t oldfs = get_fs();
set_fs(get_ds());
/* make the target dir, so that the user can always see the target has a session, even if
* LUN probing fails to find anything or no target drivers have attached.
*/
ensure_directories_exist(session->target_link_dir,
session->dir_mode);
if (device_info->max_sd_devices > 0) {
DEBUG_INIT
("iSCSI: session (bus %d target %d) updating disk links under %s\n",
session->iscsi_bus, session->target_id,
session->target_link_dir);
iscsi_update_disk_links(session,
device_info->max_sd_devices,
device_info->max_sd_partitions,
session->dir_mode);
}
if (device_info->max_sg_devices > 0) {
DEBUG_INIT
("iSCSI: session (bus %d target %d) updating generic links under %s\n",
session->iscsi_bus, session->target_id,
session->target_link_dir);
iscsi_update_generic_links(session,
device_info->max_sg_devices,
session->dir_mode);
}
if (device_info->max_st_devices > 0) {
DEBUG_INIT
("iSCSI: session (bus %d target %d) updating tape links under %s\n",
session->iscsi_bus, session->target_id,
session->target_link_dir);
iscsi_update_tape_links(session,
device_info->max_st_devices,
session->dir_mode);
}
if (device_info->max_sr_devices > 0) {
DEBUG_INIT
("iSCSI: session (bus %d target %d) updating cdrom links under %s\n",
session->iscsi_bus, session->target_id,
session->target_link_dir);
iscsi_update_cd_links(session,
device_info->max_sr_devices,
session->dir_mode);
}
set_fs(oldfs);
}
give_up:
up(&iscsi_lun_probe_mutex);
done:
/* clean up after wait_for_probe_order, and possibly start the next session probing */
iscsi_probe_finished(session);
}
typedef struct iscsi_cmnd {
struct semaphore done_sem;
Scsi_Cmnd sc;
unsigned int bufflen;
uint8_t buffer[1];
} iscsi_cmnd_t;
/* callback function for Scsi_Cmnd's generated by the iSCSI driver itself */
void
iscsi_done(Scsi_Cmnd * sc)
{
iscsi_cmnd_t *c = (iscsi_cmnd_t *) sc->buffer;
up(&c->done_sem);
}
static int
iscsi_do_cmnd(iscsi_session_t * session, iscsi_cmnd_t * c,
unsigned int attempts_allowed)
{
Scsi_Cmnd *sc = NULL;
int queue_attempts = 0;
if (c->sc.host) {
DEBUG_FLOW
("iSCSI: iscsi_do_cmnd %p to (%u %u %u %u), Cmd 0x%02x, %u retries, buffer %p, bufflen %u\n",
c, c->sc.host->host_no, c->sc.channel,
c->sc.target, c->sc.lun, c->sc.cmnd[0], attempts_allowed,
c->sc.request_buffer, c->sc.request_bufflen);
} else {
printk
("iSCSI: session (bus %d target %d) iscsi_do_cmnd %p, buffer %p, bufflen %u, host %p\n",
session->iscsi_bus, session->target_id, c,
c->sc.request_buffer, c->sc.request_bufflen, c->sc.host);
return 0;
}
if (!c->sc.request_buffer)
return 0;
if (!c->sc.request_bufflen)
return 0;
sc = &(c->sc);
sc->retries = -1;
sc->allowed = attempts_allowed;
retry:
while (++sc->retries < sc->allowed) {
if (signal_pending(current))
return 0;
if (test_bit(SESSION_TERMINATING, &session->control_bits))
return 0;
sc->result = 0;
memset(sc->sense_buffer, 0, sizeof (sc->sense_buffer));
memset(c->buffer, 0, c->bufflen);
/* try to queue the command */
queue_attempts = 0;
for (;;) {
sema_init(&c->done_sem, 0);
smp_mb();
if (signal_pending(current))
return 0;
if (test_bit
(SESSION_TERMINATING, &session->control_bits))
return 0;
DEBUG_INIT
("iSCSI: detect_luns queueing %p to session (bus %d target %d) at %lu\n",
sc, session->iscsi_bus, session->target_id,
jiffies);
/* give up eventually, in case the replacement timeout is in effect.
* we don't want to loop forever trying to queue to a session
* that may never accept commands.
*/
if (iscsi_queue(session, sc, iscsi_done)) {
break;
} else if (queue_attempts++ >= 500) {
/* give up after 10 seconds */
return 0;
}
/* command not queued, wait a bit and try again */
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(MSECS_TO_JIFFIES(20));
}
DEBUG_QUEUE
("iSCSI: session (bus %d target %d) queued iscsi_cmnd %p, buffer %p, bufflen %u, scsi_done %p\n",
session->iscsi_bus, session->target_id, c,
c->sc.request_buffer, c->sc.request_bufflen,
c->sc.scsi_done);
/* wait til either the command completes, or we get signalled. */
if (down_interruptible(&c->done_sem)) {
/* if we got signalled, squash the command and give up */
iscsi_squash_cmnd(session, sc);
return 0;
}
DEBUG_QUEUE
("iSCSI: session (bus %d target %d) hba %p host %p woken up by iscsi_cmnd %p, buffer %p, bufflen %u\n",
session->iscsi_bus, session->target_id, session->hba,
session->hba->host, c, c->sc.request_buffer,
c->sc.request_bufflen);
/* the command completed, check the result and decide if it needs to be retried. */
DEBUG_FLOW
("iSCSI: session (bus %d target %d) iscsi cmnd %p to (%u %u %u %u), Cmd 0x%02x, "
"host byte 0x%x, SCSI status 0x%x, residual %u\n",
session->iscsi_bus, session->target_id c,
c->sc.host->host_no, c->sc.channel, c->sc.target,
c->sc.lun, c->sc.cmnd[0], (sc->result >> 24) & 0xFF,
sc->result & 0xFF, sc->resid);
/* check the host byte */
switch (host_byte(sc->result)) {
case DID_OK:
/* no problems so far */
break;
case DID_NO_CONNECT:
/* give up, we can't talk to the device */
printk
("iSCSI: failing iscsi cmnd %p to (%u %u %u %u), Cmd 0x%02x, "
"host byte 0x%x, SCSI status 0x%x, residual %u\n",
c, c->sc.host->host_no, c->sc.channel,
c->sc.target, c->sc.lun, c->sc.cmnd[0],
(sc->result >> 24) & 0xFF, sc->result & 0xFF,
sc->resid);
return 0;
case DID_ERROR:
case DID_SOFT_ERROR:
case DID_ABORT:
case DID_BUS_BUSY:
case DID_PARITY:
case DID_TIME_OUT:
case DID_RESET:
default:
if (LOG_ENABLED(ISCSI_LOG_INIT))
printk
("iSCSI: iscsi cmnd %p to (%u %u %u %u), Cmd 0x%02x, "
"host byte 0x%x, SCSI status 0x%x, residual %u\n",
c, c->sc.host->host_no,
c->sc.channel, c->sc.target, c->sc.lun,
c->sc.cmnd[0], (sc->result >> 24) & 0xFF,
sc->result & 0xFF, sc->resid);
/* some sort of problem, possibly retry */
goto retry;
}
/* check the SCSI status byte. Note, Linux values are right-shifted once compared to the SCSI spec */
switch (status_byte(sc->result)) {
case GOOD:
case COMMAND_TERMINATED:
/* make sure we got enough of a response */
if (sc->resid
&& ((iscsi_expected_data_length(sc) - sc->resid) <
sc->underflow)) {
/* try again */
if (LOG_ENABLED(ISCSI_LOG_INIT))
printk
("iSCSI: iscsi cmnd %p to (%u %u %u %u), Cmd 0x%02x, "
"residual %u, retrying to get %u bytes desired\n",
c, c->sc.host->host_no,
c->sc.channel, c->sc.target,
c->sc.lun, c->sc.cmnd[0],
sc->resid, sc->underflow);
goto retry;
}
/* all done */
return 1;
case BUSY: /* device is busy, try again later */
case QUEUE_FULL: /* tagged queuing device has a full queue, wait a bit and try again. */
sc->allowed++;
if (sc->allowed > 100) {
printk
("iSCSI: iscsi cmnd %p to (%u %u %u %u), Cmd 0x%02x, SCSI status 0x%x, out of retries\n",
c, c->sc.host->host_no,
c->sc.channel, c->sc.target, c->sc.lun,
c->sc.cmnd[0], sc->result & 0xFF);
return 0;
}
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(MSECS_TO_JIFFIES(20));
goto retry;
case CONDITION_GOOD:
case INTERMEDIATE_GOOD:
case INTERMEDIATE_C_GOOD:
/* we should never get the linked command return codes */
case RESERVATION_CONFLICT:
/* this is probably never going to happen for INQUIRY or REPORT_LUNS, but retry if it does */
printk
("iSCSI: session (bus %d target %d) iscsi_do_cmnd %p SCSI status 0x%x at %lu, retrying\n",
session->iscsi_bus, session->target_id, c,
sc->result & 0xFF, jiffies);
goto retry;
case CHECK_CONDITION:
/* look at the sense. If it's illegal request, don't bother retrying the command */
if ((sc->sense_buffer[0] & 0x70) == 0x70) {
switch (SENSE_KEY(sc->sense_buffer)) {
case ILLEGAL_REQUEST:
printk
("iSCSI: iscsi cmnd %p to (%u %u %u %u), Cmd 0x%02x, illegal request\n",
c, c->sc.host->host_no,
c->sc.channel, c->sc.target,
c->sc.lun, c->sc.cmnd[0]);
return 0;
default:
/* possibly retry */
if (LOG_ENABLED(ISCSI_LOG_INIT))
printk
("iSCSI: iscsi cmnd %p to (%u %u %u %u), Cmd 0x%02x with sense, retrying\n",
c, c->sc.host->host_no,
c->sc.channel,
c->sc.target, c->sc.lun,
c->sc.cmnd[0]);
goto retry;
}
}
goto retry;
default:
printk
("iSCSI: session (bus %d target %d) iscsi_do_cmnd %p unexpected SCSI status 0x%x at %lu\n",
session->iscsi_bus, session->target_id, c,
sc->result & 0xFF, jiffies);
return 0;
}
}
if (LOG_ENABLED(ISCSI_LOG_INIT))
printk
("iSCSI: session (bus %d target %d) iscsi_do_cmnd %p SCSI status 0x%x, out of retries at %lu\n",
session->iscsi_bus, session->target_id, c,
sc->result & 0xFF, jiffies);
return 0;
}
void
send_tur(iscsi_session_t * session)
{
iscsi_cmnd_t *c = NULL;
Scsi_Cmnd *sc = NULL;
size_t cmd_size = sizeof (iscsi_cmnd_t);
unsigned int bufflen = 255;
cmd_size += bufflen;
c = kmalloc(cmd_size, GFP_KERNEL);
if (!c) {
printk
("iSCSI: session (bus %d target %d) send_tur couldn't allocate a Scsi_Cmnd\n",
session->iscsi_bus, session->target_id);
return;
}
/* initialize */
memset(c, 0, cmd_size);
sema_init(&c->done_sem, 0);
c->bufflen = bufflen;
DEBUG_ALLOC
("iSCSI: session (bus %d target %d) hba %p host %p allocated iscsi cmnd %p, size %d, buffer %p, bufflen %u, end %p\n",
session->iscsi_bus, session->target_id, session->hba,
session->hba->host, c, cmd_size, c->buffer, c->bufflen,
c->buffer + c->bufflen);
/* fill in the basic required info in the Scsi_Cmnd */
sc = &(c->sc);
sc->host = session->hba->host;
sc->channel = session->channel;
sc->target = session->target_id;
sc->lun = 0;
sc->use_sg = 0;
sc->request_buffer = c->buffer;
sc->request_bufflen = c->bufflen;
sc->scsi_done = iscsi_done;
sc->timeout_per_command = 30 * HZ;
sc->resid = 0;
sc->underflow = 8;
init_timer(&sc->eh_timeout);
/* save a pointer to the iscsi_cmnd in the Scsi_Cmnd, so that iscsi_done can
use it */
sc->buffer = (void *) c;
{
if (signal_pending(current)) {
DEBUG_INIT
("iSCSI: session (bus %d target %d) send_tur aborted by signal\n",
session->iscsi_bus, session->target_id);
goto done;
}
if (test_bit(SESSION_TERMINATING, &session->control_bits))
goto done;
sc->cmd_len = 6;
memset(sc->cmnd, 0, sizeof (sc->cmnd));
sc->cmnd[0] = TEST_UNIT_READY;
sc->cmnd[1] = 0;
sc->cmnd[2] = 0;
sc->cmnd[3] = 0;
sc->cmnd[4] = 0;
sc->cmnd[5] = 0;
smp_mb();
if (iscsi_do_cmnd(session, c, 6)) {
} else {
printk
("iSCSI: session (bus %d target %d) Received a sense for a TEST UNIT READY\n",
session->iscsi_bus, session->target_id);
}
}
done:
kfree(c);
}
void
reinitialize_disk(iscsi_session_t * session)
{
iscsi_cmnd_t *c = NULL;
Scsi_Cmnd *sc = NULL;
size_t cmd_size = sizeof (iscsi_cmnd_t);
unsigned int bufflen = 255;
cmd_size += bufflen;
c = kmalloc(cmd_size, GFP_KERNEL);
if (!c) {
printk
("iSCSI: session (bus %d target %d) reinitialize_disk couldn't allocate a Scsi_Cmnd\n",
session->iscsi_bus, session->target_id);
return;
}
/* initialize */
memset(c, 0, cmd_size);
sema_init(&c->done_sem, 0);
c->bufflen = bufflen;
DEBUG_ALLOC
("iSCSI: session (bus %d target %d) hba %p host %p allocated iscsi cmnd %p, size %d, buffer %p, bufflen %u, end %p\n",
session->iscsi_bus, session->target_id, session->hba,
session->hba->host, c, cmd_size, c->buffer, c->bufflen,
c->buffer + c->bufflen);
/* fill in the basic required info in the Scsi_Cmnd */
sc = &(c->sc);
sc->host = session->hba->host;
sc->channel = session->channel;
sc->target = session->target_id;
sc->lun = 0;
sc->use_sg = 0;
sc->request_buffer = c->buffer;
sc->request_bufflen = c->bufflen;
sc->scsi_done = iscsi_done;
sc->timeout_per_command = 30 * HZ;
sc->resid = 0;
sc->underflow = 8;
init_timer(&sc->eh_timeout);
/* save a pointer to the iscsi_cmnd in the Scsi_Cmnd, so that iscsi_done can
use it */
sc->buffer = (void *) c;
{
if (signal_pending(current)) {
DEBUG_INIT
("iSCSI: session (bus %d target %d) reinitialize_disk aborted by signal\n",
session->iscsi_bus, session->target_id);
goto done;
}
if (test_bit(SESSION_TERMINATING, &session->control_bits))
goto done;
sc->cmd_len = 6;
memset(sc->cmnd, 0, sizeof (sc->cmnd));
sc->cmnd[0] = START_STOP;
sc->cmnd[1] = 0;
sc->cmnd[1] |= 1;
sc->cmnd[2] = 0;
sc->cmnd[3] = 0;
sc->cmnd[4] = 1;
sc->cmnd[5] = 0;
smp_mb();
if (iscsi_do_cmnd(session, c, 6)) {
} else {
printk
("\niSCSI:session (bus %d target %d) Received a sense for a START STOP\n",
session->iscsi_bus, session->target_id);
}
}
done:
kfree(c);
}
static void
make_report_luns(Scsi_Cmnd * sc, uint32_t max_entries)
{
uint32_t length = 8 + (max_entries * 8); /* 8 byte header plus 8 bytes per LUN */
sc->cmd_len = 10;
sc->request_bufflen = length;
sc->underflow = 8; /* need at least the length */
sc->resid = 0;
/* CDB */
memset(sc->cmnd, 0, sizeof (sc->cmnd));
sc->cmnd[0] = REPORT_LUNS;
sc->cmnd[1] = 0;
sc->cmnd[2] = 0; /* either reserved or select report in various versions of SCSI-3 */
sc->cmnd[3] = 0;
sc->cmnd[4] = 0;
sc->cmnd[5] = 0;
sc->cmnd[6] = (length >> 24) & 0xFF;
sc->cmnd[7] = (length >> 16) & 0xFF;
sc->cmnd[8] = (length >> 8) & 0xFF;
sc->cmnd[9] = (length) & 0xFF;
}
static void
make_inquiry(Scsi_Cmnd * sc, int lun0_scsi_level)
{
sc->cmd_len = 6;
sc->request_bufflen = 255;
if (sc->lun == 0)
sc->underflow = 3; /* we need at least the peripheral code and SCSI version */
else
sc->underflow = 1; /* we need at least the peripheral code */
sc->resid = 0;
memset(sc->cmnd, 0, sizeof (sc->cmnd));
sc->cmnd[0] = INQUIRY;
if (lun0_scsi_level >= 0x3)
sc->cmnd[1] = 0; /* reserved in SCSI-3 and higher */
else
sc->cmnd[1] = (sc->lun << 5) & 0xe0;
sc->cmnd[2] = 0;
sc->cmnd[3] = 0;
sc->cmnd[4] = 255; /* length */
sc->cmnd[5] = 0;
}
/* scan for LUNs */
void
iscsi_detect_luns(iscsi_session_t * session)
{
int l;
iscsi_cmnd_t *c = NULL;
Scsi_Cmnd *sc = NULL;
int lun0_scsi_level = 0;
size_t cmd_size = sizeof (iscsi_cmnd_t);
unsigned int bufflen = 0;
uint32_t last_luns = 0;
uint32_t luns = 32; /* start small to avoid bugs in REPORT_LUNS handling */
int report_luns_failed = 0;
memset(session->luns_found, 0, sizeof (session->luns_found));
/* need enough buffer space for replies to INQUIRY and REPORT_LUNS */
if ((8 + (ISCSI_MAX_LUN * 8)) < 255)
bufflen = 255;
else
bufflen = (ISCSI_MAX_LUN * 8) + 8;
cmd_size += bufflen;
c = kmalloc(cmd_size, GFP_KERNEL);
if (!c) {
printk
("iSCSI: session (bus %d target %d) iscsi_detect_luns couldn't allocate a Scsi_Cmnd\n",
session->iscsi_bus, session->target_id);
return;
}
/* initialize */
memset(c, 0, cmd_size);
sema_init(&c->done_sem, 0);
c->bufflen = bufflen;
DEBUG_ALLOC
("iSCSI: session (bus %d target %d) hba %p host %p allocated iscsi cmnd %p, size %d, buffer %p, bufflen %u, end %p\n",
session->iscsi_bus, session->target_id, session->hba,
session->hba->host, c, cmd_size, c->buffer, c->bufflen,
c->buffer + c->bufflen);
/* fill in the basic required info in the Scsi_Cmnd */
sc = &(c->sc);
sc->host = session->hba->host;
sc->channel = session->channel;
sc->target = session->target_id;
sc->lun = 0;
sc->use_sg = 0;
sc->request_buffer = c->buffer;
sc->request_bufflen = c->bufflen;
sc->scsi_done = iscsi_done;
sc->timeout_per_command = 30 * HZ;
init_timer(&sc->eh_timeout);
/* save a pointer to the iscsi_cmnd in the Scsi_Cmnd, so that iscsi_done can use it */
sc->buffer = (void *) c;
do {
if (signal_pending(current)) {
DEBUG_INIT
("iSCSI: session (bus %d target %d) detect LUNs aborted by signal\n",
session->iscsi_bus, session->target_id);
goto done;
}
if (test_bit(SESSION_TERMINATING, &session->control_bits))
goto done;
/* send a REPORT_LUNS to LUN 0. If it works, we know the LUNs. */
last_luns = luns;
make_report_luns(sc, luns);
smp_mb();
if (iscsi_do_cmnd(session, c, 6)) {
uint8_t *lun_list = c->buffer + 8;
int luns_listed;
uint32_t length = 0;
/* get the list length the target has */
length = c->buffer[0] << 24;
length |= c->buffer[1] << 16;
length |= c->buffer[2] << 8;
length |= c->buffer[3];
if (length < 8) {
/* odd, assume REPORT_LUNS is broken, fall back to doing INQUIRY */
DEBUG_INIT
("iSCSI: session (bus %d target %d) REPORT_LUNS length 0, falling back to INQUIRY\n",
session->iscsi_bus, session->target_id);
report_luns_failed = 1;
break;
}
/* figure out how many luns we were told about this time */
if ((length / 8U) < luns)
luns_listed = length / 8U;
else
luns_listed = luns;
/* loop until we run out of data, or out of buffer */
for (l = 0; l < luns_listed; l++) {
int address_method = (lun_list[0] & 0xc0) >> 6;
int lun;
if (LOG_ENABLED(ISCSI_LOG_LOGIN)
|| LOG_ENABLED(ISCSI_LOG_INIT))
printk
("iSCSI: session (%u %u %u *) REPORT_LUNS[%d] = %02x %02x %02x %02x %02x %02x %02x %02x\n",
session->host_no,
session->channel,
session->target_id, l, lun_list[0],
lun_list[1], lun_list[2],
lun_list[3], lun_list[4],
lun_list[5], lun_list[6],
lun_list[7]);
switch (address_method) {
case 0x0:{
/* single-level LUN if bus id is 0, else peripheral device addressing */
lun = lun_list[1];
set_bit(lun,
session->luns_detected);
/* This is useful while checking for deleted luns */
set_bit(lun,
session->luns_found);
break;
}
case 0x1:{
/* flat-space addressing */
lun = lun_list[1];
set_bit(lun,
session->luns_detected);
/* This is useful while checking for deleted luns */
set_bit(lun,
session->luns_found);
break;
}
case 0x2:{
/* logical unit addressing method */
lun = lun_list[1] & 0x1F;
set_bit(lun,
session->luns_detected);
/* This is useful while checking for deleted luns */
set_bit(lun,
session->luns_found);
break;
}
case 0x3:{
/* extended logical unit addressing method is too complicated for us to want to deal with */
printk
("iSCSI: session (%u %u %u *) REPORT_LUNS[%d] with extended LU address method 0x%x ignored\n",
session->host_no,
session->channel,
session->target_id, l,
address_method);
break;
}
default:
printk
("iSCSI: session (%u %u %u *) REPORT_LUNS[%d] with unknown address method 0x%x ignored\n",
session->host_no,
session->channel,
session->target_id, l,
address_method);
break;
}
/* next LUN in the list */
lun_list += 8;
}
/* decide how many luns to ask for on the next iteration, if there is one */
luns = length / 8U;
if (luns > ISCSI_MAX_LUN) {
/* we only have buffer space for so many LUNs */
luns = ISCSI_MAX_LUN;
printk
("iSCSI: session (bus %d target %d) REPORT_LUNS length %u (%u entries) truncated to %u (%u entries)\n",
session->iscsi_bus, session->target_id,
length, (length / 8) - 1, (luns + 1) * 8U,
luns);
}
} else {
/* REPORT_LUNS failed, fall back to doing INQUIRY */
DEBUG_INIT
("iSCSI: session (bus %d target %d) REPORT_LUNS failed, falling back to INQUIRY\n",
session->iscsi_bus, session->target_id);
report_luns_failed = 1;
break;
}
} while (luns > last_luns);
if (signal_pending(current)) {
DEBUG_INIT
("iSCSI: session (bus %d target %d) detect LUNs aborted by signal\n",
session->iscsi_bus, session->target_id);
goto done;
}
if (report_luns_failed) {
/* if REPORT_LUNS failed, then either it's a SCSI-2 device
* that doesn't understand the command, or it's a SCSI-3
* device that only has one LUN and decided not to implement
* REPORT_LUNS. In either case, we're safe just probing LUNs
* 0-7 with INQUIRY, since SCSI-2 can't have more than 8 LUNs,
* and SCSI-3 should do REPORT_LUNS if it has more than 1 LUN.
*/
for (l = 0; l < 8; l++) {
sc->lun = l;
sc->request_buffer = c->buffer;
make_inquiry(sc, lun0_scsi_level);
/* we'll make a note of the LUN when the rx thread receives the response.
* No need to do it again here.
*/
if (iscsi_do_cmnd(session, c, 6)) {
/* we do need to record the SCSI level so we can build inquiries properly though */
if (l == 0) {
lun0_scsi_level = c->buffer[2] & 0x07;
if (LOG_ENABLED(ISCSI_LOG_INIT))
printk
("iSCSI: session (%u %u %u %u) is SCSI level %d\n",
sc->host->host_no,
sc->channel, sc->target,
sc->lun, lun0_scsi_level);
}
} else {
/* just assume there's no LUN */
}
if (test_bit
(SESSION_TERMINATING, &session->control_bits))
break;
if (signal_pending(current))
break;
}
}
done:
DEBUG_ALLOC
("iSCSI: session (bus %d target %d) hba %p host %p kfree iscsi cmnd %p, bufflen %u\n",
session->iscsi_bus, session->target_id, session->hba,
session->hba->host, c, c->bufflen);
kfree(c);
}
int
iscsi_reset_lun_probing(void)
{
int ret = 0;
spin_lock(&iscsi_lun_probe_lock);
if ((iscsi_currently_probing == NULL) && (iscsi_lun_probe_head == NULL)) {
/* if we're not currently probing, reset */
DEBUG_INIT
("iSCSI: session (bus %d target %d) reset LUN probing at %lu\n",
session->iscsi_bus, session->target_id, jiffies);
iscsi_next_probe = 0;
iscsi_lun_probe_start = 0;
smp_mb();
ret = 1;
} else {
DEBUG_INIT
("iSCSI: session (bus %d target %d)failed to reset LUN probing at %lu, currently probing %p, queue head %p\n",
session->iscsi_bus, session->target_id, jiffies,
iscsi_currently_probing, iscsi_lun_probe_head);
}
spin_unlock(&iscsi_lun_probe_lock);
return ret;
}
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [iscsi 2/2] iscsi-probe.c
2003-09-23 15:23 [iscsi 2/2] iscsi-probe.c Jeff Garzik
@ 2003-09-23 15:53 ` Christoph Hellwig
2003-09-24 3:43 ` Lincoln Dale
0 siblings, 1 reply; 13+ messages in thread
From: Christoph Hellwig @ 2003-09-23 15:53 UTC (permalink / raw)
To: Jeff Garzik; +Cc: linux-scsi
> #include <linux/blk.h>
Okay, this is for review only, but only a 2.6 version would have the
slightest chance of getting merged some day..
> #include <endian.h>
Did you actually try to compile this? This is a userspace header and
not available in a kernel uild.
> /* these are from $(TOPDIR)/drivers/scsi, not $(TOPDIR)/include */
> #include <scsi.h>
> #include <hosts.h>
We tend to use "" includes for these. Also in 2.6 there's no need for
hosts.h anymore and soon scsi.h - use <scsi/*.h) instead.
> DECLARE_MUTEX(iscsi_lun_probe_mutex);
> spinlock_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;
No need for initializers here, looks like someone didn't read the
basic docs.
>
> /* we need to make some syscalls to create and destroy the device name tree. */
> static int errno = 0;
Umm...
> 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);
Even more umm...
> if (session->probe_next || session->probe_prev) {
> DEBUG_INIT
> ("iSCSI: session for bus_id id %d target_id %d already queued for LUN probing\n",
Please linewrap after 80 chars.
> 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;
Don't you want list.h lists here? (And all over the driver)
> /* 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 (bus %d target %d)couldn't open /proc/scsi/scsi\n",
> session->iscsi_bus, session->target_id);
> set_fs(oldfs);
> return -ENOENT;
> }
>
> rc = filp->f_op->write(filp, str, strlen(str), &offset);
> filp_close(filp, 0);
> set_fs(oldfs);
Stop that crap. Now. I'm not going to review this any further until
all this asssumptions about random files in the namespace are fixed.
(the /dev messing below is even worse..)
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [iscsi 2/2] iscsi-probe.c
2003-09-23 15:53 ` Christoph Hellwig
@ 2003-09-24 3:43 ` Lincoln Dale
2003-09-24 12:37 ` Christoph Hellwig
0 siblings, 1 reply; 13+ messages in thread
From: Lincoln Dale @ 2003-09-24 3:43 UTC (permalink / raw)
To: Christoph Hellwig, Jeff Garzik; +Cc: linux-scsi
Jeff, Christoph:
At 01:53 AM 24/09/2003, Christoph Hellwig wrote:
> > #include <linux/blk.h>
>
>Okay, this is for review only, but only a 2.6 version would have the
>slightest chance of getting merged some day..
FYI, we (linux-iscsi@cisco.com) have created a branch of the iSCSI
initiator (https://sourceforge.net/projects/linux-iscsi/) that we plan to
submit for inclusion into 2.6 / 2.7.
lots of these things are already addressed and/or fixed.
the branch targeting 2.6 has lots of the
legacy/backwards-compatability-with-2.4 stuff removed as well as code that
should be in userspace removed from the kernel driver. (that fixed a bunch
of non-IA32 platform issues too)
if you're interested in more details and/or interested in helping review
the code, let me know.
the more the merrier.
cheers,
lincoln.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [iscsi 2/2] iscsi-probe.c
2003-09-24 3:43 ` Lincoln Dale
@ 2003-09-24 12:37 ` Christoph Hellwig
2003-09-24 13:42 ` Sachin Mhatre (smhatre)
0 siblings, 1 reply; 13+ messages in thread
From: Christoph Hellwig @ 2003-09-24 12:37 UTC (permalink / raw)
To: Lincoln Dale; +Cc: Christoph Hellwig, Jeff Garzik, linux-scsi
On Wed, Sep 24, 2003 at 01:43:50PM +1000, Lincoln Dale wrote:
> FYI, we (linux-iscsi@cisco.com) have created a branch of the iSCSI
> initiator (https://sourceforge.net/projects/linux-iscsi/) that we plan to
> submit for inclusion into 2.6 / 2.7.
We is that code in the sf CVS? I've found a lot of branches but
nothing that looks 2.6ish to me..
^ permalink raw reply [flat|nested] 13+ messages in thread
* RE: [iscsi 2/2] iscsi-probe.c
2003-09-24 12:37 ` Christoph Hellwig
@ 2003-09-24 13:42 ` Sachin Mhatre (smhatre)
2003-09-24 13:48 ` 'Christoph Hellwig'
0 siblings, 1 reply; 13+ messages in thread
From: Sachin Mhatre (smhatre) @ 2003-09-24 13:42 UTC (permalink / raw)
To: 'Christoph Hellwig'; +Cc: 'Jeff Garzik', linux-scsi
Hi,
The linux-iscsi source code submitted for review is for
inclusion into the 2.4 linux kernel tree. You can get this source code
from the linux-iscsi sourceforge branch with the tag
"linux-iscsi-3-5-branch". Just for convenience here is the cvs command
for anonymous checkout of this code:-
cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/linux-iscsi co -r
linux-iscsi-3-5-branch linux-iscsi .
We have tested this code on the Redhat Linux 9.0 platform, kernel
version 2.4.22.
Please let your valuable feedback pour in.
Thanks,
Sachin
-----Original Message-----
From: linux-scsi-owner@vger.kernel.org
[mailto:linux-scsi-owner@vger.kernel.org] On Behalf Of Christoph Hellwig
Sent: Wednesday, September 24, 2003 6:07 PM
To: Lincoln Dale
Cc: Christoph Hellwig; Jeff Garzik; linux-scsi@vger.kernel.org
Subject: Re: [iscsi 2/2] iscsi-probe.c
On Wed, Sep 24, 2003 at 01:43:50PM +1000, Lincoln Dale wrote:
> FYI, we (linux-iscsi@cisco.com) have created a branch of the iSCSI
> initiator (https://sourceforge.net/projects/linux-iscsi/) that we plan
to
> submit for inclusion into 2.6 / 2.7.
We is that code in the sf CVS? I've found a lot of branches but nothing
that looks 2.6ish to me..
-
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org More majordomo info
at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [iscsi 2/2] iscsi-probe.c
2003-09-24 13:42 ` Sachin Mhatre (smhatre)
@ 2003-09-24 13:48 ` 'Christoph Hellwig'
2003-09-24 14:14 ` Jeff Garzik
2003-09-24 16:20 ` Sachin Mhatre (smhatre)
0 siblings, 2 replies; 13+ messages in thread
From: 'Christoph Hellwig' @ 2003-09-24 13:48 UTC (permalink / raw)
To: Sachin Mhatre (smhatre); +Cc: 'Jeff Garzik', linux-scsi
On Wed, Sep 24, 2003 at 07:12:12PM +0530, Sachin Mhatre (smhatre) wrote:
> Hi,
> The linux-iscsi source code submitted for review is for
> inclusion into the 2.4 linux kernel tree. You can get this source code
> from the linux-iscsi sourceforge branch with the tag
> "linux-iscsi-3-5-branch". Just for convenience here is the cvs command
> for anonymous checkout of this code:-
> cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/linux-iscsi co -r
> linux-iscsi-3-5-branch linux-iscsi .
>
> We have tested this code on the Redhat Linux 9.0 platform, kernel
> version 2.4.22.
>
> Please let your valuable feedback pour in.
It looks like all comments I posted on linux-scsi apply to the
inux-iscsi-3-5-branch version of iscsi-probe.c, too. So please
stop messing randomly with the namespace and opening files from
kernelspace, respect Documentation/CodingStyle (avoid typedefs,
linebreaks after 80 chars, etc..) and try to use kernel functionality
where available.
Note that I can't help you with redhat inclusion, but for mainline
inclusion we'd like to see a 2.6 version before a 2.4 one. So my
advice would be to concentrate on 2.6 first, the scsi layer is much
nicer anyway there. If that's done backport it to 2.4 which might
require a few bad workarounds and/or small changes to the scsi core.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [iscsi 2/2] iscsi-probe.c
2003-09-24 13:48 ` 'Christoph Hellwig'
@ 2003-09-24 14:14 ` Jeff Garzik
2003-09-24 16:20 ` Sachin Mhatre (smhatre)
1 sibling, 0 replies; 13+ messages in thread
From: Jeff Garzik @ 2003-09-24 14:14 UTC (permalink / raw)
To: 'Christoph Hellwig'; +Cc: Sachin Mhatre (smhatre), linux-scsi
'Christoph Hellwig' wrote:
> Note that I can't help you with redhat inclusion, but for mainline
> inclusion we'd like to see a 2.6 version before a 2.4 one. So my
A pre-requisite for Red Hat inclusion is upstream inclusion.
Jeff
^ permalink raw reply [flat|nested] 13+ messages in thread
* RE: [iscsi 2/2] iscsi-probe.c
2003-09-24 13:48 ` 'Christoph Hellwig'
2003-09-24 14:14 ` Jeff Garzik
@ 2003-09-24 16:20 ` Sachin Mhatre (smhatre)
2003-09-24 16:27 ` Jeff Garzik
` (2 more replies)
1 sibling, 3 replies; 13+ messages in thread
From: Sachin Mhatre (smhatre) @ 2003-09-24 16:20 UTC (permalink / raw)
To: 'Christoph Hellwig'; +Cc: 'Jeff Garzik', linux-scsi
Hi,
1. We are using the following options in the indent command for handling
the 80 character limit:-
indent -kr -i8 -ts8 -sob -l80 -ss -bs -psl <filename>.c
However, this does not handle the log messages in double quotes. We will
manually take care of such messages.
2. The opening of files from kernel space is important for the iscsi
probing mechanism.
We read/write into the "/proc/scsi/scsi" file for
configuration/deconfiguration of iSCSI devices.
3. typedefs in our code will be removed. Kernel functions will be used
wherever available.
We will update you once the above mentioned changes are done.
Thanks,
Sachin
-----Original Message-----
From: linux-scsi-owner@vger.kernel.org
[mailto:linux-scsi-owner@vger.kernel.org] On Behalf Of 'Christoph
Hellwig'
Sent: Wednesday, September 24, 2003 7:18 PM
To: Sachin Mhatre (smhatre)
Cc: 'Jeff Garzik'; linux-scsi@vger.kernel.org
Subject: Re: [iscsi 2/2] iscsi-probe.c
On Wed, Sep 24, 2003 at 07:12:12PM +0530, Sachin Mhatre (smhatre) wrote:
> Hi,
> The linux-iscsi source code submitted for review is for
inclusion
> into the 2.4 linux kernel tree. You can get this source code from the
> linux-iscsi sourceforge branch with the tag "linux-iscsi-3-5-branch".
> Just for convenience here is the cvs command for anonymous checkout of
> this code:- cvs
> -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/linux-iscsi co -r
> linux-iscsi-3-5-branch linux-iscsi .
>
> We have tested this code on the Redhat Linux 9.0 platform, kernel
> version 2.4.22.
>
> Please let your valuable feedback pour in.
It looks like all comments I posted on linux-scsi apply to the
inux-iscsi-3-5-branch version of iscsi-probe.c, too. So please stop
messing randomly with the namespace and opening files from kernelspace,
respect Documentation/CodingStyle (avoid typedefs, linebreaks after 80
chars, etc..) and try to use kernel functionality where available.
Note that I can't help you with redhat inclusion, but for mainline
inclusion we'd like to see a 2.6 version before a 2.4 one. So my advice
would be to concentrate on 2.6 first, the scsi layer is much nicer
anyway there. If that's done backport it to 2.4 which might require a
few bad workarounds and/or small changes to the scsi core.
-
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org More majordomo info
at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [iscsi 2/2] iscsi-probe.c
2003-09-24 16:20 ` Sachin Mhatre (smhatre)
@ 2003-09-24 16:27 ` Jeff Garzik
2003-09-24 16:40 ` 'Christoph Hellwig'
2003-09-24 18:26 ` Scott M. Ferris
2 siblings, 0 replies; 13+ messages in thread
From: Jeff Garzik @ 2003-09-24 16:27 UTC (permalink / raw)
To: smhatre; +Cc: 'Christoph Hellwig', linux-scsi
Sachin Mhatre (smhatre) wrote:
> 2. The opening of files from kernel space is important for the iscsi
> probing mechanism.
No, it's not. This is the _reverse_ of what you should be doing.
> We read/write into the "/proc/scsi/scsi" file for
> configuration/deconfiguration of iSCSI devices.
This is wrong.
Jeff
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [iscsi 2/2] iscsi-probe.c
2003-09-24 16:20 ` Sachin Mhatre (smhatre)
2003-09-24 16:27 ` Jeff Garzik
@ 2003-09-24 16:40 ` 'Christoph Hellwig'
2003-09-24 17:11 ` Patrick Mansfield
2003-09-24 18:26 ` Scott M. Ferris
2 siblings, 1 reply; 13+ messages in thread
From: 'Christoph Hellwig' @ 2003-09-24 16:40 UTC (permalink / raw)
To: Sachin Mhatre (smhatre); +Cc: 'Jeff Garzik', linux-scsi
On Wed, Sep 24, 2003 at 09:50:02PM +0530, Sachin Mhatre (smhatre) wrote:
> indent -kr -i8 -ts8 -sob -l80 -ss -bs -psl <filename>.c
> However, this does not handle the log messages in double quotes. We will
> manually take care of such messages.
Please do.
> 2. The opening of files from kernel space is important for the iscsi
> probing mechanism.
It's not. Opening files from kernelspaced is utter crap. Creating
directories and special files from kernelspace is even worse.
> We read/write into the "/proc/scsi/scsi" file for
> configuration/deconfiguration of iSCSI devices.
I see. And that's exactly wrong. Take a look at the usb-storage or sbp2
drivers on how to handle it.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [iscsi 2/2] iscsi-probe.c
2003-09-24 16:40 ` 'Christoph Hellwig'
@ 2003-09-24 17:11 ` Patrick Mansfield
2003-09-24 17:30 ` 'Christoph Hellwig'
0 siblings, 1 reply; 13+ messages in thread
From: Patrick Mansfield @ 2003-09-24 17:11 UTC (permalink / raw)
To: 'Christoph Hellwig'
Cc: Sachin Mhatre (smhatre), 'Jeff Garzik', linux-scsi
On Wed, Sep 24, 2003 at 05:40:46PM +0100, 'Christoph Hellwig' wrote:
> > We read/write into the "/proc/scsi/scsi" file for
> > configuration/deconfiguration of iSCSI devices.
>
> I see. And that's exactly wrong. Take a look at the usb-storage or sbp2
> drivers on how to handle it.
They could use scsi_scan_host_selected(host, chan, target, SCAN_WILD_CARD,
0) or a scan target function, though their current lun scanning looks odd.
What was wrong with exporting scsi_scan_host_selected?
We don't have a scsi_remove_target().
-- Patrick Mansfield
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [iscsi 2/2] iscsi-probe.c
2003-09-24 17:11 ` Patrick Mansfield
@ 2003-09-24 17:30 ` 'Christoph Hellwig'
0 siblings, 0 replies; 13+ messages in thread
From: 'Christoph Hellwig' @ 2003-09-24 17:30 UTC (permalink / raw)
To: Patrick Mansfield
Cc: Sachin Mhatre (smhatre), 'Jeff Garzik', linux-scsi
On Wed, Sep 24, 2003 at 10:11:52AM -0700, Patrick Mansfield wrote:
> > I see. And that's exactly wrong. Take a look at the usb-storage or sbp2
> > drivers on how to handle it.
>
> They could use scsi_scan_host_selected(host, chan, target, SCAN_WILD_CARD,
> 0) or a scan target function, though their current lun scanning looks odd.
Well, if you want to keep their current code. I'm pretty sure they're
much better served with scsi_add_device.
> What was wrong with exporting scsi_scan_host_selected?
There's nothing inherently wrong except that aacraid who wanted it
shouldn't have used it in the first place. And I'm sure that's true
for iscsi, too.
> We don't have a scsi_remove_target().
Indeed. But that nothing that couldn't be easily fixed if we really
wanted it.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [iscsi 2/2] iscsi-probe.c
2003-09-24 16:20 ` Sachin Mhatre (smhatre)
2003-09-24 16:27 ` Jeff Garzik
2003-09-24 16:40 ` 'Christoph Hellwig'
@ 2003-09-24 18:26 ` Scott M. Ferris
2 siblings, 0 replies; 13+ messages in thread
From: Scott M. Ferris @ 2003-09-24 18:26 UTC (permalink / raw)
To: smhatre; +Cc: 'Christoph Hellwig', 'Jeff Garzik', linux-scsi
Sachin Mhatre (smhatre) wrote:
> Hi,
>
> 1. We are using the following options in the indent command for handling
> the 80 character limit:-
> indent -kr -i8 -ts8 -sob -l80 -ss -bs -psl <filename>.c
> However, this does not handle the log messages in double quotes. We will
> manually take care of such messages.
> 2. The opening of files from kernel space is important for the iscsi
> probing mechanism.
> We read/write into the "/proc/scsi/scsi" file for
> configuration/deconfiguration of iSCSI devices.
> 3. typedefs in our code will be removed. Kernel functions will be used
> wherever available.
> We will update you once the above mentioned changes are done.
If this iSCSI driver is going into a mainline 2.6 kernel,
iscsi-probe.c should cease to exist, among numerous other cleanups.
The probing code in linux-iscsi tries to accomplish two things:
1) Deal with the lack of a stable device naming scheme for dynamically
added SCSI devices on 2.2 and 2.4 kernels (since devfs was hardly ever
used). Now that 2.6 has sysfs and hotplugging, those mechanisms
should be used, and if need be altered to work better with iSCSI.
2) Get all LUNs scanned properly. The SCSI midlayer in 2.2 and (at
least older) 2.4 kernels didn't use REPORT_LUNS at all, and the kernel
still doesn't use REPORT_LUNS unless the INQUIRY data indicates
SCSI-3. This results in the kernel not scanning for all of the LUNs
available through a variety of iSCSI<->FC and iSCSI<->SPI gateway
devices, especially gateways that virtualize targets and do LUN
mapping.
It could be argued that the gateways are broken for not adjusting the
INQUIRY data to indicate SCSI-3. It could also be argued that a
gateway changing the SCSI level in the INQUIRY data would falsely
indicate that the end device supports commands that it can't actually
handle. One solution is to add a mechanism for the low-level driver
to tell the SCSI midlayer that it should use REPORT_LUNs
unconditionally when scanning, regardless of the end device's SCSI
level. Any gateway reached via a SCSI-3 transport protocol ought to
either respond to REPORT_LUNs or return the appropriate check
condition.
--
Scott M. Ferris,
sferris@acm.org
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2003-09-24 18:26 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2003-09-23 15:23 [iscsi 2/2] iscsi-probe.c Jeff Garzik
2003-09-23 15:53 ` Christoph Hellwig
2003-09-24 3:43 ` Lincoln Dale
2003-09-24 12:37 ` Christoph Hellwig
2003-09-24 13:42 ` Sachin Mhatre (smhatre)
2003-09-24 13:48 ` 'Christoph Hellwig'
2003-09-24 14:14 ` Jeff Garzik
2003-09-24 16:20 ` Sachin Mhatre (smhatre)
2003-09-24 16:27 ` Jeff Garzik
2003-09-24 16:40 ` 'Christoph Hellwig'
2003-09-24 17:11 ` Patrick Mansfield
2003-09-24 17:30 ` 'Christoph Hellwig'
2003-09-24 18:26 ` Scott M. Ferris
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox