2002-10-17 06:44:30

by Mike Anderson

[permalink] [raw]
Subject: [PATCH] scsi host cleanup 1/3 (base)

This is a resend of my previous patch clean ups to the scsi_host lists.

* Made function naming consistent with rest of SCSI
* Corrected a problem with driverfs registration to early. Also
changed from put_device to device_unregister.
* Fixed a regression in my previous patch that the scsi_host
list was not sorted by host number. When we get some device
naming this hack can be removed.
* Switch scsi host template, name, host lists to struct
list_head's.
* Moved all scsi_host related register / unregister functions
into hosts.c
* Added list accessor interface and created a function similar
to driverfs bus_for_each_dev.

The full patch is available at:
http://www-124.ibm.com/storageio/patches/2.5/scsi-host

-andmike
--
Michael Anderson
[email protected]

hosts.c | 893 +++++++++++++++++++++++++++++++++++++++++++++++++---------------
hosts.h | 97 +++---
2 files changed, 737 insertions(+), 253 deletions(-)
------

diff -Nru a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
--- a/drivers/scsi/hosts.c Wed Oct 16 22:51:48 2002
+++ b/drivers/scsi/hosts.c Wed Oct 16 22:51:48 2002
@@ -15,12 +15,15 @@
* Updated to reflect the new initialization scheme for the higher
* level of scsi drivers (sd/sr/st)
* September 17, 2000 Torben Mathiasen <[email protected]>
+ *
+ * Restructured scsi_host lists and associated functions.
+ * September 04, 2002 Mike Anderson ([email protected])
*/


/*
* This file contains the medium level SCSI
- * host interface initialization, as well as the scsi_hosts array of SCSI
+ * host interface initialization, as well as the scsi_hosts list of SCSI
* hosts currently present in the system.
*/

@@ -31,232 +34,714 @@
#include <linux/mm.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/smp_lock.h>

#define __KERNEL_SYSCALLS__

#include <linux/unistd.h>
+#include <asm/dma.h>

#include "scsi.h"
#include "hosts.h"

-/*
-static const char RCSid[] = "$Header: /vger/u4/cvs/linux/drivers/scsi/hosts.c,v 1.20 1996/12/12 19:18:32 davem Exp $";
-*/
+LIST_HEAD(scsi_host_tmpl_list);
+LIST_HEAD(scsi_host_hn_list);

-/*
- * The scsi host entries should be in the order you wish the
- * cards to be detected. A driver may appear more than once IFF
- * it can deal with being detected (and therefore initialized)
- * with more than one simultaneous host number, can handle being
- * reentrant, etc.
+LIST_HEAD(scsi_host_list);
+spinlock_t scsi_host_list_lock = SPIN_LOCK_UNLOCKED;
+
+struct Scsi_Device_Template * scsi_devicelist;
+
+static int scsi_host_next_hn; /* host_no for next new host */
+static int scsi_hosts_registered; /* cnt of registered scsi hosts */
+
+/**
+ * scsi_tp_for_each_host - call function for each scsi host off a template
+ * @shost_tp: a pointer to a scsi host template
+ * @callback: a pointer to callback function
*
- * They may appear in any order, as each SCSI host is told which host
- * number it is during detection.
- */
+ * Return value:
+ * 0 on Success / 1 on Failure
+ **/
+int scsi_tp_for_each_host(Scsi_Host_Template *shost_tp, int
+ (*callback)(struct Scsi_Host *shost))
+{
+ struct list_head *lh, *lh_sf;
+ struct Scsi_Host *shost;

-/*
- * When figure is run, we don't want to link to any object code. Since
- * the macro for each host will contain function pointers, we cannot
- * use it and instead must use a "blank" that does no such
- * idiocy.
- */
+ spin_lock(&scsi_host_list_lock);

-Scsi_Host_Template * scsi_hosts;
+ list_for_each_safe(lh, lh_sf, &scsi_host_list) {
+ shost = list_entry(lh, struct Scsi_Host, sh_list);
+ if (shost->hostt == shost_tp) {
+ spin_unlock(&scsi_host_list_lock);
+ callback(shost);
+ spin_lock(&scsi_host_list_lock);
+ }
+ }

+ spin_unlock(&scsi_host_list_lock);

-/*
- * Our semaphores and timeout counters, where size depends on
- * MAX_SCSI_HOSTS here.
- */
+ return 0;
+}

-Scsi_Host_Name * scsi_host_no_list;
-struct Scsi_Host * scsi_hostlist;
-struct Scsi_Device_Template * scsi_devicelist;
+/**
+ * scsi_host_generic_release - default release function for hosts
+ * @shost:
+ *
+ * Description:
+ * This is the default case for the release function. It should do
+ * the right thing for most correctly written host adapters.
+ **/
+static void scsi_host_generic_release(struct Scsi_Host *shost)
+{
+ if (shost->irq)
+ free_irq(shost->irq, NULL);
+ if (shost->dma_channel != 0xff)
+ free_dma(shost->dma_channel);
+ if (shost->io_port && shost->n_io_port)
+ release_region(shost->io_port, shost->n_io_port);
+}

-int max_scsi_hosts;
-int next_scsi_host;
+/**
+ * scsi_host_chk_and_release - check a scsi host for release and release
+ * @shost: a pointer to a scsi host to release
+ *
+ * Return value:
+ * 0 on Success / 1 on Failure
+ **/
+int scsi_host_chk_and_release(struct Scsi_Host *shost)
+{
+ int pcount;
+ Scsi_Device *sdev;
+ struct Scsi_Device_Template *sdev_tp;
+ Scsi_Cmnd *scmd;
+
+ /*
+ * Current policy is all shosts go away on unregister.
+ */
+ if (shost->hostt->module && GET_USE_COUNT(shost->hostt->module))
+ return 1;
+
+ /*
+ * FIXME Do ref counting. We force all of the devices offline to
+ * help prevent race conditions where other hosts/processors could
+ * try and get in and queue a command.
+ */
+ for (sdev = shost->host_queue; sdev; sdev = sdev->next)
+ sdev->online = FALSE;
+
+ for (sdev = shost->host_queue; sdev; sdev = sdev->next) {
+ /*
+ * Loop over all of the commands associated with the
+ * device. If any of them are busy, then set the state
+ * back to inactive and bail.
+ */
+ for (scmd = sdev->device_queue; scmd; scmd = scmd->next) {
+ if (scmd->request && scmd->request->rq_status !=
+ RQ_INACTIVE) {
+ printk(KERN_ERR "SCSI device not inactive"
+ "- rq_status=%d, target=%d, pid=%ld,"
+ "state=%d, owner=%d.\n",
+ scmd->request->rq_status,
+ scmd->target, scmd->pid,
+ scmd->state, scmd->owner);
+ for (sdev = shost->host_queue; sdev;
+ sdev = sdev->next) {
+ for (scmd = sdev->device_queue; scmd;
+ scmd = scmd->next)
+ if (scmd->request->rq_status ==
+ RQ_SCSI_DISCONNECTING)
+ scmd->request->rq_status = RQ_INACTIVE;
+ }
+ printk(KERN_ERR "Device busy???\n");
+ return 1;
+ }
+ /*
+ * No, this device is really free. Mark it as such, and
+ * continue on.
+ */
+ scmd->state = SCSI_STATE_DISCONNECTING;
+ if (scmd->request)
+ scmd->request->rq_status =
+ RQ_SCSI_DISCONNECTING; /* Mark as
+ busy */
+ }
+ }

-void
-scsi_unregister(struct Scsi_Host * sh){
- struct Scsi_Host * shpnt;
- Scsi_Host_Name *shn;
-
- if(scsi_hostlist == sh)
- scsi_hostlist = sh->next;
- else {
- shpnt = scsi_hostlist;
- while(shpnt->next != sh) shpnt = shpnt->next;
- shpnt->next = shpnt->next->next;
- }
-
- /*
- * We have to unregister the host from the scsi_host_no_list as well.
- * Decide by the host_no not by the name because most host drivers are
- * able to handle more than one adapters from the same kind (or family).
- */
- for ( shn=scsi_host_no_list; shn && (sh->host_no != shn->host_no);
- shn=shn->next);
- if (shn) shn->host_registered = 0;
- /* else {} : This should not happen, we should panic here... */
-
- /* If we are removing the last host registered, it is safe to reuse
- * its host number (this avoids "holes" at boot time) (DB)
- * It is also safe to reuse those of numbers directly below which have
- * been released earlier (to avoid some holes in numbering).
- */
- if(sh->host_no == max_scsi_hosts - 1) {
- while(--max_scsi_hosts >= next_scsi_host) {
- shpnt = scsi_hostlist;
- while(shpnt && shpnt->host_no != max_scsi_hosts - 1)
- shpnt = shpnt->next;
- if(shpnt)
- break;
- }
- }
- next_scsi_host--;
- kfree((char *) sh);
-}
-
-/* We call this when we come across a new host adapter. We only do this
- * once we are 100% sure that we want to use this host adapter - it is a
- * pain to reverse this, so we try to avoid it
- */
+ /*
+ * Next we detach the high level drivers from the Scsi_Device
+ * structures
+ */
+ for (sdev = shost->host_queue; sdev; sdev = sdev->next) {
+ for (sdev_tp = scsi_devicelist; sdev_tp;
+ sdev_tp = sdev_tp->next)
+ if (sdev_tp->detach)
+ (*sdev_tp->detach) (sdev);
+
+ /* If something still attached, punt */
+ if (sdev->attached) {
+ printk(KERN_ERR "Attached usage count = %d\n",
+ sdev->attached);
+ return 1;
+ }
+
+ if (shost-->hostt->slave_detach)
+ (*shost->hostt->slave_detach) (sdev);
+
+ devfs_unregister(sdev->de);
+ device_unregister(&sdev->sdev_driverfs_dev);
+ }
+
+ /* Next we free up the Scsi_Cmnd structures for this host */
+
+ for (sdev = shost->host_queue; sdev;
+ sdev = shost->host_queue) {
+ scsi_release_commandblocks(sdev);
+ blk_cleanup_queue(&sdev->request_queue);
+ /* Next free up the Scsi_Device structures for this host */
+ shost->host_queue = sdev->next;
+ if (sdev->inquiry)
+ kfree(sdev->inquiry);
+ kfree(sdev);
+ }
+
+ /* Remove the instance of the individual hosts */
+ pcount = scsi_hosts_registered;
+ if (shost->hostt->release)
+ (*shost->hostt->release) (shost);
+ else {
+ scsi_host_generic_release(shost);
+ }
+
+ if (pcount == scsi_hosts_registered)
+ scsi_unregister(shost);
+
+ return 0;
+}
+
+/**
+ * scsi_unregister - unregister a scsi host
+ * @shost: scsi host to be unregistered
+ **/
+void scsi_unregister(struct Scsi_Host *shost)
+{
+ struct list_head *lh;
+ Scsi_Host_Name *shost_name;
+
+ /* Remove shost from scsi_host_list */
+ spin_lock(&scsi_host_list_lock);
+ list_del(&shost->sh_list);
+ spin_unlock(&scsi_host_list_lock);
+
+ /* Unregister from scsi_host_hn_list */
+ list_for_each(lh, &scsi_host_hn_list) {
+ shost_name = list_entry(lh, Scsi_Host_Name, shn_list);
+ if (shost->host_no == shost_name->host_no)
+ shost_name->host_registered = 0;
+ }
+
+ /*
+ * Next, kill the kernel error recovery thread for this host.
+ */
+ if (shost->ehandler) {
+ DECLARE_MUTEX_LOCKED(sem);
+ shost->eh_notify = &sem;
+ send_sig(SIGHUP, shost->ehandler, 1);
+ down(&sem);
+ shost->eh_notify = NULL;
+ }
+
+ scsi_hosts_registered--;
+ shost->hostt->present--;
+
+ /* Cleanup proc and driverfs */
+#ifdef CONFIG_PROC_FS
+ scsi_proc_host_rm(shost);
+ if (!shost->hostt->present)
+ remove_proc_entry(shost->hostt->proc_name, proc_scsi);
+#endif
+ device_unregister(&shost->host_driverfs_dev);
+
+ kfree(shost);
+}
+
+/**
+ * scsi_host_hn_add - allocate and add new Scsi_Host_Name
+ * @name: String to store in name field
+ *
+ * Return value:
+ * Pointer to a new Scsi_Host_Name
+ **/
+Scsi_Host_Name *scsi_host_hn_add(char *name)
+{
+ Scsi_Host_Name *shost_name;
+ int len;
+
+ len = strlen(name);
+ shost_name = kmalloc(sizeof(*shost_name), GFP_KERNEL);
+ if (!shost_name) {
+ printk(KERN_ERR "%s: out of memory at line %d.\n",
+ __FUNCTION__, __LINE__);
+ return NULL;
+ }
+ shost_name->name = kmalloc(len + 1, GFP_KERNEL);
+ if (!shost_name->name) {
+ kfree(shost_name);
+ printk(KERN_ERR "%s: out of memory at line %d.\n",
+ __FUNCTION__, __LINE__);
+ return NULL;
+ }
+
+ if (len)
+ strncpy(shost_name->name, name, len);
+ shost_name->name[len] = 0;
+ shost_name->host_no = scsi_host_next_hn++;
+ shost_name->host_registered = 0;
+ list_add_tail(&shost_name->shn_list, &scsi_host_hn_list);
+
+ return shost_name;
+}
+
+/**
+ * scsi_register - register a scsi host adapter instance.
+ * @shost_tp: pointer to scsi host template
+ * @xtr_bytes: extra bytes to allocate for driver
+ *
+ * Note:
+ * We call this when we come across a new host adapter. We only do
+ * this once we are 100% sure that we want to use this host adapter -
+ * it is a pain to reverse this, so we try to avoid it
+ *
+ * Return value:
+ * Pointer to a new Scsi_Host
+ **/
extern int blk_nohighio;
-struct Scsi_Host * scsi_register(Scsi_Host_Template * tpnt, int j)
+struct Scsi_Host * scsi_register(Scsi_Host_Template *shost_tp, int xtr_bytes)
{
- struct Scsi_Host * retval, *shpnt, *o_shp;
- Scsi_Host_Name *shn, *shn2;
- int flag_new = 1;
- const char * hname;
- size_t hname_len;
- retval = (struct Scsi_Host *)kmalloc(sizeof(struct Scsi_Host) + j,
- (tpnt->unchecked_isa_dma && j ?
- GFP_DMA : 0) | GFP_ATOMIC);
- if(retval == NULL)
- {
- printk("scsi: out of memory in scsi_register.\n");
- return NULL;
- }
-
- memset(retval, 0, sizeof(struct Scsi_Host) + j);
-
- /* trying to find a reserved entry (host_no) */
- hname = (tpnt->proc_name) ? tpnt->proc_name : "";
- hname_len = strlen(hname);
- for (shn = scsi_host_no_list;shn;shn = shn->next) {
- if (!(shn->host_registered) &&
- (hname_len > 0) && (0 == strncmp(hname, shn->name, hname_len))) {
- flag_new = 0;
- retval->host_no = shn->host_no;
- shn->host_registered = 1;
- break;
- }
- }
- spin_lock_init(&retval->default_lock);
- scsi_assign_lock(retval, &retval->default_lock);
- atomic_set(&retval->host_active,0);
- retval->host_busy = 0;
- retval->host_failed = 0;
- if (flag_new) {
- shn = (Scsi_Host_Name *) kmalloc(sizeof(Scsi_Host_Name), GFP_ATOMIC);
- if (!shn) {
- kfree(retval);
- printk(KERN_ERR "scsi: out of memory(2) in scsi_register.\n");
- return NULL;
- }
- shn->name = kmalloc(hname_len + 1, GFP_ATOMIC);
- if (hname_len > 0)
- strncpy(shn->name, hname, hname_len);
- shn->name[hname_len] = 0;
- shn->host_no = max_scsi_hosts++;
- shn->host_registered = 1;
- shn->next = NULL;
- if (scsi_host_no_list) {
- for (shn2 = scsi_host_no_list;shn2->next;shn2 = shn2->next)
- ;
- shn2->next = shn;
- }
- else
- scsi_host_no_list = shn;
- retval->host_no = shn->host_no;
- }
- next_scsi_host++;
- retval->host_queue = NULL;
- init_waitqueue_head(&retval->host_wait);
- retval->resetting = 0;
- retval->last_reset = 0;
- retval->irq = 0;
- retval->dma_channel = 0xff;
-
- /* These three are default values which can be overridden */
- retval->max_channel = 0;
- retval->max_id = 8;
- retval->max_lun = 8;
-
- /*
- * All drivers right now should be able to handle 12 byte commands.
- * Every so often there are requests for 16 byte commands, but individual
- * low-level drivers need to certify that they actually do something
- * sensible with such commands.
- */
- retval->max_cmd_len = 12;
-
- retval->unique_id = 0;
- retval->io_port = 0;
- retval->hostt = tpnt;
- retval->next = NULL;
- retval->in_recovery = 0;
- retval->ehandler = NULL; /* Initial value until the thing starts up. */
- retval->eh_notify = NULL; /* Who we notify when we exit. */
+ struct Scsi_Host *shost, *shost_scr;
+ Scsi_Host_Name *shost_name = NULL;
+ Scsi_Host_Name *shn = NULL;
+ char *hname;
+ size_t hname_len;
+ struct list_head *lh;
+ int gfp_mask;
+ DECLARE_MUTEX_LOCKED(sem);
+
+ gfp_mask = GFP_KERNEL;
+ if (shost_tp->unchecked_isa_dma && xtr_bytes)
+ gfp_mask |= __GFP_DMA;
+
+ shost = kmalloc(sizeof(struct Scsi_Host) + xtr_bytes, gfp_mask);
+ if (!shost) {
+ printk(KERN_ERR "%s: out of memory.\n", __FUNCTION__);
+ return NULL;
+ }

- retval->max_host_blocked = tpnt->max_host_blocked ? tpnt->max_host_blocked : SCSI_DEFAULT_HOST_BLOCKED;
+ memset(shost, 0, sizeof(struct Scsi_Host) + xtr_bytes);

- retval->host_blocked = 0;
- retval->host_self_blocked = FALSE;
+ /*
+ * Determine host number. Check reserved first before allocating
+ * new one
+ */
+ hname = (shost_tp->proc_name) ? shost_tp->proc_name : "";
+ hname_len = strlen(hname);
+
+ if (hname_len)
+ list_for_each(lh, &scsi_host_hn_list) {
+ shn = list_entry(lh, Scsi_Host_Name, shn_list);
+ if (!(shn->host_registered) &&
+ !strncmp(hname, shn->name, hname_len)) {
+ shost_name = shn;
+ break;
+ }
+ }
+
+ if (!shost_name) {
+ shost_name = scsi_host_hn_add(hname);
+ if (!shost_name) {
+ kfree(shost);
+ return NULL;
+ }
+ }
+
+ shost->host_no = shost_name->host_no;
+ shost_name->host_registered = 1;
+ scsi_hosts_registered++;
+
+ spin_lock_init(&shost->default_lock);
+ scsi_assign_lock(shost, &shost->default_lock);
+ atomic_set(&shost->host_active,0);
+
+ init_waitqueue_head(&shost->host_wait);
+ shost->dma_channel = 0xff;
+
+ /* These three are default values which can be overridden */
+ shost->max_channel = 0;
+ shost->max_id = 8;
+ shost->max_lun = 8;
+
+ /*
+ * All drivers right now should be able to handle 12 byte
+ * commands. Every so often there are requests for 16 byte
+ * commands, but individual low-level drivers need to certify that
+ * they actually do something sensible with such commands.
+ */
+ shost->max_cmd_len = 12;
+ shost->hostt = shost_tp;
+ shost->host_blocked = FALSE;
+ shost->host_self_blocked = FALSE;

#ifdef DEBUG
- printk("Register %x %x: %d\n", (int)retval, (int)retval->hostt, j);
+ printk("%s: %x %x: %d\n", __FUNCTION_ (int)shost,
+ (int)shost->hostt, xtr_bytes);
#endif

- /* The next six are the default values which can be overridden
- * if need be */
- retval->this_id = tpnt->this_id;
- retval->can_queue = tpnt->can_queue;
- retval->sg_tablesize = tpnt->sg_tablesize;
- retval->cmd_per_lun = tpnt->cmd_per_lun;
- retval->unchecked_isa_dma = tpnt->unchecked_isa_dma;
- retval->use_clustering = tpnt->use_clustering;
- if (!blk_nohighio)
- retval->highmem_io = tpnt->highmem_io;
-
- retval->max_sectors = tpnt->max_sectors;
- retval->use_blk_tcq = tpnt->use_blk_tcq;
-
- if(!scsi_hostlist)
- scsi_hostlist = retval;
- else {
- shpnt = scsi_hostlist;
- if (retval->host_no < shpnt->host_no) {
- retval->next = shpnt;
- wmb(); /* want all to see these writes in this order */
- scsi_hostlist = retval;
+ /*
+ * The next six are the default values which can be overridden if
+ * need be
+ */
+ shost->this_id = shost_tp->this_id;
+ shost->can_queue = shost_tp->can_queue;
+ shost->sg_tablesize = shost_tp->sg_tablesize;
+ shost->cmd_per_lun = shost_tp->cmd_per_lun;
+ shost->unchecked_isa_dma = shost_tp->unchecked_isa_dma;
+ shost->use_clustering = shost_tp->use_clustering;
+ if (!blk_nohighio)
+ shost->highmem_io = shost_tp->highmem_io;
+
+ shost->max_sectors = shost_tp->max_sectors;
+ shost->use_blk_tcq = shost_tp->use_blk_tcq;
+
+ spin_lock(&scsi_host_list_lock);
+ /*
+ * FIXME When device naming is complete remove this step that
+ * orders the scsi_host_list by host number and just do a
+ * list_add_tail.
+ */
+ list_for_each(lh, &scsi_host_list) {
+ shost_scr = list_entry(lh, struct Scsi_Host, sh_list);
+ if (shost->host_no < shost_scr->host_no) {
+ __list_add(&shost->sh_list, shost_scr->sh_list.prev,
+ &shost_scr->sh_list);
+ goto found;
+ }
+ }
+ list_add_tail(&shost->sh_list, &scsi_host_list);
+found:
+ spin_unlock(&scsi_host_list_lock);
+
+#ifdef CONFIG_PROC_FS
+ /* Add the new driver to /proc/scsi if not already there */
+ if (!shost_tp->proc_dir)
+ scsi_proc_host_mkdir(shost_tp);
+ scsi_proc_host_add(shost);
+#endif
+
+ strncpy(shost->host_driverfs_dev.name, shost_tp->proc_name,
+ DEVICE_NAME_SIZE-1);
+ sprintf(shost->host_driverfs_dev.bus_id, "scsi%d",
+ shost->host_no);
+
+ shost->eh_notify = &sem;
+ kernel_thread((int (*)(void *)) scsi_error_handler, (void *) shost, 0);
+ /*
+ * Now wait for the kernel error thread to initialize itself
+ * as it might be needed when we scan the bus.
+ */
+ down(&sem);
+ shost->eh_notify = NULL;
+
+ shost->hostt->present++;
+
+ return shost;
+}
+
+
+/**
+ * scsi_register_host - register a low level host driver
+ * @shost_tp: pointer to a scsi host driver template
+ *
+ * Return value:
+ * 0 on Success / 1 on Failure.
+ **/
+int scsi_register_host(Scsi_Host_Template *shost_tp)
+{
+ int cur_cnt;
+ Scsi_Device *sdev;
+ struct Scsi_Device_Template *sdev_tp;
+ struct list_head *lh;
+ struct Scsi_Host *shost;
+
+ /*
+ * Check no detect routine.
+ */
+ if (!shost_tp->detect)
+ return 1;
+
+ /* If max_sectors isn't set, default to max */
+ if (!shost_tp->max_sectors)
+ shost_tp->max_sectors = 1024;
+
+ cur_cnt = scsi_hosts_registered;
+
+ MOD_INC_USE_COUNT;
+
+ /*
+ * The detect routine must carefully spinunlock/spinlock if it
+ * enables interrupts, since all interrupt handlers do spinlock as
+ * well.
+ */
+
+ /*
+ * detect should do its own locking
+ * FIXME present is now set is scsi_register which breaks manual
+ * registration code below.
+ */
+ shost_tp->detect(shost_tp);
+
+ if (shost_tp->present) {
+ /*
+ * FIXME Who needs manual registration and why???
+ */
+ if (cur_cnt == scsi_hosts_registered) {
+ if (shost_tp->present > 1) {
+ printk(KERN_ERR "scsi: Failure to register"
+ "low-level scsi driver");
+ scsi_unregister_host(shost_tp);
+ return 1;
+ }
+ /*
+ * The low-level driver failed to register a driver.
+ * We can do this now.
+ */
+ if(scsi_register(shost_tp, 0)==NULL) {
+ printk(KERN_ERR "scsi: register failed.\n");
+ scsi_unregister_host(shost_tp);
+ return 1;
+ }
+ }
+
+ list_add_tail(&shost_tp->shtp_list, &scsi_host_tmpl_list);
+
+ /* The next step is to call scan_scsis here. This generates the
+ * Scsi_Devices entries
+ */
+ list_for_each(lh, &scsi_host_list) {
+ shost = list_entry(lh, struct Scsi_Host, sh_list);
+ if (shost->hostt == shost_tp) {
+ const char *dm_name;
+ if (shost_tp->info) {
+ dm_name = shost_tp->info(shost);
+ } else {
+ dm_name = shost_tp->name;
+ }
+ printk(KERN_INFO "scsi%d : %s\n",
+ shost->host_no, dm_name);
+
+ /* first register parent with driverfs */
+ device_register(&shost->host_driverfs_dev);
+ scan_scsis(shost, 0, 0, 0, 0);
+ if (shost->select_queue_depths != NULL) {
+ (shost->select_queue_depths) (shost, shost->host_queue);
+ }
+ }
+ }
+
+ for (sdev_tp = scsi_devicelist; sdev_tp;
+ sdev_tp = sdev_tp->next) {
+ if (sdev_tp->init && sdev_tp->dev_noticed)
+ (*sdev_tp->init) ();
+ }
+
+ /*
+ * Next we create the Scsi_Cmnd structures for this host
+ */
+ list_for_each(lh, &scsi_host_list) {
+ shost = list_entry(lh, struct Scsi_Host, sh_list);
+ for (sdev = shost->host_queue; sdev; sdev = sdev->next)
+ if (sdev->host->hostt == shost_tp) {
+ for (sdev_tp = scsi_devicelist;
+ sdev_tp;
+ sdev_tp = sdev_tp->next)
+ if (sdev_tp->attach)
+ (*sdev_tp->attach) (sdev);
+ if (sdev->attached) {
+ scsi_build_commandblocks(sdev);
+ if (sdev->queue_depth == 0)
+ goto out_of_space;
+ }
+ }
+ }
+
+ /* This does any final handling that is required. */
+ for (sdev_tp = scsi_devicelist; sdev_tp;
+ sdev_tp = sdev_tp->next) {
+ if (sdev_tp->finish && sdev_tp->nr_dev) {
+ (*sdev_tp->finish) ();
+ }
+ }
+ }
+
+ return 0;
+
+out_of_space:
+ scsi_unregister_host(shost_tp); /* easiest way to clean up?? */
+ return 1;
+}
+
+/**
+ * scsi_unregister_host - unregister a low level host adapter driver
+ * @shost_tp: scsi host template to unregister.
+ *
+ * Description:
+ * Similarly, this entry point should be called by a loadable module
+ * if it is trying to remove a low level scsi driver from the system.
+ *
+ * Return value:
+ * 0 on Success / 1 on Failure
+ *
+ * Notes:
+ * rmmod does not care what we return here the module will be
+ * removed.
+ **/
+int scsi_unregister_host(Scsi_Host_Template *shost_tp)
+{
+ int pcount;
+
+ /* get the big kernel lock, so we don't race with open() */
+ lock_kernel();
+
+ pcount = scsi_hosts_registered;
+
+ scsi_tp_for_each_host(shost_tp, scsi_host_chk_and_release);
+
+ if (pcount != scsi_hosts_registered)
+ printk(KERN_INFO "scsi : %d host%s left.\n", scsi_hosts_registered,
+ (scsi_hosts_registered == 1) ? "" : "s");
+
+ /*
+ * Remove it from the list if all
+ * hosts were successfully removed (ie preset == 0)
+ */
+ if (!shost_tp->present) {
+ list_del(&shost_tp->shtp_list);
+ }
+
+ MOD_DEC_USE_COUNT;
+
+ unlock_kernel();
+ return 0;
+
+}
+
+/**
+ * *scsi_host_get_next - get scsi host and inc ref count
+ * @shost: pointer to a Scsi_Host or NULL to start.
+ *
+ * Return value:
+ * A pointer to next Scsi_Host in list or NULL.
+ **/
+struct Scsi_Host *scsi_host_get_next(struct Scsi_Host *shost)
+{
+ struct list_head *lh = NULL;
+
+ spin_lock(&scsi_host_list_lock);
+ if (shost) {
+ /* XXX Dec ref on cur shost */
+ lh = shost->sh_list.next;
+ } else {
+ lh = scsi_host_list.next;
+ }
+
+ if (lh == &scsi_host_list) {
+ shost = (struct Scsi_Host *)NULL;
+ goto done;
+ }
+
+ shost = list_entry(lh, struct Scsi_Host, sh_list);
+ /* XXX Inc ref count */
+
+done:
+ spin_unlock(&scsi_host_list_lock);
+ return shost;
+}
+
+/**
+ * scsi_host_hn_get - get a Scsi_Host by host no and inc ref count
+ * @host_no: host number to locate
+ *
+ * Return value:
+ * A pointer to located Scsi_Host or NULL.
+ **/
+struct Scsi_Host *scsi_host_hn_get(unsigned short host_no)
+{
+ struct list_head *lh;
+ struct Scsi_Host *shost;
+
+ spin_lock(&scsi_host_list_lock);
+ list_for_each(lh, &scsi_host_list) {
+ shost = list_entry(lh, struct Scsi_Host, sh_list);
+ if (shost->host_no == host_no) {
+ /* XXX Inc ref count */
+ goto done;
+ }
+ }
+
+ shost = (struct Scsi_Host *)NULL;
+done:
+ spin_unlock(&scsi_host_list_lock);
+ return shost;
+}
+
+/**
+ * *scsi_host_put - dec a Scsi_Host ref count
+ * @shost: Pointer to Scsi_Host to dec.
+ **/
+void scsi_host_put(struct Scsi_Host *shost)
+{
+
+ /* XXX Get list lock */
+ /* XXX dec ref count */
+ /* XXX Release list lock */
+ return;
+}
+
+/**
+ * scsi_host_hn_init - init scsi host number list from string
+ * @shost_hn: string of scsi host driver names.
+ **/
+void __init scsi_host_hn_init(char *shost_hn)
+{
+ char *temp = shost_hn;
+
+ while (temp) {
+ while (*temp && (*temp != ':') && (*temp != ','))
+ temp++;
+ if (!*temp)
+ temp = NULL;
+ else
+ *temp++ = 0;
+ (void)scsi_host_hn_add(shost_hn);
+ shost_hn = temp;
+ }
+}
+
+/**
+ * scsi_host_no_release - free all entries in scsi host number list
+ **/
+void __exit scsi_host_hn_release()
+{
+ struct list_head *lh, *next;
+ Scsi_Host_Name *shn;
+
+ list_for_each_safe(lh, next, &scsi_host_hn_list) {
+ shn = list_entry(lh, Scsi_Host_Name, shn_list);
+ if (shn->name)
+ kfree(shn->name);
+ kfree(shn);
}
- else {
- for (o_shp = shpnt, shpnt = shpnt->next; shpnt;
- o_shp = shpnt, shpnt = shpnt->next) {
- if (retval->host_no < shpnt->host_no) {
- retval->next = shpnt;
- wmb();
- o_shp->next = retval;
- break;
- }
- }
- if (! shpnt)
- o_shp->next = retval;
- }
- }
-
- return retval;
}

void scsi_host_busy_inc(struct Scsi_Host *shost, Scsi_Device *sdev)
@@ -279,8 +764,7 @@
if (shost->in_recovery && (shost->host_busy == shost->host_failed)) {
up(shost->eh_wait);
SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler"
- "thread (%d)\n",
- atomic_read(&shost->eh_wait->count)));
+ " thread\n"));
}
spin_unlock_irqrestore(shost->host_lock, flags);
}
@@ -295,8 +779,7 @@
if (shost->host_busy == shost->host_failed) {
up(shost->eh_wait);
SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler"
- "thread (%d)\n",
- atomic_read(&shost->eh_wait->count)));
+ " thread\n"));
}
spin_unlock_irqrestore(shost->host_lock, flags);
}
diff -Nru a/drivers/scsi/hosts.h b/drivers/scsi/hosts.h
--- a/drivers/scsi/hosts.h Wed Oct 16 22:51:48 2002
+++ b/drivers/scsi/hosts.h Wed Oct 16 22:51:48 2002
@@ -16,15 +16,14 @@
* of the same type.
*
* Jiffies wrap fixes (host->resetting), 3 Dec 1998 Andrea Arcangeli
+ *
+ * Restructured scsi_host lists and associated functions.
+ * September 04, 2002 Mike Anderson ([email protected])
*/

#ifndef _HOSTS_H
#define _HOSTS_H

-/*
- $Header: /vger/u4/cvs/linux/drivers/scsi/hosts.h,v 1.6 1997/01/19 23:07:13 davem Exp $
-*/
-
#include <linux/config.h>
#include <linux/proc_fs.h>
#include <linux/pci.h>
@@ -58,8 +57,7 @@
typedef struct SHT
{

- /* Used with loadable modules so we can construct a linked list. */
- struct SHT * next;
+ struct list_head shtp_list;

/* Used with loadable modules so that we know when it is safe to unload */
struct module * module;
@@ -374,7 +372,7 @@
* This information is private to the scsi mid-layer. Wrapping it in a
* struct private is a way of marking it in a sort of C++ type of way.
*/
- struct Scsi_Host * next;
+ struct list_head sh_list;
Scsi_Device * host_queue;
struct list_head all_scsi_hosts;
struct list_head my_devices;
@@ -510,28 +508,26 @@
* thing. This physical pseudo-device isn't real and won't be available
* from any high-level drivers.
*/
-extern void scsi_free_host_dev(Scsi_Device * SDpnt);
-extern Scsi_Device * scsi_get_host_dev(struct Scsi_Host * SHpnt);
+extern void scsi_free_host_dev(Scsi_Device *);
+extern Scsi_Device * scsi_get_host_dev(struct Scsi_Host *);

-extern void scsi_unblock_requests(struct Scsi_Host * SHpnt);
-extern void scsi_block_requests(struct Scsi_Host * SHpnt);
-extern void scsi_report_bus_reset(struct Scsi_Host * SHpnt, int channel);
+extern void scsi_unblock_requests(struct Scsi_Host *);
+extern void scsi_block_requests(struct Scsi_Host *);
+extern void scsi_report_bus_reset(struct Scsi_Host *, int);

typedef struct SHN
- {
- struct SHN * next;
- char * name;
- unsigned short host_no;
- unsigned short host_registered;
- } Scsi_Host_Name;
+{
+ struct list_head shn_list;
+ char *name;
+ unsigned short host_no;
+ unsigned short host_registered;
+} Scsi_Host_Name;

-extern Scsi_Host_Name * scsi_host_no_list;
-extern struct Scsi_Host * scsi_hostlist;
extern struct Scsi_Device_Template * scsi_devicelist;

-extern Scsi_Host_Template * scsi_hosts;
-
-extern void build_proc_dir_entries(Scsi_Host_Template *);
+extern void scsi_proc_host_mkdir(Scsi_Host_Template *);
+extern void scsi_proc_host_add(struct Scsi_Host *);
+extern void scsi_proc_host_rm(struct Scsi_Host *);

/*
* scsi_init initializes the scsi hosts.
@@ -540,34 +536,33 @@
extern int next_scsi_host;

unsigned int scsi_init(void);
-extern struct Scsi_Host * scsi_register(Scsi_Host_Template *, int j);
-extern void scsi_unregister(struct Scsi_Host * i);
-extern void scsi_register_blocked_host(struct Scsi_Host * SHpnt);
-extern void scsi_deregister_blocked_host(struct Scsi_Host * SHpnt);
+extern struct Scsi_Host * scsi_register(Scsi_Host_Template *, int);
+extern void scsi_unregister(struct Scsi_Host *);
+extern void scsi_register_blocked_host(struct Scsi_Host *);
+extern void scsi_deregister_blocked_host(struct Scsi_Host *);

-static inline void scsi_assign_lock(struct Scsi_Host *host, spinlock_t *lock)
+static inline void scsi_assign_lock(struct Scsi_Host *shost, spinlock_t *lock)
{
- host->host_lock = lock;
+ shost->host_lock = lock;
}

-static inline void scsi_set_pci_device(struct Scsi_Host *SHpnt,
+static inline void scsi_set_pci_device(struct Scsi_Host *shost,
struct pci_dev *pdev)
{
- SHpnt->pci_dev = pdev;
- SHpnt->host_driverfs_dev.parent=&pdev->dev;
+ shost->pci_dev = pdev;
+ shost->host_driverfs_dev.parent=&pdev->dev;
+
+ /* register parent with driverfs */
+ device_register(&shost->host_driverfs_dev);
}


/*
* Prototypes for functions/data in scsi_scan.c
*/
-extern void scan_scsis(struct Scsi_Host *shpnt,
- uint hardcoded,
- uint hchannel,
- uint hid,
- uint hlun);
+extern void scan_scsis(struct Scsi_Host *, uint, uint, uint, uint);

-extern void scsi_mark_host_reset(struct Scsi_Host *Host);
+extern void scsi_mark_host_reset(struct Scsi_Host *);

#define BLANK_HOST {"", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}

@@ -596,7 +591,7 @@
struct device_driver scsi_driverfs_driver;
};

-void scsi_initialize_queue(Scsi_Device * SDpnt, struct Scsi_Host * SHpnt);
+void scsi_initialize_queue(Scsi_Device *, struct Scsi_Host *);


/*
@@ -607,6 +602,12 @@
extern int scsi_register_host(Scsi_Host_Template *);
extern int scsi_unregister_host(Scsi_Host_Template *);

+extern struct Scsi_Host *scsi_host_get_next(struct Scsi_Host *);
+extern struct Scsi_Host *scsi_host_hn_get(unsigned short);
+extern void scsi_host_put(struct Scsi_Host *);
+extern void scsi_host_hn_init(char *);
+extern void scsi_host_hn_release(void);
+
/*
* host_busy inc/dec/test functions
*/
@@ -614,7 +615,6 @@
extern void scsi_host_busy_dec_and_test(struct Scsi_Host *, Scsi_Device *);
extern void scsi_host_failed_inc_and_test(struct Scsi_Host *);

-
/*
* This is an ugly hack. If we expect to be able to load devices at run time,
* we need to leave extra room in some of the data structures. Doing a
@@ -643,21 +643,22 @@

/**
* scsi_find_device - find a device given the host
+ * @shost: SCSI host pointer
* @channel: SCSI channel (zero if only one channel)
* @pun: SCSI target number (physical unit number)
* @lun: SCSI Logical Unit Number
**/
-static inline Scsi_Device *scsi_find_device(struct Scsi_Host *host,
+static inline Scsi_Device *scsi_find_device(struct Scsi_Host *shost,
int channel, int pun, int lun) {
- Scsi_Device *SDpnt;
+ Scsi_Device *sdev;

- for(SDpnt = host->host_queue;
- SDpnt != NULL;
- SDpnt = SDpnt->next)
- if(SDpnt->channel == channel && SDpnt->id == pun
- && SDpnt->lun ==lun)
+ for (sdev = shost->host_queue;
+ sdev != NULL;
+ sdev = sdev->next)
+ if (sdev->channel == channel && sdev->id == pun
+ && sdev->lun ==lun)
break;
- return SDpnt;
+ return sdev;
}

#endif


2002-10-17 06:47:09

by Mike Anderson

[permalink] [raw]
Subject: [PATCH] scsi host cleanup 2/3 (mid lvl changes)

This is a resend of my previous patch clean ups to the scsi_host lists.

* Made function naming consistent with rest of SCSI
* Corrected a problem with driverfs registration to early. Also
changed from put_device to device_unregister.
* Fixed a regression in my previous patch that the scsi_host
list was not sorted by host number. When we get some device
naming this hack can be removed.
* Switch scsi host template, name, host lists to struct
list_head's.
* Moved all scsi_host related register / unregister functions
into hosts.c
* Added list accessor interface and created a function similar
to driverfs bus_for_each_dev.

The full patch is available at:
http://www-124.ibm.com/storageio/patches/2.5/scsi-host

-andmike
--
Michael Anderson
[email protected]

scsi.c | 456 +++---------------------------------------------------------
scsi_proc.c | 57 ++++---
scsi_syms.c | 5
sg.c | 6
4 files changed, 63 insertions(+), 461 deletions(-)
------

diff -Nru a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
--- a/drivers/scsi/scsi.c Wed Oct 16 22:51:48 2002
+++ b/drivers/scsi/scsi.c Wed Oct 16 22:51:48 2002
@@ -1659,33 +1659,6 @@
}
}

-void __init scsi_host_no_insert(char *str, int n)
-{
- Scsi_Host_Name *shn, *shn2;
- int len;
-
- len = strlen(str);
- if (len && (shn = (Scsi_Host_Name *) kmalloc(sizeof(Scsi_Host_Name), GFP_ATOMIC))) {
- if ((shn->name = kmalloc(len+1, GFP_ATOMIC))) {
- strncpy(shn->name, str, len);
- shn->name[len] = 0;
- shn->host_no = n;
- shn->host_registered = 0;
- shn->next = NULL;
- if (scsi_host_no_list) {
- for (shn2 = scsi_host_no_list;shn2->next;shn2 = shn2->next)
- ;
- shn2->next = shn;
- }
- else
- scsi_host_no_list = shn;
- max_scsi_hosts = n+1;
- }
- else
- kfree((char *) shn);
- }
-}
-
#ifdef CONFIG_PROC_FS
static int scsi_proc_info(char *buffer, char **start, off_t offset, int length)
{
@@ -1698,7 +1671,8 @@
/*
* First, see if there are any attached devices or not.
*/
- for (HBA_ptr = scsi_hostlist; HBA_ptr; HBA_ptr = HBA_ptr->next) {
+ for (HBA_ptr = scsi_host_get_next(NULL); HBA_ptr;
+ HBA_ptr = scsi_host_get_next(HBA_ptr)) {
if (HBA_ptr->host_queue != NULL) {
break;
}
@@ -1706,7 +1680,8 @@
size = sprintf(buffer + len, "Attached devices: %s\n", (HBA_ptr) ? "" : "none");
len += size;
pos = begin + len;
- for (HBA_ptr = scsi_hostlist; HBA_ptr; HBA_ptr = HBA_ptr->next) {
+ for (HBA_ptr = scsi_host_get_next(NULL); HBA_ptr;
+ HBA_ptr = scsi_host_get_next(HBA_ptr)) {
#if 0
size += sprintf(buffer + len, "scsi%2d: %s\n", (int) HBA_ptr->host_no,
HBA_ptr->hostt->procname);
@@ -1873,7 +1848,8 @@
printk(KERN_INFO "scsi singledevice %d %d %d %d\n", host, channel,
id, lun);

- for (HBA_ptr = scsi_hostlist; HBA_ptr; HBA_ptr = HBA_ptr->next) {
+ for (HBA_ptr = scsi_host_get_next(NULL); HBA_ptr;
+ HBA_ptr = scsi_host_get_next(HBA_ptr)) {
if (HBA_ptr->host_no == host) {
break;
}
@@ -1918,7 +1894,8 @@
lun = simple_strtoul(p + 1, &p, 0);


- for (HBA_ptr = scsi_hostlist; HBA_ptr; HBA_ptr = HBA_ptr->next) {
+ for (HBA_ptr = scsi_host_get_next(NULL); HBA_ptr;
+ HBA_ptr = scsi_host_get_next(HBA_ptr)) {
if (HBA_ptr->host_no == host) {
break;
}
@@ -1988,369 +1965,6 @@
#endif

/*
- * This entry point should be called by a driver if it is trying
- * to add a low level scsi driver to the system.
- */
-int scsi_register_host(Scsi_Host_Template * tpnt)
-{
- int pcount;
- struct Scsi_Host *shpnt;
- Scsi_Device *SDpnt;
- struct Scsi_Device_Template *sdtpnt;
- const char *name;
- int out_of_space = 0;
-
- if (tpnt->next || !tpnt->detect)
- return 1; /* Must be already loaded, or
- * no detect routine available
- */
-
- /* If max_sectors isn't set, default to max */
- if (!tpnt->max_sectors)
- tpnt->max_sectors = 1024;
-
- pcount = next_scsi_host;
-
- MOD_INC_USE_COUNT;
-
- /* The detect routine must carefully spinunlock/spinlock if
- it enables interrupts, since all interrupt handlers do
- spinlock as well. */
-
- /*
- * detect should do its own locking
- */
- tpnt->present = tpnt->detect(tpnt);
-
- if (tpnt->present) {
- if (pcount == next_scsi_host) {
- if (tpnt->present > 1) {
- printk(KERN_ERR "scsi: Failure to register low-level scsi driver");
- scsi_unregister_host(tpnt);
- return 1;
- }
- /*
- * The low-level driver failed to register a driver.
- * We can do this now.
- */
- if(scsi_register(tpnt, 0)==NULL)
- {
- printk(KERN_ERR "scsi: register failed.\n");
- scsi_unregister_host(tpnt);
- return 1;
- }
- }
- tpnt->next = scsi_hosts; /* Add to the linked list */
- scsi_hosts = tpnt;
-
- /* Add the new driver to /proc/scsi */
-#ifdef CONFIG_PROC_FS
- build_proc_dir_entries(tpnt);
-#endif
-
-
- /*
- * Add the kernel threads for each host adapter that will
- * handle error correction.
- */
- for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
- if (shpnt->hostt == tpnt) {
- DECLARE_MUTEX_LOCKED(sem);
-
- shpnt->eh_notify = &sem;
- kernel_thread((int (*)(void *)) scsi_error_handler,
- (void *) shpnt, 0);
-
- /*
- * Now wait for the kernel error thread to initialize itself
- * as it might be needed when we scan the bus.
- */
- down(&sem);
- shpnt->eh_notify = NULL;
- }
- }
-
- for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
- if (shpnt->hostt == tpnt) {
- if (tpnt->info) {
- name = tpnt->info(shpnt);
- } else {
- name = tpnt->name;
- }
- printk(KERN_INFO "scsi%d : %s\n", /* And print a little message */
- shpnt->host_no, name);
- strncpy(shpnt->host_driverfs_dev.name,name,
- DEVICE_NAME_SIZE-1);
- sprintf(shpnt->host_driverfs_dev.bus_id,
- "scsi%d",
- shpnt->host_no);
- }
- }
-
- /* The next step is to call scan_scsis here. This generates the
- * Scsi_Devices entries
- */
- for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
- if (shpnt->hostt == tpnt) {
- /* first register parent with driverfs */
- device_register(&shpnt->host_driverfs_dev);
- scan_scsis(shpnt, 0, 0, 0, 0);
- }
- }
-
- for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) {
- if (sdtpnt->init && sdtpnt->dev_noticed)
- (*sdtpnt->init) ();
- }
-
- /*
- * Next we create the Scsi_Cmnd structures for this host
- */
- for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
- for (SDpnt = shpnt->host_queue; SDpnt; SDpnt = SDpnt->next)
- if (SDpnt->host->hostt == tpnt) {
- for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
- if (sdtpnt->attach)
- (*sdtpnt->attach) (SDpnt);
- if (SDpnt->attached) {
- scsi_build_commandblocks(SDpnt);
- if (SDpnt->current_queue_depth == 0)
- out_of_space = 1;
- }
- }
- }
-
- /* This does any final handling that is required. */
- for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) {
- if (sdtpnt->finish && sdtpnt->nr_dev) {
- (*sdtpnt->finish) ();
- }
- }
- }
-
- if (out_of_space) {
- scsi_unregister_host(tpnt); /* easiest way to clean up?? */
- return 1;
- } else
- return 0;
-}
-
-/*
- * Similarly, this entry point should be called by a loadable module if it
- * is trying to remove a low level scsi driver from the system.
- */
-int scsi_unregister_host(Scsi_Host_Template * tpnt)
-{
- int online_status;
- int pcount0, pcount;
- Scsi_Cmnd *SCpnt;
- Scsi_Device *SDpnt;
- Scsi_Device *SDpnt1;
- struct Scsi_Device_Template *sdtpnt;
- struct Scsi_Host *sh1;
- struct Scsi_Host *shpnt;
- char name[10]; /* host_no>=10^9? I don't think so. */
-
- /* get the big kernel lock, so we don't race with open() */
- lock_kernel();
-
- /*
- * First verify that this host adapter is completely free with no pending
- * commands
- */
- for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
- for (SDpnt = shpnt->host_queue; SDpnt;
- SDpnt = SDpnt->next) {
- if (SDpnt->host->hostt == tpnt
- && SDpnt->host->hostt->module
- && GET_USE_COUNT(SDpnt->host->hostt->module))
- goto err_out;
- /*
- * FIXME(eric) - We need to find a way to notify the
- * low level driver that we are shutting down - via the
- * special device entry that still needs to get added.
- *
- * Is detach interface below good enough for this?
- */
- }
- }
-
- /*
- * FIXME(eric) put a spinlock on this. We force all of the devices offline
- * to help prevent race conditions where other hosts/processors could try and
- * get in and queue a command.
- */
- for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
- for (SDpnt = shpnt->host_queue; SDpnt;
- SDpnt = SDpnt->next) {
- if (SDpnt->host->hostt == tpnt)
- SDpnt->online = FALSE;
-
- }
- }
-
- for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
- if (shpnt->hostt != tpnt) {
- continue;
- }
- for (SDpnt = shpnt->host_queue; SDpnt;
- SDpnt = SDpnt->next) {
- /*
- * Loop over all of the commands associated with the device. If any of
- * them are busy, then set the state back to inactive and bail.
- */
- for (SCpnt = SDpnt->device_queue; SCpnt;
- SCpnt = SCpnt->next) {
- online_status = SDpnt->online;
- SDpnt->online = FALSE;
- if (SCpnt->request && SCpnt->request->rq_status != RQ_INACTIVE) {
- printk(KERN_ERR "SCSI device not inactive - rq_status=%d, target=%d, pid=%ld, state=%d, owner=%d.\n",
- SCpnt->request->rq_status, SCpnt->target, SCpnt->pid,
- SCpnt->state, SCpnt->owner);
- for (SDpnt1 = shpnt->host_queue; SDpnt1;
- SDpnt1 = SDpnt1->next) {
- for (SCpnt = SDpnt1->device_queue; SCpnt;
- SCpnt = SCpnt->next)
- if (SCpnt->request->rq_status == RQ_SCSI_DISCONNECTING)
- SCpnt->request->rq_status = RQ_INACTIVE;
- }
- SDpnt->online = online_status;
- printk(KERN_ERR "Device busy???\n");
- goto err_out;
- }
- /*
- * No, this device is really free. Mark it as such, and
- * continue on.
- */
- SCpnt->state = SCSI_STATE_DISCONNECTING;
- if(SCpnt->request)
- SCpnt->request->rq_status = RQ_SCSI_DISCONNECTING; /* Mark as busy */
- }
- }
- }
- /* Next we detach the high level drivers from the Scsi_Device structures */
-
- for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
- if (shpnt->hostt != tpnt) {
- continue;
- }
- for (SDpnt = shpnt->host_queue; SDpnt;
- SDpnt = SDpnt->next) {
- for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
- if (sdtpnt->detach)
- (*sdtpnt->detach) (SDpnt);
-
- /* If something still attached, punt */
- if (SDpnt->attached) {
- printk(KERN_ERR "Attached usage count = %d\n", SDpnt->attached);
- goto err_out;
- }
- if (shpnt->hostt->slave_detach)
- (*shpnt->hostt->slave_detach) (SDpnt);
- devfs_unregister (SDpnt->de);
- put_device(&SDpnt->sdev_driverfs_dev);
- }
- }
-
- /*
- * Next, kill the kernel error recovery thread for this host.
- */
- for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
- if (shpnt->hostt == tpnt
- && shpnt->ehandler != NULL) {
- DECLARE_MUTEX_LOCKED(sem);
-
- shpnt->eh_notify = &sem;
- send_sig(SIGHUP, shpnt->ehandler, 1);
- down(&sem);
- shpnt->eh_notify = NULL;
- }
- }
-
- /* Next we free up the Scsi_Cmnd structures for this host */
-
- for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
- if (shpnt->hostt != tpnt) {
- continue;
- }
- for (SDpnt = shpnt->host_queue; SDpnt;
- SDpnt = shpnt->host_queue) {
- scsi_release_commandblocks(SDpnt);
-
- blk_cleanup_queue(&SDpnt->request_queue);
- /* Next free up the Scsi_Device structures for this host */
- shpnt->host_queue = SDpnt->next;
- if (SDpnt->inquiry)
- kfree(SDpnt->inquiry);
- kfree((char *) SDpnt);
-
- }
- }
-
- /* Next we go through and remove the instances of the individual hosts
- * that were detected */
-
- pcount0 = next_scsi_host;
- for (shpnt = scsi_hostlist; shpnt; shpnt = sh1) {
- sh1 = shpnt->next;
- if (shpnt->hostt != tpnt)
- continue;
- pcount = next_scsi_host;
- /* Remove the /proc/scsi directory entry */
- sprintf(name,"%d",shpnt->host_no);
- remove_proc_entry(name, tpnt->proc_dir);
- put_device(&shpnt->host_driverfs_dev);
- if (tpnt->release)
- (*tpnt->release) (shpnt);
- else {
- /* This is the default case for the release function.
- * It should do the right thing for most correctly
- * written host adapters.
- */
- if (shpnt->irq)
- free_irq(shpnt->irq, NULL);
- if (shpnt->dma_channel != 0xff)
- free_dma(shpnt->dma_channel);
- if (shpnt->io_port && shpnt->n_io_port)
- release_region(shpnt->io_port, shpnt->n_io_port);
- }
- if (pcount == next_scsi_host)
- scsi_unregister(shpnt);
- tpnt->present--;
- }
-
- if (pcount0 != next_scsi_host)
- printk(KERN_INFO "scsi : %d host%s left.\n", next_scsi_host,
- (next_scsi_host == 1) ? "" : "s");
-
- /*
- * Remove it from the linked list and /proc if all
- * hosts were successfully removed (ie preset == 0)
- */
- if (!tpnt->present) {
- Scsi_Host_Template **SHTp = &scsi_hosts;
- Scsi_Host_Template *SHT;
-
- while ((SHT = *SHTp) != NULL) {
- if (SHT == tpnt) {
- *SHTp = SHT->next;
- remove_proc_entry(tpnt->proc_name, proc_scsi);
- break;
- }
- SHTp = &SHT->next;
- }
- }
- MOD_DEC_USE_COUNT;
-
- unlock_kernel();
- return 0;
-
-err_out:
- unlock_kernel();
- return -1;
-}
-
-/*
* This entry point should be called by a loadable module if it is trying
* add a high level scsi driver to the system.
*/
@@ -2361,7 +1975,7 @@
int out_of_space = 0;

#ifdef CONFIG_KMOD
- if (scsi_hosts == NULL)
+ if (scsi_host_get_next(NULL) == NULL)
request_module("scsi_hostadapter");
#endif

@@ -2375,7 +1989,8 @@
* First scan the devices that we know about, and see if we notice them.
*/

- for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
+ for (shpnt = scsi_host_get_next(NULL); shpnt;
+ shpnt = scsi_host_get_next(shpnt)) {
for (SDpnt = shpnt->host_queue; SDpnt;
SDpnt = SDpnt->next) {
if (tpnt->detect)
@@ -2394,7 +2009,8 @@
/*
* Now actually connect the devices to the new driver.
*/
- for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
+ for (shpnt = scsi_host_get_next(NULL); shpnt;
+ shpnt = scsi_host_get_next(shpnt)) {
for (SDpnt = shpnt->host_queue; SDpnt;
SDpnt = SDpnt->next) {
if (tpnt->attach)
@@ -2444,7 +2060,8 @@
* Next, detach the devices from the driver.
*/

- for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
+ for (shpnt = scsi_host_get_next(NULL); shpnt;
+ shpnt = scsi_host_get_next(shpnt)) {
for (SDpnt = shpnt->host_queue; SDpnt;
SDpnt = SDpnt->next) {
if (tpnt->detach)
@@ -2516,7 +2133,8 @@
Scsi_Device *SDpnt;
printk(KERN_INFO "Dump of scsi host parameters:\n");
i = 0;
- for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
+ for (shpnt = scsi_host_get_next(NULL); shpnt;
+ shpnt = scsi_host_get_next(shpnt)) {
printk(KERN_INFO " %d %d %d : %d %d\n",
shpnt->host_failed,
shpnt->host_busy,
@@ -2527,7 +2145,8 @@

printk(KERN_INFO "\n\n");
printk(KERN_INFO "Dump of scsi command parameters:\n");
- for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
+ for (shpnt = scsi_host_get_next(NULL); shpnt;
+ shpnt = scsi_host_get_next(shpnt)) {
printk(KERN_INFO "h:c:t:l (dev sect nsect cnumsec sg) (ret all flg) (to/cmd to ito) cmd snse result\n");
for (SDpnt = shpnt->host_queue; SDpnt; SDpnt = SDpnt->next) {
for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) {
@@ -2565,26 +2184,6 @@
}
#endif /* CONFIG_PROC_FS */

-static int __init scsi_host_no_init (char *str)
-{
- static int next_no = 0;
- char *temp;
-
- while (str) {
- temp = str;
- while (*temp && (*temp != ':') && (*temp != ','))
- temp++;
- if (!*temp)
- temp = NULL;
- else
- *temp++ = 0;
- scsi_host_no_insert(str, next_no);
- str = temp;
- next_no++;
- }
- return 1;
-}
-
static char *scsihosts;

MODULE_PARM(scsihosts, "s");
@@ -2724,9 +2323,8 @@
#endif

scsi_devfs_handle = devfs_mk_dir (NULL, "scsi", NULL);
- if (scsihosts)
- printk(KERN_INFO "scsi: host order: %s\n", scsihosts);
- scsi_host_no_init (scsihosts);
+
+ scsi_host_hn_init(scsihosts);

bus_register(&scsi_driverfs_bus_type);

@@ -2738,19 +2336,11 @@

static void __exit exit_scsi(void)
{
- Scsi_Host_Name *shn, *shn2 = NULL;
int i;

devfs_unregister (scsi_devfs_handle);
- for (shn = scsi_host_no_list;shn;shn = shn->next) {
- if (shn->name)
- kfree(shn->name);
- if (shn2)
- kfree (shn2);
- shn2 = shn;
- }
- if (shn2)
- kfree (shn2);
+
+ scsi_host_hn_release();

#ifdef CONFIG_PROC_FS
/* No, we're not here anymore. Don't show the /proc/scsi files. */
diff -Nru a/drivers/scsi/scsi_proc.c b/drivers/scsi/scsi_proc.c
--- a/drivers/scsi/scsi_proc.c Wed Oct 16 22:51:48 2002
+++ b/drivers/scsi/scsi_proc.c Wed Oct 16 22:51:48 2002
@@ -119,35 +119,44 @@
return(ret);
}

-void build_proc_dir_entries(Scsi_Host_Template * tpnt)
+void scsi_proc_host_mkdir(Scsi_Host_Template *shost_tp)
{
- struct Scsi_Host *hpnt;
- char name[10]; /* see scsi_unregister_host() */
-
- tpnt->proc_dir = proc_mkdir(tpnt->proc_name, proc_scsi);
- if (!tpnt->proc_dir) {
- printk(KERN_ERR "Unable to proc_mkdir in scsi.c/build_proc_dir_entries");
+ shost_tp->proc_dir = proc_mkdir(shost_tp->proc_name, proc_scsi);
+ if (!shost_tp->proc_dir) {
+ printk(KERN_ERR "%s: proc_mkdir failed for %s\n",
+ __FUNCTION__, shost_tp->proc_name);
return;
}
- tpnt->proc_dir->owner = tpnt->module;
+ shost_tp->proc_dir->owner = shost_tp->module;
+}
+
+void scsi_proc_host_add(struct Scsi_Host *shost)
+{
+ char name[10];
+ struct proc_dir_entry *p;

- hpnt = scsi_hostlist;
- while (hpnt) {
- if (tpnt == hpnt->hostt) {
- struct proc_dir_entry *p;
- sprintf(name,"%d",hpnt->host_no);
- p = create_proc_read_entry(name,
- S_IFREG | S_IRUGO | S_IWUSR,
- tpnt->proc_dir,
- proc_scsi_read,
- (void *)hpnt);
- if (!p)
- panic("Not enough memory to register SCSI HBA in /proc/scsi !\n");
- p->write_proc=proc_scsi_write;
- p->owner = tpnt->module;
- }
- hpnt = hpnt->next;
+ sprintf(name,"%d",shost->host_no);
+ p = create_proc_read_entry(name,
+ S_IFREG | S_IRUGO | S_IWUSR,
+ shost->hostt->proc_dir,
+ proc_scsi_read,
+ (void *)shost);
+ if (!p) {
+ printk(KERN_ERR "%s: Failed to register host %d in"
+ "%s\n", __FUNCTION__, shost->host_no,
+ shost->hostt->proc_name);
+ } else {
+ p->write_proc=proc_scsi_write;
+ p->owner = shost->hostt->module;
}
+
+}
+
+void scsi_proc_host_rm(struct Scsi_Host *shost)
+{
+ char name[10];
+ sprintf(name,"%d",shost->host_no);
+ remove_proc_entry(name, shost->hostt->proc_dir);
}

/*
diff -Nru a/drivers/scsi/scsi_syms.c b/drivers/scsi/scsi_syms.c
--- a/drivers/scsi/scsi_syms.c Wed Oct 16 22:51:48 2002
+++ b/drivers/scsi/scsi_syms.c Wed Oct 16 22:51:48 2002
@@ -91,8 +91,9 @@
/*
* These are here only while I debug the rest of the scsi stuff.
*/
-EXPORT_SYMBOL(scsi_hostlist);
-EXPORT_SYMBOL(scsi_hosts);
+EXPORT_SYMBOL(scsi_host_get_next);
+EXPORT_SYMBOL(scsi_host_hn_get);
+EXPORT_SYMBOL(scsi_host_put);
EXPORT_SYMBOL(scsi_devicelist);
EXPORT_SYMBOL(scsi_device_types);

diff -Nru a/drivers/scsi/sg.c b/drivers/scsi/sg.c
--- a/drivers/scsi/sg.c Wed Oct 16 22:51:48 2002
+++ b/drivers/scsi/sg.c Wed Oct 16 22:51:48 2002
@@ -3103,7 +3103,8 @@
struct Scsi_Host *shp;
int k;

- for (k = 0, shp = scsi_hostlist; shp; shp = shp->next, ++k) {
+ for (k = 0, shp = scsi_host_get_next(NULL); shp;
+ shp = scsi_host_get_next(shp), ++k) {
for (; k < shp->host_no; ++k)
PRINT_PROC("-1\t-1\t-1\t-1\t-1\t-1\n");
PRINT_PROC("%u\t%hu\t%hd\t%hu\t%d\t%d\n",
@@ -3147,7 +3148,8 @@
char buff[SG_MAX_HOST_STR_LEN];
char *cp;

- for (k = 0, shp = scsi_hostlist; shp; shp = shp->next, ++k) {
+ for (k = 0, shp = scsi_host_get_next(NULL); shp;
+ shp = scsi_host_get_next(shp), ++k) {
for (; k < shp->host_no; ++k)
PRINT_PROC("<no active host>\n");
strncpy(buff, shp->hostt->info ? shp->hostt->info(shp) :

2002-10-17 06:51:01

by Mike Anderson

[permalink] [raw]
Subject: [PATCH] scsi host cleanup 3/3 (driver changes)


If you read my previous post on this patch I indicated that few of the
driver changes I was only able to compile test ( block/cciss_scsi.c,
scsi/53c700.c, scsi/pcmcia/*, scsi/wd33c93.c). The changes to the
drivers are to remove the old interfaces and possibly extra NULL inits
of struct members. These changes will need to be ok'd by there
respective maintainers. I did receive feedback from two.

I have tested these changes on ips, aic, qlogicisp, and scsi_debug
drivers.

This is a resend of my previous patch clean ups to the scsi_host lists.

* Made function naming consistent with rest of SCSI
* Corrected a problem with driverfs registration to early. Also
changed from put_device to device_unregister.
* Fixed a regression in my previous patch that the scsi_host
list was not sorted by host number. When we get some device
naming this hack can be removed.
* Switch scsi host template, name, host lists to struct
list_head's.
* Moved all scsi_host related register / unregister functions
into hosts.c
* Added list accessor interface and created a function similar
to driverfs bus_for_each_dev.

The full patch is available at:
http://www-124.ibm.com/storageio/patches/2.5/scsi-host

-andmike
--
Michael Anderson
[email protected]

acorn/scsi/acornscsi.c | 6 ++----
acorn/scsi/arxescsi.c | 8 ++------
acorn/scsi/cumana_2.c | 8 ++------
acorn/scsi/eesox.c | 8 ++------
acorn/scsi/powertec.c | 8 ++------
block/cciss_scsi.c | 21 ++-------------------
scsi/53c700.c | 6 ++----
scsi/aic7xxx/aic7xxx_linux_host.h | 1 -
scsi/cpqfcTSinit.c | 4 +---
scsi/fcal.c | 4 +---
scsi/ips.h | 3 ---
scsi/pcmcia/aha152x_stub.c | 3 ++-
scsi/pcmcia/fdomain_stub.c | 3 ++-
scsi/pcmcia/nsp_cs.c | 3 ++-
scsi/pcmcia/qlogic_stub.c | 3 ++-
scsi/wd33c93.c | 5 +----
16 files changed, 25 insertions(+), 69 deletions(-)
------

diff -Nru a/drivers/acorn/scsi/acornscsi.c b/drivers/acorn/scsi/acornscsi.c
--- a/drivers/acorn/scsi/acornscsi.c Wed Oct 16 22:51:48 2002
+++ b/drivers/acorn/scsi/acornscsi.c Wed Oct 16 22:51:48 2002
@@ -3010,14 +3010,12 @@
int length, int host_no, int inout)
{
int pos, begin = 0, devidx;
- struct Scsi_Host *instance = scsi_hostlist;
+ struct Scsi_Host *instance;
Scsi_Device *scd;
AS_Host *host;
char *p = buffer;

- for (instance = scsi_hostlist;
- instance && instance->host_no != host_no;
- instance = instance->next);
+ instance = scsi_host_hn_get(host_no);

if (inout == 1 || !instance)
return -EINVAL;
diff -Nru a/drivers/acorn/scsi/arxescsi.c b/drivers/acorn/scsi/arxescsi.c
--- a/drivers/acorn/scsi/arxescsi.c Wed Oct 16 22:51:48 2002
+++ b/drivers/acorn/scsi/arxescsi.c Wed Oct 16 22:51:48 2002
@@ -384,15 +384,11 @@
int length, int host_no, int inout)
{
int pos, begin;
- struct Scsi_Host *host = scsi_hostlist;
+ struct Scsi_Host *host;
ARXEScsi_Info *info;
Scsi_Device *scd;

- while (host) {
- if (host->host_no == host_no)
- break;
- host = host->next;
- }
+ host = scsi_host_hn_get(host_no);
if (!host)
return 0;

diff -Nru a/drivers/acorn/scsi/cumana_2.c b/drivers/acorn/scsi/cumana_2.c
--- a/drivers/acorn/scsi/cumana_2.c Wed Oct 16 22:51:48 2002
+++ b/drivers/acorn/scsi/cumana_2.c Wed Oct 16 22:51:48 2002
@@ -498,15 +498,11 @@
int length, int host_no, int inout)
{
int pos, begin;
- struct Scsi_Host *host = scsi_hostlist;
+ struct Scsi_Host *host;
CumanaScsi2_Info *info;
Scsi_Device *scd;

- while (host) {
- if (host->host_no == host_no)
- break;
- host = host->next;
- }
+ host = scsi_host_hn_get(host_no);
if (!host)
return 0;

diff -Nru a/drivers/acorn/scsi/eesox.c b/drivers/acorn/scsi/eesox.c
--- a/drivers/acorn/scsi/eesox.c Wed Oct 16 22:51:48 2002
+++ b/drivers/acorn/scsi/eesox.c Wed Oct 16 22:51:48 2002
@@ -499,15 +499,11 @@
int length, int host_no, int inout)
{
int pos, begin;
- struct Scsi_Host *host = scsi_hostlist;
+ struct Scsi_Host *host;
EESOXScsi_Info *info;
Scsi_Device *scd;

- while (host) {
- if (host->host_no == host_no)
- break;
- host = host->next;
- }
+ host = scsi_host_hn_get(host_no);
if (!host)
return 0;

diff -Nru a/drivers/acorn/scsi/powertec.c b/drivers/acorn/scsi/powertec.c
--- a/drivers/acorn/scsi/powertec.c Wed Oct 16 22:51:48 2002
+++ b/drivers/acorn/scsi/powertec.c Wed Oct 16 22:51:48 2002
@@ -404,15 +404,11 @@
int length, int host_no, int inout)
{
int pos, begin;
- struct Scsi_Host *host = scsi_hostlist;
+ struct Scsi_Host *host;
PowerTecScsi_Info *info;
Scsi_Device *scd;

- while (host) {
- if (host->host_no == host_no)
- break;
- host = host->next;
- }
+ host = scsi_host_hn_get(host_no);
if (!host)
return 0;

diff -Nru a/drivers/block/cciss_scsi.c b/drivers/block/cciss_scsi.c
--- a/drivers/block/cciss_scsi.c Wed Oct 16 22:51:48 2002
+++ b/drivers/block/cciss_scsi.c Wed Oct 16 22:51:48 2002
@@ -1250,8 +1250,6 @@
return length;
}

-/* It's a pity that we need this, but, we do... */
-extern struct Scsi_Host *scsi_hostlist; /* from ../scsi/hosts.c */

int
cciss_scsi_proc_info(char *buffer, /* data buffer */
@@ -1268,24 +1266,9 @@
ctlr_info_t *ci;
int cntl_num;

- /* Lets see if we can find our Scsi_Host...
- this might be kind of "bad", searching scis_hostlist this way
- but how else can we find the scsi host? I think I've seen
- this coded both ways, (circular list and null terminated list)
- I coded it to work either way, since I wasn't sure. */

- sh = scsi_hostlist;
- found=0;
- do {
- if (sh == NULL) break;
- if (sh->host_no == hostnum) {
- found++;
- break;
- }
- sh = sh->next;
- } while (sh != scsi_hostlist && sh != NULL);
-
- if (sh == NULL || found == 0) /* This really shouldn't ever happen. */
+ sh = scsi_host_hn_get(hostnum);
+ if (sh == NULL) /* This really shouldn't ever happen. */
return -EINVAL;

ci = (ctlr_info_t *) sh->hostdata[0];
diff -Nru a/drivers/scsi/53c700.c b/drivers/scsi/53c700.c
--- a/drivers/scsi/53c700.c Wed Oct 16 22:51:48 2002
+++ b/drivers/scsi/53c700.c Wed Oct 16 22:51:48 2002
@@ -1719,13 +1719,11 @@
{
static char buf[4096]; /* 1 page should be sufficient */
int len = 0;
- struct Scsi_Host *host = scsi_hostlist;
+ struct Scsi_Host *host;
struct NCR_700_Host_Parameters *hostdata;
Scsi_Device *SDp;

- while(host != NULL && host->host_no != host_no)
- host = host->next;
-
+ host = scsi_host_hn_get(host_no);
if(host == NULL)
return 0;

diff -Nru a/drivers/scsi/aic7xxx/aic7xxx_linux_host.h b/drivers/scsi/aic7xxx/aic7xxx_linux_host.h
--- a/drivers/scsi/aic7xxx/aic7xxx_linux_host.h Wed Oct 16 22:51:48 2002
+++ b/drivers/scsi/aic7xxx/aic7xxx_linux_host.h Wed Oct 16 22:51:48 2002
@@ -63,7 +63,6 @@
* to do with card config are filled in after the card is detected.
*/
#define AIC7XXX { \
- next: NULL, \
module: NULL, \
proc_dir: NULL, \
proc_info: ahc_linux_proc_info, \
diff -Nru a/drivers/scsi/cpqfcTSinit.c b/drivers/scsi/cpqfcTSinit.c
--- a/drivers/scsi/cpqfcTSinit.c Wed Oct 16 22:51:48 2002
+++ b/drivers/scsi/cpqfcTSinit.c Wed Oct 16 22:51:48 2002
@@ -938,9 +938,7 @@
char buf[81];

// Search the Scsi host list for our controller
- for (host=scsi_hostlist; host; host=host->next)
- if (host->host_no == hostno)
- break;
+ host = scsi_host_hn_get(hostno);

if (!host) return -ESRCH;

diff -Nru a/drivers/scsi/fcal.c b/drivers/scsi/fcal.c
--- a/drivers/scsi/fcal.c Wed Oct 16 22:51:48 2002
+++ b/drivers/scsi/fcal.c Wed Oct 16 22:51:48 2002
@@ -213,9 +213,7 @@
char *pos = buffer;
int i, j;

- for (host=scsi_hostlist; host; host=host->next)
- if (host->host_no == hostno)
- break;
+ host = scsi_host_hn_get(hostno);

if (!host) return -ESRCH;

diff -Nru a/drivers/scsi/ips.h b/drivers/scsi/ips.h
--- a/drivers/scsi/ips.h Wed Oct 16 22:51:48 2002
+++ b/drivers/scsi/ips.h Wed Oct 16 22:51:48 2002
@@ -407,7 +407,6 @@
*/
#if LINUX_VERSION_CODE < LinuxVersionCode(2,4,0)
#define IPS { \
- next : NULL, \
module : NULL, \
proc_info : NULL, \
proc_dir : NULL, \
@@ -437,7 +436,6 @@
}
#elif LINUX_VERSION_CODE < LinuxVersionCode(2,5,0)
#define IPS { \
- next : NULL, \
module : NULL, \
proc_info : NULL, \
name : NULL, \
@@ -466,7 +464,6 @@
}
#else
#define IPS { \
- next : NULL, \
module : NULL, \
proc_info : NULL, \
name : NULL, \
diff -Nru a/drivers/scsi/wd33c93.c b/drivers/scsi/wd33c93.c
--- a/drivers/scsi/wd33c93.c Wed Oct 16 22:51:48 2002
+++ b/drivers/scsi/wd33c93.c Wed Oct 16 22:51:48 2002
@@ -1871,10 +1871,7 @@
int x,i;
static int stop = 0;

- for (instance=scsi_hostlist; instance; instance=instance->next) {
- if (instance->host_no == hn)
- break;
- }
+ instance = scsi_host_hn_get(hn);
if (!instance) {
printk("*** Hmm... Can't find host #%d!\n",hn);
return (-ESRCH);
diff -Nru a/drivers/scsi/pcmcia/aha152x_stub.c b/drivers/scsi/pcmcia/aha152x_stub.c
--- a/drivers/scsi/pcmcia/aha152x_stub.c Wed Oct 16 22:51:48 2002
+++ b/drivers/scsi/pcmcia/aha152x_stub.c Wed Oct 16 22:51:48 2002
@@ -294,7 +294,8 @@

tail = &link->dev;
info->ndev = 0;
- for (host = scsi_hostlist; host; host = host->next)
+ for (host = scsi_host_get_next(NULL); host;
+ host = scsi_host_get_next(host))
if (host->hostt == &driver_template)
for (dev = host->host_queue; dev; dev = dev->next) {
u_long arg[2], id;
diff -Nru a/drivers/scsi/pcmcia/fdomain_stub.c b/drivers/scsi/pcmcia/fdomain_stub.c
--- a/drivers/scsi/pcmcia/fdomain_stub.c Wed Oct 16 22:51:48 2002
+++ b/drivers/scsi/pcmcia/fdomain_stub.c Wed Oct 16 22:51:48 2002
@@ -258,7 +258,8 @@

tail = &link->dev;
info->ndev = 0;
- for (host = scsi_hostlist; host; host = host->next)
+ for (host = scsi_host_get_next(NULL); host;
+ host = scsi_host_get_next(host))
if (host->hostt == &driver_template)
for (dev = host->host_queue; dev; dev = dev->next) {
u_long arg[2], id;
diff -Nru a/drivers/scsi/pcmcia/nsp_cs.c b/drivers/scsi/pcmcia/nsp_cs.c
--- a/drivers/scsi/pcmcia/nsp_cs.c Wed Oct 16 22:51:48 2002
+++ b/drivers/scsi/pcmcia/nsp_cs.c Wed Oct 16 22:51:48 2002
@@ -1520,7 +1520,8 @@
DEBUG(0, "GET_SCSI_INFO\n");
tail = &link->dev;
info->ndev = 0;
- for (host = scsi_hostlist; host != NULL; host = host->next) {
+ for (host = scsi_host_get_next(NULL); host;
+ host = scsi_host_get_next(host))
if (host->hostt == &driver_template) {
for (dev = host->host_queue; dev != NULL; dev = dev->next) {
u_long arg[2], id;
diff -Nru a/drivers/scsi/pcmcia/qlogic_stub.c b/drivers/scsi/pcmcia/qlogic_stub.c
--- a/drivers/scsi/pcmcia/qlogic_stub.c Wed Oct 16 22:51:48 2002
+++ b/drivers/scsi/pcmcia/qlogic_stub.c Wed Oct 16 22:51:48 2002
@@ -281,7 +281,8 @@

tail = &link->dev;
info->ndev = 0;
- for (host = scsi_hostlist; host; host = host->next)
+ for (host = scsi_host_get_next(NULL); host;
+ host = scsi_host_get_next(host))
if (host->hostt == &driver_template)
for (dev = host->host_queue; dev; dev = dev->next) {
u_long arg[2], id;

2002-10-17 12:06:08

by Mike Anderson

[permalink] [raw]
Subject: [PATCH] scsi host cleanup 1/3 (base) (corrected)

I had diffed against a tree missing a compile fix this is the correct base
patch.

This is a resend of my previous patch clean ups to the scsi_host lists.

* Made function naming consistent with rest of SCSI
* Corrected a problem with driverfs registration to early. Also
changed from put_device to device_unregister.
* Fixed a regression in my previous patch that the scsi_host
list was not sorted by host number. When we get some device
naming this hack can be removed.
* Switch scsi host template, name, host lists to struct
list_head's.
* Moved all scsi_host related register / unregister functions
into hosts.c
* Added list accessor interface and created a function similar
to driverfs bus_for_each_dev.

The full patch is available at:
http://www-124.ibm.com/storageio/patches/2.5/scsi-host

-andmike
--
Michael Anderson
[email protected]

hosts.c | 890 +++++++++++++++++++++++++++++++++++++++++++++++++---------------
hosts.h | 97 +++---
2 files changed, 734 insertions(+), 253 deletions(-)
------

diff -Nru a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
--- a/drivers/scsi/hosts.c Thu Oct 17 00:53:47 2002
+++ b/drivers/scsi/hosts.c Thu Oct 17 00:53:47 2002
@@ -15,12 +15,15 @@
* Updated to reflect the new initialization scheme for the higher
* level of scsi drivers (sd/sr/st)
* September 17, 2000 Torben Mathiasen <[email protected]>
+ *
+ * Restructured scsi_host lists and associated functions.
+ * September 04, 2002 Mike Anderson ([email protected])
*/


/*
* This file contains the medium level SCSI
- * host interface initialization, as well as the scsi_hosts array of SCSI
+ * host interface initialization, as well as the scsi_hosts list of SCSI
* hosts currently present in the system.
*/

@@ -31,232 +34,711 @@
#include <linux/mm.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/smp_lock.h>

#define __KERNEL_SYSCALLS__

#include <linux/unistd.h>
+#include <asm/dma.h>

#include "scsi.h"
#include "hosts.h"

-/*
-static const char RCSid[] = "$Header: /vger/u4/cvs/linux/drivers/scsi/hosts.c,v 1.20 1996/12/12 19:18:32 davem Exp $";
-*/
+LIST_HEAD(scsi_host_tmpl_list);
+LIST_HEAD(scsi_host_hn_list);

-/*
- * The scsi host entries should be in the order you wish the
- * cards to be detected. A driver may appear more than once IFF
- * it can deal with being detected (and therefore initialized)
- * with more than one simultaneous host number, can handle being
- * reentrant, etc.
+LIST_HEAD(scsi_host_list);
+spinlock_t scsi_host_list_lock = SPIN_LOCK_UNLOCKED;
+
+struct Scsi_Device_Template * scsi_devicelist;
+
+static int scsi_host_next_hn; /* host_no for next new host */
+static int scsi_hosts_registered; /* cnt of registered scsi hosts */
+
+/**
+ * scsi_tp_for_each_host - call function for each scsi host off a template
+ * @shost_tp: a pointer to a scsi host template
+ * @callback: a pointer to callback function
*
- * They may appear in any order, as each SCSI host is told which host
- * number it is during detection.
- */
+ * Return value:
+ * 0 on Success / 1 on Failure
+ **/
+int scsi_tp_for_each_host(Scsi_Host_Template *shost_tp, int
+ (*callback)(struct Scsi_Host *shost))
+{
+ struct list_head *lh, *lh_sf;
+ struct Scsi_Host *shost;

-/*
- * When figure is run, we don't want to link to any object code. Since
- * the macro for each host will contain function pointers, we cannot
- * use it and instead must use a "blank" that does no such
- * idiocy.
- */
+ spin_lock(&scsi_host_list_lock);

-Scsi_Host_Template * scsi_hosts;
+ list_for_each_safe(lh, lh_sf, &scsi_host_list) {
+ shost = list_entry(lh, struct Scsi_Host, sh_list);
+ if (shost->hostt == shost_tp) {
+ spin_unlock(&scsi_host_list_lock);
+ callback(shost);
+ spin_lock(&scsi_host_list_lock);
+ }
+ }

+ spin_unlock(&scsi_host_list_lock);

-/*
- * Our semaphores and timeout counters, where size depends on
- * MAX_SCSI_HOSTS here.
- */
+ return 0;
+}

-Scsi_Host_Name * scsi_host_no_list;
-struct Scsi_Host * scsi_hostlist;
-struct Scsi_Device_Template * scsi_devicelist;
+/**
+ * scsi_host_generic_release - default release function for hosts
+ * @shost:
+ *
+ * Description:
+ * This is the default case for the release function. It should do
+ * the right thing for most correctly written host adapters.
+ **/
+static void scsi_host_generic_release(struct Scsi_Host *shost)
+{
+ if (shost->irq)
+ free_irq(shost->irq, NULL);
+ if (shost->dma_channel != 0xff)
+ free_dma(shost->dma_channel);
+ if (shost->io_port && shost->n_io_port)
+ release_region(shost->io_port, shost->n_io_port);
+}

-int max_scsi_hosts;
-int next_scsi_host;
+/**
+ * scsi_host_chk_and_release - check a scsi host for release and release
+ * @shost: a pointer to a scsi host to release
+ *
+ * Return value:
+ * 0 on Success / 1 on Failure
+ **/
+int scsi_host_chk_and_release(struct Scsi_Host *shost)
+{
+ int pcount;
+ Scsi_Device *sdev;
+ struct Scsi_Device_Template *sdev_tp;
+ Scsi_Cmnd *scmd;
+
+ /*
+ * Current policy is all shosts go away on unregister.
+ */
+ if (shost->hostt->module && GET_USE_COUNT(shost->hostt->module))
+ return 1;
+
+ /*
+ * FIXME Do ref counting. We force all of the devices offline to
+ * help prevent race conditions where other hosts/processors could
+ * try and get in and queue a command.
+ */
+ for (sdev = shost->host_queue; sdev; sdev = sdev->next)
+ sdev->online = FALSE;
+
+ for (sdev = shost->host_queue; sdev; sdev = sdev->next) {
+ /*
+ * Loop over all of the commands associated with the
+ * device. If any of them are busy, then set the state
+ * back to inactive and bail.
+ */
+ for (scmd = sdev->device_queue; scmd; scmd = scmd->next) {
+ if (scmd->request && scmd->request->rq_status !=
+ RQ_INACTIVE) {
+ printk(KERN_ERR "SCSI device not inactive"
+ "- rq_status=%d, target=%d, pid=%ld,"
+ "state=%d, owner=%d.\n",
+ scmd->request->rq_status,
+ scmd->target, scmd->pid,
+ scmd->state, scmd->owner);
+ for (sdev = shost->host_queue; sdev;
+ sdev = sdev->next) {
+ for (scmd = sdev->device_queue; scmd;
+ scmd = scmd->next)
+ if (scmd->request->rq_status ==
+ RQ_SCSI_DISCONNECTING)
+ scmd->request->rq_status = RQ_INACTIVE;
+ }
+ printk(KERN_ERR "Device busy???\n");
+ return 1;
+ }
+ /*
+ * No, this device is really free. Mark it as such, and
+ * continue on.
+ */
+ scmd->state = SCSI_STATE_DISCONNECTING;
+ if (scmd->request)
+ scmd->request->rq_status =
+ RQ_SCSI_DISCONNECTING; /* Mark as
+ busy */
+ }
+ }

-void
-scsi_unregister(struct Scsi_Host * sh){
- struct Scsi_Host * shpnt;
- Scsi_Host_Name *shn;
-
- if(scsi_hostlist == sh)
- scsi_hostlist = sh->next;
- else {
- shpnt = scsi_hostlist;
- while(shpnt->next != sh) shpnt = shpnt->next;
- shpnt->next = shpnt->next->next;
- }
-
- /*
- * We have to unregister the host from the scsi_host_no_list as well.
- * Decide by the host_no not by the name because most host drivers are
- * able to handle more than one adapters from the same kind (or family).
- */
- for ( shn=scsi_host_no_list; shn && (sh->host_no != shn->host_no);
- shn=shn->next);
- if (shn) shn->host_registered = 0;
- /* else {} : This should not happen, we should panic here... */
-
- /* If we are removing the last host registered, it is safe to reuse
- * its host number (this avoids "holes" at boot time) (DB)
- * It is also safe to reuse those of numbers directly below which have
- * been released earlier (to avoid some holes in numbering).
- */
- if(sh->host_no == max_scsi_hosts - 1) {
- while(--max_scsi_hosts >= next_scsi_host) {
- shpnt = scsi_hostlist;
- while(shpnt && shpnt->host_no != max_scsi_hosts - 1)
- shpnt = shpnt->next;
- if(shpnt)
- break;
- }
- }
- next_scsi_host--;
- kfree((char *) sh);
-}
-
-/* We call this when we come across a new host adapter. We only do this
- * once we are 100% sure that we want to use this host adapter - it is a
- * pain to reverse this, so we try to avoid it
- */
+ /*
+ * Next we detach the high level drivers from the Scsi_Device
+ * structures
+ */
+ for (sdev = shost->host_queue; sdev; sdev = sdev->next) {
+ for (sdev_tp = scsi_devicelist; sdev_tp;
+ sdev_tp = sdev_tp->next)
+ if (sdev_tp->detach)
+ (*sdev_tp->detach) (sdev);
+
+ /* If something still attached, punt */
+ if (sdev->attached) {
+ printk(KERN_ERR "Attached usage count = %d\n",
+ sdev->attached);
+ return 1;
+ }
+
+ if (shost->hostt->slave_detach)
+ (*shost->hostt->slave_detach) (sdev);
+
+ devfs_unregister(sdev->de);
+ device_unregister(&sdev->sdev_driverfs_dev);
+ }
+
+ /* Next we free up the Scsi_Cmnd structures for this host */
+
+ for (sdev = shost->host_queue; sdev;
+ sdev = shost->host_queue) {
+ scsi_release_commandblocks(sdev);
+ blk_cleanup_queue(&sdev->request_queue);
+ /* Next free up the Scsi_Device structures for this host */
+ shost->host_queue = sdev->next;
+ if (sdev->inquiry)
+ kfree(sdev->inquiry);
+ kfree(sdev);
+ }
+
+ /* Remove the instance of the individual hosts */
+ pcount = scsi_hosts_registered;
+ if (shost->hostt->release)
+ (*shost->hostt->release) (shost);
+ else {
+ scsi_host_generic_release(shost);
+ }
+
+ if (pcount == scsi_hosts_registered)
+ scsi_unregister(shost);
+
+ return 0;
+}
+
+/**
+ * scsi_unregister - unregister a scsi host
+ * @shost: scsi host to be unregistered
+ **/
+void scsi_unregister(struct Scsi_Host *shost)
+{
+ struct list_head *lh;
+ Scsi_Host_Name *shost_name;
+
+ /* Remove shost from scsi_host_list */
+ spin_lock(&scsi_host_list_lock);
+ list_del(&shost->sh_list);
+ spin_unlock(&scsi_host_list_lock);
+
+ /* Unregister from scsi_host_hn_list */
+ list_for_each(lh, &scsi_host_hn_list) {
+ shost_name = list_entry(lh, Scsi_Host_Name, shn_list);
+ if (shost->host_no == shost_name->host_no)
+ shost_name->host_registered = 0;
+ }
+
+ /*
+ * Next, kill the kernel error recovery thread for this host.
+ */
+ if (shost->ehandler) {
+ DECLARE_MUTEX_LOCKED(sem);
+ shost->eh_notify = &sem;
+ send_sig(SIGHUP, shost->ehandler, 1);
+ down(&sem);
+ shost->eh_notify = NULL;
+ }
+
+ scsi_hosts_registered--;
+ shost->hostt->present--;
+
+ /* Cleanup proc and driverfs */
+#ifdef CONFIG_PROC_FS
+ scsi_proc_host_rm(shost);
+ if (!shost->hostt->present)
+ remove_proc_entry(shost->hostt->proc_name, proc_scsi);
+#endif
+ device_unregister(&shost->host_driverfs_dev);
+
+ kfree(shost);
+}
+
+/**
+ * scsi_host_hn_add - allocate and add new Scsi_Host_Name
+ * @name: String to store in name field
+ *
+ * Return value:
+ * Pointer to a new Scsi_Host_Name
+ **/
+Scsi_Host_Name *scsi_host_hn_add(char *name)
+{
+ Scsi_Host_Name *shost_name;
+ int len;
+
+ len = strlen(name);
+ shost_name = kmalloc(sizeof(*shost_name), GFP_KERNEL);
+ if (!shost_name) {
+ printk(KERN_ERR "%s: out of memory at line %d.\n",
+ __FUNCTION__, __LINE__);
+ return NULL;
+ }
+ shost_name->name = kmalloc(len + 1, GFP_KERNEL);
+ if (!shost_name->name) {
+ kfree(shost_name);
+ printk(KERN_ERR "%s: out of memory at line %d.\n",
+ __FUNCTION__, __LINE__);
+ return NULL;
+ }
+
+ if (len)
+ strncpy(shost_name->name, name, len);
+ shost_name->name[len] = 0;
+ shost_name->host_no = scsi_host_next_hn++;
+ shost_name->host_registered = 0;
+ list_add_tail(&shost_name->shn_list, &scsi_host_hn_list);
+
+ return shost_name;
+}
+
+/**
+ * scsi_register - register a scsi host adapter instance.
+ * @shost_tp: pointer to scsi host template
+ * @xtr_bytes: extra bytes to allocate for driver
+ *
+ * Note:
+ * We call this when we come across a new host adapter. We only do
+ * this once we are 100% sure that we want to use this host adapter -
+ * it is a pain to reverse this, so we try to avoid it
+ *
+ * Return value:
+ * Pointer to a new Scsi_Host
+ **/
extern int blk_nohighio;
-struct Scsi_Host * scsi_register(Scsi_Host_Template * tpnt, int j)
+struct Scsi_Host * scsi_register(Scsi_Host_Template *shost_tp, int xtr_bytes)
{
- struct Scsi_Host * retval, *shpnt, *o_shp;
- Scsi_Host_Name *shn, *shn2;
- int flag_new = 1;
- const char * hname;
- size_t hname_len;
- retval = (struct Scsi_Host *)kmalloc(sizeof(struct Scsi_Host) + j,
- (tpnt->unchecked_isa_dma && j ?
- GFP_DMA : 0) | GFP_ATOMIC);
- if(retval == NULL)
- {
- printk("scsi: out of memory in scsi_register.\n");
- return NULL;
- }
-
- memset(retval, 0, sizeof(struct Scsi_Host) + j);
-
- /* trying to find a reserved entry (host_no) */
- hname = (tpnt->proc_name) ? tpnt->proc_name : "";
- hname_len = strlen(hname);
- for (shn = scsi_host_no_list;shn;shn = shn->next) {
- if (!(shn->host_registered) &&
- (hname_len > 0) && (0 == strncmp(hname, shn->name, hname_len))) {
- flag_new = 0;
- retval->host_no = shn->host_no;
- shn->host_registered = 1;
- break;
- }
- }
- spin_lock_init(&retval->default_lock);
- scsi_assign_lock(retval, &retval->default_lock);
- atomic_set(&retval->host_active,0);
- retval->host_busy = 0;
- retval->host_failed = 0;
- if (flag_new) {
- shn = (Scsi_Host_Name *) kmalloc(sizeof(Scsi_Host_Name), GFP_ATOMIC);
- if (!shn) {
- kfree(retval);
- printk(KERN_ERR "scsi: out of memory(2) in scsi_register.\n");
- return NULL;
- }
- shn->name = kmalloc(hname_len + 1, GFP_ATOMIC);
- if (hname_len > 0)
- strncpy(shn->name, hname, hname_len);
- shn->name[hname_len] = 0;
- shn->host_no = max_scsi_hosts++;
- shn->host_registered = 1;
- shn->next = NULL;
- if (scsi_host_no_list) {
- for (shn2 = scsi_host_no_list;shn2->next;shn2 = shn2->next)
- ;
- shn2->next = shn;
- }
- else
- scsi_host_no_list = shn;
- retval->host_no = shn->host_no;
- }
- next_scsi_host++;
- retval->host_queue = NULL;
- init_waitqueue_head(&retval->host_wait);
- retval->resetting = 0;
- retval->last_reset = 0;
- retval->irq = 0;
- retval->dma_channel = 0xff;
-
- /* These three are default values which can be overridden */
- retval->max_channel = 0;
- retval->max_id = 8;
- retval->max_lun = 8;
-
- /*
- * All drivers right now should be able to handle 12 byte commands.
- * Every so often there are requests for 16 byte commands, but individual
- * low-level drivers need to certify that they actually do something
- * sensible with such commands.
- */
- retval->max_cmd_len = 12;
-
- retval->unique_id = 0;
- retval->io_port = 0;
- retval->hostt = tpnt;
- retval->next = NULL;
- retval->in_recovery = 0;
- retval->ehandler = NULL; /* Initial value until the thing starts up. */
- retval->eh_notify = NULL; /* Who we notify when we exit. */
+ struct Scsi_Host *shost, *shost_scr;
+ Scsi_Host_Name *shost_name = NULL;
+ Scsi_Host_Name *shn = NULL;
+ char *hname;
+ size_t hname_len;
+ struct list_head *lh;
+ int gfp_mask;
+ DECLARE_MUTEX_LOCKED(sem);
+
+ gfp_mask = GFP_KERNEL;
+ if (shost_tp->unchecked_isa_dma && xtr_bytes)
+ gfp_mask |= __GFP_DMA;
+
+ shost = kmalloc(sizeof(struct Scsi_Host) + xtr_bytes, gfp_mask);
+ if (!shost) {
+ printk(KERN_ERR "%s: out of memory.\n", __FUNCTION__);
+ return NULL;
+ }

- retval->max_host_blocked = tpnt->max_host_blocked ? tpnt->max_host_blocked : SCSI_DEFAULT_HOST_BLOCKED;
+ memset(shost, 0, sizeof(struct Scsi_Host) + xtr_bytes);

- retval->host_blocked = 0;
- retval->host_self_blocked = FALSE;
+ /*
+ * Determine host number. Check reserved first before allocating
+ * new one
+ */
+ hname = (shost_tp->proc_name) ? shost_tp->proc_name : "";
+ hname_len = strlen(hname);
+
+ if (hname_len)
+ list_for_each(lh, &scsi_host_hn_list) {
+ shn = list_entry(lh, Scsi_Host_Name, shn_list);
+ if (!(shn->host_registered) &&
+ !strncmp(hname, shn->name, hname_len)) {
+ shost_name = shn;
+ break;
+ }
+ }
+
+ if (!shost_name) {
+ shost_name = scsi_host_hn_add(hname);
+ if (!shost_name) {
+ kfree(shost);
+ return NULL;
+ }
+ }
+
+ shost->host_no = shost_name->host_no;
+ shost_name->host_registered = 1;
+ scsi_hosts_registered++;
+
+ spin_lock_init(&shost->default_lock);
+ scsi_assign_lock(shost, &shost->default_lock);
+ atomic_set(&shost->host_active,0);
+
+ init_waitqueue_head(&shost->host_wait);
+ shost->dma_channel = 0xff;
+
+ /* These three are default values which can be overridden */
+ shost->max_channel = 0;
+ shost->max_id = 8;
+ shost->max_lun = 8;
+
+ /*
+ * All drivers right now should be able to handle 12 byte
+ * commands. Every so often there are requests for 16 byte
+ * commands, but individual low-level drivers need to certify that
+ * they actually do something sensible with such commands.
+ */
+ shost->max_cmd_len = 12;
+ shost->hostt = shost_tp;
+ shost->host_blocked = FALSE;
+ shost->host_self_blocked = FALSE;

#ifdef DEBUG
- printk("Register %x %x: %d\n", (int)retval, (int)retval->hostt, j);
+ printk("%s: %x %x: %d\n", __FUNCTION_ (int)shost,
+ (int)shost->hostt, xtr_bytes);
#endif

- /* The next six are the default values which can be overridden
- * if need be */
- retval->this_id = tpnt->this_id;
- retval->can_queue = tpnt->can_queue;
- retval->sg_tablesize = tpnt->sg_tablesize;
- retval->cmd_per_lun = tpnt->cmd_per_lun;
- retval->unchecked_isa_dma = tpnt->unchecked_isa_dma;
- retval->use_clustering = tpnt->use_clustering;
- if (!blk_nohighio)
- retval->highmem_io = tpnt->highmem_io;
-
- retval->max_sectors = tpnt->max_sectors;
- retval->use_blk_tcq = tpnt->use_blk_tcq;
-
- if(!scsi_hostlist)
- scsi_hostlist = retval;
- else {
- shpnt = scsi_hostlist;
- if (retval->host_no < shpnt->host_no) {
- retval->next = shpnt;
- wmb(); /* want all to see these writes in this order */
- scsi_hostlist = retval;
+ /*
+ * The next six are the default values which can be overridden if
+ * need be
+ */
+ shost->this_id = shost_tp->this_id;
+ shost->can_queue = shost_tp->can_queue;
+ shost->sg_tablesize = shost_tp->sg_tablesize;
+ shost->cmd_per_lun = shost_tp->cmd_per_lun;
+ shost->unchecked_isa_dma = shost_tp->unchecked_isa_dma;
+ shost->use_clustering = shost_tp->use_clustering;
+ if (!blk_nohighio)
+ shost->highmem_io = shost_tp->highmem_io;
+
+ shost->max_sectors = shost_tp->max_sectors;
+ shost->use_blk_tcq = shost_tp->use_blk_tcq;
+
+ spin_lock(&scsi_host_list_lock);
+ /*
+ * FIXME When device naming is complete remove this step that
+ * orders the scsi_host_list by host number and just do a
+ * list_add_tail.
+ */
+ list_for_each(lh, &scsi_host_list) {
+ shost_scr = list_entry(lh, struct Scsi_Host, sh_list);
+ if (shost->host_no < shost_scr->host_no) {
+ __list_add(&shost->sh_list, shost_scr->sh_list.prev,
+ &shost_scr->sh_list);
+ goto found;
+ }
+ }
+ list_add_tail(&shost->sh_list, &scsi_host_list);
+found:
+ spin_unlock(&scsi_host_list_lock);
+
+#ifdef CONFIG_PROC_FS
+ /* Add the new driver to /proc/scsi if not already there */
+ if (!shost_tp->proc_dir)
+ scsi_proc_host_mkdir(shost_tp);
+ scsi_proc_host_add(shost);
+#endif
+
+ strncpy(shost->host_driverfs_dev.name, shost_tp->proc_name,
+ DEVICE_NAME_SIZE-1);
+ sprintf(shost->host_driverfs_dev.bus_id, "scsi%d",
+ shost->host_no);
+
+ shost->eh_notify = &sem;
+ kernel_thread((int (*)(void *)) scsi_error_handler, (void *) shost, 0);
+ /*
+ * Now wait for the kernel error thread to initialize itself
+ * as it might be needed when we scan the bus.
+ */
+ down(&sem);
+ shost->eh_notify = NULL;
+
+ shost->hostt->present++;
+
+ return shost;
+}
+
+
+/**
+ * scsi_register_host - register a low level host driver
+ * @shost_tp: pointer to a scsi host driver template
+ *
+ * Return value:
+ * 0 on Success / 1 on Failure.
+ **/
+int scsi_register_host(Scsi_Host_Template *shost_tp)
+{
+ int cur_cnt;
+ Scsi_Device *sdev;
+ struct Scsi_Device_Template *sdev_tp;
+ struct list_head *lh;
+ struct Scsi_Host *shost;
+
+ /*
+ * Check no detect routine.
+ */
+ if (!shost_tp->detect)
+ return 1;
+
+ /* If max_sectors isn't set, default to max */
+ if (!shost_tp->max_sectors)
+ shost_tp->max_sectors = 1024;
+
+ cur_cnt = scsi_hosts_registered;
+
+ MOD_INC_USE_COUNT;
+
+ /*
+ * The detect routine must carefully spinunlock/spinlock if it
+ * enables interrupts, since all interrupt handlers do spinlock as
+ * well.
+ */
+
+ /*
+ * detect should do its own locking
+ * FIXME present is now set is scsi_register which breaks manual
+ * registration code below.
+ */
+ shost_tp->detect(shost_tp);
+
+ if (shost_tp->present) {
+ /*
+ * FIXME Who needs manual registration and why???
+ */
+ if (cur_cnt == scsi_hosts_registered) {
+ if (shost_tp->present > 1) {
+ printk(KERN_ERR "scsi: Failure to register"
+ "low-level scsi driver");
+ scsi_unregister_host(shost_tp);
+ return 1;
+ }
+ /*
+ * The low-level driver failed to register a driver.
+ * We can do this now.
+ */
+ if(scsi_register(shost_tp, 0)==NULL) {
+ printk(KERN_ERR "scsi: register failed.\n");
+ scsi_unregister_host(shost_tp);
+ return 1;
+ }
+ }
+
+ list_add_tail(&shost_tp->shtp_list, &scsi_host_tmpl_list);
+
+ /* The next step is to call scan_scsis here. This generates the
+ * Scsi_Devices entries
+ */
+ list_for_each(lh, &scsi_host_list) {
+ shost = list_entry(lh, struct Scsi_Host, sh_list);
+ if (shost->hostt == shost_tp) {
+ const char *dm_name;
+ if (shost_tp->info) {
+ dm_name = shost_tp->info(shost);
+ } else {
+ dm_name = shost_tp->name;
+ }
+ printk(KERN_INFO "scsi%d : %s\n",
+ shost->host_no, dm_name);
+
+ /* first register parent with driverfs */
+ device_register(&shost->host_driverfs_dev);
+ scan_scsis(shost, 0, 0, 0, 0);
+ }
+ }
+
+ for (sdev_tp = scsi_devicelist; sdev_tp;
+ sdev_tp = sdev_tp->next) {
+ if (sdev_tp->init && sdev_tp->dev_noticed)
+ (*sdev_tp->init) ();
+ }
+
+ /*
+ * Next we create the Scsi_Cmnd structures for this host
+ */
+ list_for_each(lh, &scsi_host_list) {
+ shost = list_entry(lh, struct Scsi_Host, sh_list);
+ for (sdev = shost->host_queue; sdev; sdev = sdev->next)
+ if (sdev->host->hostt == shost_tp) {
+ for (sdev_tp = scsi_devicelist;
+ sdev_tp;
+ sdev_tp = sdev_tp->next)
+ if (sdev_tp->attach)
+ (*sdev_tp->attach) (sdev);
+ if (sdev->attached) {
+ scsi_build_commandblocks(sdev);
+ if (sdev->current_queue_depth == 0)
+ goto out_of_space;
+ }
+ }
+ }
+
+ /* This does any final handling that is required. */
+ for (sdev_tp = scsi_devicelist; sdev_tp;
+ sdev_tp = sdev_tp->next) {
+ if (sdev_tp->finish && sdev_tp->nr_dev) {
+ (*sdev_tp->finish) ();
+ }
+ }
+ }
+
+ return 0;
+
+out_of_space:
+ scsi_unregister_host(shost_tp); /* easiest way to clean up?? */
+ return 1;
+}
+
+/**
+ * scsi_unregister_host - unregister a low level host adapter driver
+ * @shost_tp: scsi host template to unregister.
+ *
+ * Description:
+ * Similarly, this entry point should be called by a loadable module
+ * if it is trying to remove a low level scsi driver from the system.
+ *
+ * Return value:
+ * 0 on Success / 1 on Failure
+ *
+ * Notes:
+ * rmmod does not care what we return here the module will be
+ * removed.
+ **/
+int scsi_unregister_host(Scsi_Host_Template *shost_tp)
+{
+ int pcount;
+
+ /* get the big kernel lock, so we don't race with open() */
+ lock_kernel();
+
+ pcount = scsi_hosts_registered;
+
+ scsi_tp_for_each_host(shost_tp, scsi_host_chk_and_release);
+
+ if (pcount != scsi_hosts_registered)
+ printk(KERN_INFO "scsi : %d host%s left.\n", scsi_hosts_registered,
+ (scsi_hosts_registered == 1) ? "" : "s");
+
+ /*
+ * Remove it from the list if all
+ * hosts were successfully removed (ie preset == 0)
+ */
+ if (!shost_tp->present) {
+ list_del(&shost_tp->shtp_list);
+ }
+
+ MOD_DEC_USE_COUNT;
+
+ unlock_kernel();
+ return 0;
+
+}
+
+/**
+ * *scsi_host_get_next - get scsi host and inc ref count
+ * @shost: pointer to a Scsi_Host or NULL to start.
+ *
+ * Return value:
+ * A pointer to next Scsi_Host in list or NULL.
+ **/
+struct Scsi_Host *scsi_host_get_next(struct Scsi_Host *shost)
+{
+ struct list_head *lh = NULL;
+
+ spin_lock(&scsi_host_list_lock);
+ if (shost) {
+ /* XXX Dec ref on cur shost */
+ lh = shost->sh_list.next;
+ } else {
+ lh = scsi_host_list.next;
+ }
+
+ if (lh == &scsi_host_list) {
+ shost = (struct Scsi_Host *)NULL;
+ goto done;
+ }
+
+ shost = list_entry(lh, struct Scsi_Host, sh_list);
+ /* XXX Inc ref count */
+
+done:
+ spin_unlock(&scsi_host_list_lock);
+ return shost;
+}
+
+/**
+ * scsi_host_hn_get - get a Scsi_Host by host no and inc ref count
+ * @host_no: host number to locate
+ *
+ * Return value:
+ * A pointer to located Scsi_Host or NULL.
+ **/
+struct Scsi_Host *scsi_host_hn_get(unsigned short host_no)
+{
+ struct list_head *lh;
+ struct Scsi_Host *shost;
+
+ spin_lock(&scsi_host_list_lock);
+ list_for_each(lh, &scsi_host_list) {
+ shost = list_entry(lh, struct Scsi_Host, sh_list);
+ if (shost->host_no == host_no) {
+ /* XXX Inc ref count */
+ goto done;
+ }
+ }
+
+ shost = (struct Scsi_Host *)NULL;
+done:
+ spin_unlock(&scsi_host_list_lock);
+ return shost;
+}
+
+/**
+ * *scsi_host_put - dec a Scsi_Host ref count
+ * @shost: Pointer to Scsi_Host to dec.
+ **/
+void scsi_host_put(struct Scsi_Host *shost)
+{
+
+ /* XXX Get list lock */
+ /* XXX dec ref count */
+ /* XXX Release list lock */
+ return;
+}
+
+/**
+ * scsi_host_hn_init - init scsi host number list from string
+ * @shost_hn: string of scsi host driver names.
+ **/
+void __init scsi_host_hn_init(char *shost_hn)
+{
+ char *temp = shost_hn;
+
+ while (temp) {
+ while (*temp && (*temp != ':') && (*temp != ','))
+ temp++;
+ if (!*temp)
+ temp = NULL;
+ else
+ *temp++ = 0;
+ (void)scsi_host_hn_add(shost_hn);
+ shost_hn = temp;
+ }
+}
+
+/**
+ * scsi_host_no_release - free all entries in scsi host number list
+ **/
+void __exit scsi_host_hn_release()
+{
+ struct list_head *lh, *next;
+ Scsi_Host_Name *shn;
+
+ list_for_each_safe(lh, next, &scsi_host_hn_list) {
+ shn = list_entry(lh, Scsi_Host_Name, shn_list);
+ if (shn->name)
+ kfree(shn->name);
+ kfree(shn);
}
- else {
- for (o_shp = shpnt, shpnt = shpnt->next; shpnt;
- o_shp = shpnt, shpnt = shpnt->next) {
- if (retval->host_no < shpnt->host_no) {
- retval->next = shpnt;
- wmb();
- o_shp->next = retval;
- break;
- }
- }
- if (! shpnt)
- o_shp->next = retval;
- }
- }
-
- return retval;
}

void scsi_host_busy_inc(struct Scsi_Host *shost, Scsi_Device *sdev)
@@ -279,8 +761,7 @@
if (shost->in_recovery && (shost->host_busy == shost->host_failed)) {
up(shost->eh_wait);
SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler"
- "thread (%d)\n",
- atomic_read(&shost->eh_wait->count)));
+ " thread\n"));
}
spin_unlock_irqrestore(shost->host_lock, flags);
}
@@ -295,8 +776,7 @@
if (shost->host_busy == shost->host_failed) {
up(shost->eh_wait);
SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler"
- "thread (%d)\n",
- atomic_read(&shost->eh_wait->count)));
+ " thread\n"));
}
spin_unlock_irqrestore(shost->host_lock, flags);
}
diff -Nru a/drivers/scsi/hosts.h b/drivers/scsi/hosts.h
--- a/drivers/scsi/hosts.h Thu Oct 17 00:53:47 2002
+++ b/drivers/scsi/hosts.h Thu Oct 17 00:53:47 2002
@@ -16,15 +16,14 @@
* of the same type.
*
* Jiffies wrap fixes (host->resetting), 3 Dec 1998 Andrea Arcangeli
+ *
+ * Restructured scsi_host lists and associated functions.
+ * September 04, 2002 Mike Anderson ([email protected])
*/

#ifndef _HOSTS_H
#define _HOSTS_H

-/*
- $Header: /vger/u4/cvs/linux/drivers/scsi/hosts.h,v 1.6 1997/01/19 23:07:13 davem Exp $
-*/
-
#include <linux/config.h>
#include <linux/proc_fs.h>
#include <linux/pci.h>
@@ -58,8 +57,7 @@
typedef struct SHT
{

- /* Used with loadable modules so we can construct a linked list. */
- struct SHT * next;
+ struct list_head shtp_list;

/* Used with loadable modules so that we know when it is safe to unload */
struct module * module;
@@ -374,7 +372,7 @@
* This information is private to the scsi mid-layer. Wrapping it in a
* struct private is a way of marking it in a sort of C++ type of way.
*/
- struct Scsi_Host * next;
+ struct list_head sh_list;
Scsi_Device * host_queue;
struct list_head all_scsi_hosts;
struct list_head my_devices;
@@ -510,28 +508,26 @@
* thing. This physical pseudo-device isn't real and won't be available
* from any high-level drivers.
*/
-extern void scsi_free_host_dev(Scsi_Device * SDpnt);
-extern Scsi_Device * scsi_get_host_dev(struct Scsi_Host * SHpnt);
+extern void scsi_free_host_dev(Scsi_Device *);
+extern Scsi_Device * scsi_get_host_dev(struct Scsi_Host *);

-extern void scsi_unblock_requests(struct Scsi_Host * SHpnt);
-extern void scsi_block_requests(struct Scsi_Host * SHpnt);
-extern void scsi_report_bus_reset(struct Scsi_Host * SHpnt, int channel);
+extern void scsi_unblock_requests(struct Scsi_Host *);
+extern void scsi_block_requests(struct Scsi_Host *);
+extern void scsi_report_bus_reset(struct Scsi_Host *, int);

typedef struct SHN
- {
- struct SHN * next;
- char * name;
- unsigned short host_no;
- unsigned short host_registered;
- } Scsi_Host_Name;
+{
+ struct list_head shn_list;
+ char *name;
+ unsigned short host_no;
+ unsigned short host_registered;
+} Scsi_Host_Name;

-extern Scsi_Host_Name * scsi_host_no_list;
-extern struct Scsi_Host * scsi_hostlist;
extern struct Scsi_Device_Template * scsi_devicelist;

-extern Scsi_Host_Template * scsi_hosts;
-
-extern void build_proc_dir_entries(Scsi_Host_Template *);
+extern void scsi_proc_host_mkdir(Scsi_Host_Template *);
+extern void scsi_proc_host_add(struct Scsi_Host *);
+extern void scsi_proc_host_rm(struct Scsi_Host *);

/*
* scsi_init initializes the scsi hosts.
@@ -540,34 +536,33 @@
extern int next_scsi_host;

unsigned int scsi_init(void);
-extern struct Scsi_Host * scsi_register(Scsi_Host_Template *, int j);
-extern void scsi_unregister(struct Scsi_Host * i);
-extern void scsi_register_blocked_host(struct Scsi_Host * SHpnt);
-extern void scsi_deregister_blocked_host(struct Scsi_Host * SHpnt);
+extern struct Scsi_Host * scsi_register(Scsi_Host_Template *, int);
+extern void scsi_unregister(struct Scsi_Host *);
+extern void scsi_register_blocked_host(struct Scsi_Host *);
+extern void scsi_deregister_blocked_host(struct Scsi_Host *);

-static inline void scsi_assign_lock(struct Scsi_Host *host, spinlock_t *lock)
+static inline void scsi_assign_lock(struct Scsi_Host *shost, spinlock_t *lock)
{
- host->host_lock = lock;
+ shost->host_lock = lock;
}

-static inline void scsi_set_pci_device(struct Scsi_Host *SHpnt,
+static inline void scsi_set_pci_device(struct Scsi_Host *shost,
struct pci_dev *pdev)
{
- SHpnt->pci_dev = pdev;
- SHpnt->host_driverfs_dev.parent=&pdev->dev;
+ shost->pci_dev = pdev;
+ shost->host_driverfs_dev.parent=&pdev->dev;
+
+ /* register parent with driverfs */
+ device_register(&shost->host_driverfs_dev);
}


/*
* Prototypes for functions/data in scsi_scan.c
*/
-extern void scan_scsis(struct Scsi_Host *shpnt,
- uint hardcoded,
- uint hchannel,
- uint hid,
- uint hlun);
+extern void scan_scsis(struct Scsi_Host *, uint, uint, uint, uint);

-extern void scsi_mark_host_reset(struct Scsi_Host *Host);
+extern void scsi_mark_host_reset(struct Scsi_Host *);

#define BLANK_HOST {"", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}

@@ -596,7 +591,7 @@
struct device_driver scsi_driverfs_driver;
};

-void scsi_initialize_queue(Scsi_Device * SDpnt, struct Scsi_Host * SHpnt);
+void scsi_initialize_queue(Scsi_Device *, struct Scsi_Host *);


/*
@@ -607,6 +602,12 @@
extern int scsi_register_host(Scsi_Host_Template *);
extern int scsi_unregister_host(Scsi_Host_Template *);

+extern struct Scsi_Host *scsi_host_get_next(struct Scsi_Host *);
+extern struct Scsi_Host *scsi_host_hn_get(unsigned short);
+extern void scsi_host_put(struct Scsi_Host *);
+extern void scsi_host_hn_init(char *);
+extern void scsi_host_hn_release(void);
+
/*
* host_busy inc/dec/test functions
*/
@@ -614,7 +615,6 @@
extern void scsi_host_busy_dec_and_test(struct Scsi_Host *, Scsi_Device *);
extern void scsi_host_failed_inc_and_test(struct Scsi_Host *);

-
/*
* This is an ugly hack. If we expect to be able to load devices at run time,
* we need to leave extra room in some of the data structures. Doing a
@@ -643,21 +643,22 @@

/**
* scsi_find_device - find a device given the host
+ * @shost: SCSI host pointer
* @channel: SCSI channel (zero if only one channel)
* @pun: SCSI target number (physical unit number)
* @lun: SCSI Logical Unit Number
**/
-static inline Scsi_Device *scsi_find_device(struct Scsi_Host *host,
+static inline Scsi_Device *scsi_find_device(struct Scsi_Host *shost,
int channel, int pun, int lun) {
- Scsi_Device *SDpnt;
+ Scsi_Device *sdev;

- for(SDpnt = host->host_queue;
- SDpnt != NULL;
- SDpnt = SDpnt->next)
- if(SDpnt->channel == channel && SDpnt->id == pun
- && SDpnt->lun ==lun)
+ for (sdev = shost->host_queue;
+ sdev != NULL;
+ sdev = sdev->next)
+ if (sdev->channel == channel && sdev->id == pun
+ && sdev->lun ==lun)
break;
- return SDpnt;
+ return sdev;
}

#endif