Commit e16ee618 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by James Bottomley

[PATCH] scsi_device refcounting and list lockdown

  - shost->my_devices is gone and replaced by ->__devices, which is not
    exposed to drivers and locked by the host lock.  Use the exported
    helpers that do proper refcounting to access it.
  - sdev->access_count is gone as a side-effect, the sg interfaces
    to export it now return 1 for a present scsi_device.
  - drivers/scsi/host.h is empty now.
parent 6d54b18c
...@@ -991,7 +991,7 @@ static int sbp2_start_device(struct scsi_id_instance_data *scsi_id) ...@@ -991,7 +991,7 @@ static int sbp2_start_device(struct scsi_id_instance_data *scsi_id)
static void sbp2_remove_device(struct scsi_id_instance_data *scsi_id) static void sbp2_remove_device(struct scsi_id_instance_data *scsi_id)
{ {
struct sbp2scsi_host_info *hi = scsi_id->hi; struct sbp2scsi_host_info *hi = scsi_id->hi;
struct scsi_device *sdev = scsi_find_device(hi->scsi_host, 0, scsi_id->id, 0); struct scsi_device *sdev;
SBP2_DEBUG("sbp2_remove_device"); SBP2_DEBUG("sbp2_remove_device");
...@@ -999,8 +999,13 @@ static void sbp2_remove_device(struct scsi_id_instance_data *scsi_id) ...@@ -999,8 +999,13 @@ static void sbp2_remove_device(struct scsi_id_instance_data *scsi_id)
sbp2scsi_complete_all_commands(scsi_id, DID_NO_CONNECT); sbp2scsi_complete_all_commands(scsi_id, DID_NO_CONNECT);
/* Remove it from the scsi layer now */ /* Remove it from the scsi layer now */
if (sdev) /* XXX(hch): why can't we simply cache the scsi_device
in struct scsi_id_instance_data? */
sdev = scsi_device_lookup(hi->scsi_host, 0, scsi_id->id, 0);
if (sdev) {
scsi_remove_device(sdev); scsi_remove_device(sdev);
scsi_device_put(sdev);
}
sbp2util_remove_command_orb_pool(scsi_id); sbp2util_remove_command_orb_pool(scsi_id);
......
...@@ -1065,7 +1065,7 @@ process_script_interrupt(__u32 dsps, __u32 dsp, Scsi_Cmnd *SCp, ...@@ -1065,7 +1065,7 @@ process_script_interrupt(__u32 dsps, __u32 dsp, Scsi_Cmnd *SCp,
DEBUG(("scsi%d: (%d:%d) RESELECTED!\n", DEBUG(("scsi%d: (%d:%d) RESELECTED!\n",
host->host_no, reselection_id, lun)); host->host_no, reselection_id, lun));
/* clear the reselection indicator */ /* clear the reselection indicator */
SDp = scsi_find_device(host, 0, reselection_id, lun); SDp = __scsi_device_lookup(host, 0, reselection_id, lun);
if(unlikely(SDp == NULL)) { if(unlikely(SDp == NULL)) {
printk(KERN_ERR "scsi%d: (%d:%d) HAS NO device\n", printk(KERN_ERR "scsi%d: (%d:%d) HAS NO device\n",
host->host_no, reselection_id, lun); host->host_no, reselection_id, lun);
...@@ -1498,7 +1498,7 @@ NCR_700_intr(int irq, void *dev_id, struct pt_regs *regs) ...@@ -1498,7 +1498,7 @@ NCR_700_intr(int irq, void *dev_id, struct pt_regs *regs)
host->host_no, SCp, SCp == NULL ? NULL : SCp->host_scribble, dsp, dsp - hostdata->pScript); host->host_no, SCp, SCp == NULL ? NULL : SCp->host_scribble, dsp, dsp - hostdata->pScript);
/* clear all the negotiated parameters */ /* clear all the negotiated parameters */
list_for_each_entry(SDp, &host->my_devices, siblings) __shost_for_each_device(SDp, host)
SDp->hostdata = 0; SDp->hostdata = 0;
/* clear all the slots and their pending commands */ /* clear all the slots and their pending commands */
......
...@@ -815,6 +815,7 @@ static int copy_info(struct info_str *info, char *fmt, ...) ...@@ -815,6 +815,7 @@ static int copy_info(struct info_str *info, char *fmt, ...)
static int esp_host_info(struct NCR_ESP *esp, char *ptr, off_t offset, int len) static int esp_host_info(struct NCR_ESP *esp, char *ptr, off_t offset, int len)
{ {
struct scsi_device *sdev;
struct info_str info; struct info_str info;
int i; int i;
...@@ -867,24 +868,21 @@ static int esp_host_info(struct NCR_ESP *esp, char *ptr, off_t offset, int len) ...@@ -867,24 +868,21 @@ static int esp_host_info(struct NCR_ESP *esp, char *ptr, off_t offset, int len)
/* Now describe the state of each existing target. */ /* Now describe the state of each existing target. */
copy_info(&info, "Target #\tconfig3\t\tSync Capabilities\tDisconnect\n"); copy_info(&info, "Target #\tconfig3\t\tSync Capabilities\tDisconnect\n");
for(i = 0; i < 15; i++) {
if(esp->targets_present & (1 << i)) {
Scsi_Device *SDptr;
struct esp_device *esp_dev;
list_for_each_entry(SDptr, &esp->ehost->my_devices, shost_for_each_device(sdev, esp->ehost) {
siblings) struct esp_device *esp_dev = sdev->hostdata;
if(SDptr->id == i) uint id = sdev->id;
break;
esp_dev = SDptr->hostdata; if (!(esp->targets_present & (1 << id)))
copy_info(&info, "%d\t\t", i); continue;
copy_info(&info, "%08lx\t", esp->config3[i]);
copy_info(&info, "[%02lx,%02lx]\t\t\t", esp_dev->sync_max_offset, copy_info(&info, "%d\t\t", id);
copy_info(&info, "%08lx\t", esp->config3[id]);
copy_info(&info, "[%02lx,%02lx]\t\t\t",
esp_dev->sync_max_offset,
esp_dev->sync_min_period); esp_dev->sync_min_period);
copy_info(&info, "%s\n", esp_dev->disconnect ? "yes" : "no"); copy_info(&info, "%s\n", esp_dev->disconnect ? "yes" : "no");
} }
}
return info.pos > info.offset? info.pos - info.offset : 0; return info.pos > info.offset? info.pos - info.offset : 0;
} }
......
...@@ -2930,7 +2930,7 @@ int acornscsi_proc_info(struct Scsi_Host *instance, char *buffer, char **start, ...@@ -2930,7 +2930,7 @@ int acornscsi_proc_info(struct Scsi_Host *instance, char *buffer, char **start,
p += sprintf(p, "\nAttached devices:\n"); p += sprintf(p, "\nAttached devices:\n");
list_for_each_entry(scd, &instance->my_devices, siblings) { shost_for_each_device(scd, instance) {
p += sprintf(p, "Device/Lun TaggedQ Sync\n"); p += sprintf(p, "Device/Lun TaggedQ Sync\n");
p += sprintf(p, " %d/%d ", scd->id, scd->lun); p += sprintf(p, " %d/%d ", scd->id, scd->lun);
if (scd->tagged_supported) if (scd->tagged_supported)
...@@ -2953,9 +2953,11 @@ int acornscsi_proc_info(struct Scsi_Host *instance, char *buffer, char **start, ...@@ -2953,9 +2953,11 @@ int acornscsi_proc_info(struct Scsi_Host *instance, char *buffer, char **start,
p = buffer; p = buffer;
} }
pos = p - buffer; pos = p - buffer;
if (pos + begin > offset + length) if (pos + begin > offset + length) {
scsi_device_put(scd);
break; break;
} }
}
pos = p - buffer; pos = p - buffer;
......
...@@ -1309,6 +1309,7 @@ static int copy_info(struct info_str *info, char *fmt, ...) ...@@ -1309,6 +1309,7 @@ static int copy_info(struct info_str *info, char *fmt, ...)
static int esp_host_info(struct esp *esp, char *ptr, off_t offset, int len) static int esp_host_info(struct esp *esp, char *ptr, off_t offset, int len)
{ {
struct scsi_device *sdev;
struct info_str info; struct info_str info;
int i; int i;
...@@ -1384,25 +1385,23 @@ static int esp_host_info(struct esp *esp, char *ptr, off_t offset, int len) ...@@ -1384,25 +1385,23 @@ static int esp_host_info(struct esp *esp, char *ptr, off_t offset, int len)
/* Now describe the state of each existing target. */ /* Now describe the state of each existing target. */
copy_info(&info, "Target #\tconfig3\t\tSync Capabilities\tDisconnect\tWide\n"); copy_info(&info, "Target #\tconfig3\t\tSync Capabilities\tDisconnect\tWide\n");
for (i = 0; i < 15; i++) {
if (esp->targets_present & (1 << i)) {
Scsi_Device *SDptr;
struct esp_device *esp_dev;
list_for_each_entry(SDptr, &esp->ehost->my_devices, shost_for_each_device(sdev, esp->ehost) {
siblings) struct esp_device *esp_dev = sdev->hostdata;
if(SDptr->id == i) uint id = sdev->id;
break;
esp_dev = SDptr->hostdata; if (!(esp->targets_present & (1 << id)))
copy_info(&info, "%d\t\t", i); continue;
copy_info(&info, "%08lx\t", esp->config3[i]);
copy_info(&info, "[%02lx,%02lx]\t\t\t", esp_dev->sync_max_offset, copy_info(&info, "%d\t\t", id);
copy_info(&info, "%08lx\t", esp->config3[id]);
copy_info(&info, "[%02lx,%02lx]\t\t\t",
esp_dev->sync_max_offset,
esp_dev->sync_min_period); esp_dev->sync_min_period);
copy_info(&info, "%s\t\t", esp_dev->disconnect ? "yes" : "no"); copy_info(&info, "%s\t\t",
esp_dev->disconnect ? "yes" : "no");
copy_info(&info, "%s\n", copy_info(&info, "%s\n",
(esp->config3[i] & ESP_CONFIG3_EWIDE) ? "yes" : "no"); (esp->config3[id] & ESP_CONFIG3_EWIDE) ? "yes" : "no");
}
} }
return info.pos > info.offset? info.pos - info.offset : 0; return info.pos > info.offset? info.pos - info.offset : 0;
} }
......
...@@ -228,7 +228,7 @@ int fcal_proc_info (struct Scsi_Host *host, char *buffer, char **start, off_t of ...@@ -228,7 +228,7 @@ int fcal_proc_info (struct Scsi_Host *host, char *buffer, char **start, off_t of
#endif #endif
SPRINTF ("Initiator AL-PA: %02x\n", fc->sid); SPRINTF ("Initiator AL-PA: %02x\n", fc->sid);
SPRINTF ("\nAttached devices: %s\n", !list_empty(&host->my_devices) ? "" : "none"); SPRINTF ("\nAttached devices:\n");
for (i = 0; i < fc->posmap->len; i++) { for (i = 0; i < fc->posmap->len; i++) {
unsigned char alpa = fc->posmap->list[i]; unsigned char alpa = fc->posmap->list[i];
......
/* /*
* hosts.c Copyright (C) 1992 Drew Eckhardt * hosts.c Copyright (C) 1992 Drew Eckhardt
* Copyright (C) 1993, 1994, 1995 Eric Youngdale * Copyright (C) 1993, 1994, 1995 Eric Youngdale
* Copyright (C) 2002-2003 Christoph Hellwig
* *
* mid to lowlevel SCSI driver interface * mid to lowlevel SCSI driver interface
* Initial versions: Drew Eckhardt * Initial versions: Drew Eckhardt
...@@ -30,8 +31,8 @@ ...@@ -30,8 +31,8 @@
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/unistd.h> #include <linux/unistd.h>
#include <scsi/scsi_host.h>
#include "scsi.h" #include "scsi.h"
#include "hosts.h"
#include "scsi_priv.h" #include "scsi_priv.h"
#include "scsi_logging.h" #include "scsi_logging.h"
...@@ -50,6 +51,11 @@ static struct class shost_class = { ...@@ -50,6 +51,11 @@ static struct class shost_class = {
.release = scsi_host_cls_release, .release = scsi_host_cls_release,
}; };
static int scsi_device_cancel_cb(struct device *dev, void *data)
{
return scsi_device_cancel(to_scsi_device(dev), *(int *)data);
}
/** /**
* scsi_host_cancel - cancel outstanding IO to this host * scsi_host_cancel - cancel outstanding IO to this host
* @shost: pointer to struct Scsi_Host * @shost: pointer to struct Scsi_Host
...@@ -57,11 +63,7 @@ static struct class shost_class = { ...@@ -57,11 +63,7 @@ static struct class shost_class = {
**/ **/
void scsi_host_cancel(struct Scsi_Host *shost, int recovery) void scsi_host_cancel(struct Scsi_Host *shost, int recovery)
{ {
unsigned long flags;
spin_lock_irqsave(shost->host_lock, flags);
set_bit(SHOST_CANCEL, &shost->shost_state); set_bit(SHOST_CANCEL, &shost->shost_state);
spin_unlock_irqrestore(shost->host_lock, flags);
device_for_each_child(&shost->shost_gendev, &recovery, device_for_each_child(&shost->shost_gendev, &recovery,
scsi_device_cancel_cb); scsi_device_cancel_cb);
wait_event(shost->host_wait, (!test_bit(SHOST_RECOVERY, wait_event(shost->host_wait, (!test_bit(SHOST_RECOVERY,
...@@ -74,15 +76,11 @@ void scsi_host_cancel(struct Scsi_Host *shost, int recovery) ...@@ -74,15 +76,11 @@ void scsi_host_cancel(struct Scsi_Host *shost, int recovery)
**/ **/
void scsi_remove_host(struct Scsi_Host *shost) void scsi_remove_host(struct Scsi_Host *shost)
{ {
unsigned long flags;
scsi_host_cancel(shost, 0); scsi_host_cancel(shost, 0);
scsi_proc_host_rm(shost); scsi_proc_host_rm(shost);
scsi_forget_host(shost); scsi_forget_host(shost);
spin_lock_irqsave(shost->host_lock, flags);
set_bit(SHOST_DEL, &shost->shost_state); set_bit(SHOST_DEL, &shost->shost_state);
spin_unlock_irqrestore(shost->host_lock, flags);
class_device_unregister(&shost->shost_classdev); class_device_unregister(&shost->shost_classdev);
device_del(&shost->shost_gendev); device_del(&shost->shost_gendev);
...@@ -209,7 +207,7 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) ...@@ -209,7 +207,7 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
spin_lock_init(&shost->default_lock); spin_lock_init(&shost->default_lock);
scsi_assign_lock(shost, &shost->default_lock); scsi_assign_lock(shost, &shost->default_lock);
INIT_LIST_HEAD(&shost->my_devices); INIT_LIST_HEAD(&shost->__devices);
INIT_LIST_HEAD(&shost->eh_cmd_q); INIT_LIST_HEAD(&shost->eh_cmd_q);
INIT_LIST_HEAD(&shost->starved_list); INIT_LIST_HEAD(&shost->starved_list);
init_waitqueue_head(&shost->host_wait); init_waitqueue_head(&shost->host_wait);
...@@ -323,11 +321,10 @@ void scsi_unregister(struct Scsi_Host *shost) ...@@ -323,11 +321,10 @@ void scsi_unregister(struct Scsi_Host *shost)
**/ **/
struct Scsi_Host *scsi_host_lookup(unsigned short hostnum) struct Scsi_Host *scsi_host_lookup(unsigned short hostnum)
{ {
struct class *class = class_get(&shost_class); struct class *class = &shost_class;
struct class_device *cdev; struct class_device *cdev;
struct Scsi_Host *shost = ERR_PTR(-ENXIO), *p; struct Scsi_Host *shost = ERR_PTR(-ENXIO), *p;
if (class) {
down_read(&class->subsys.rwsem); down_read(&class->subsys.rwsem);
list_for_each_entry(cdev, &class->children, node) { list_for_each_entry(cdev, &class->children, node) {
p = class_to_shost(cdev); p = class_to_shost(cdev);
...@@ -337,9 +334,7 @@ struct Scsi_Host *scsi_host_lookup(unsigned short hostnum) ...@@ -337,9 +334,7 @@ struct Scsi_Host *scsi_host_lookup(unsigned short hostnum)
} }
} }
up_read(&class->subsys.rwsem); up_read(&class->subsys.rwsem);
}
class_put(&shost_class);
return shost; return shost;
} }
......
/* // #warning "This file is obsolete, please use <scsi/scsi_host.h> instead"
* hosts.h Copyright (C) 1992 Drew Eckhardt
* Copyright (C) 1993, 1994, 1995, 1998, 1999 Eric Youngdale
*
* mid to low-level SCSI driver interface header
* Initial versions: Drew Eckhardt
* Subsequent revisions: Eric Youngdale
*
* <drew@colorado.edu>
*
* Modified by Eric Youngdale eric@andante.org to
* add scatter-gather, multiple outstanding request, and other
* enhancements.
*
* Further modified by Eric Youngdale to support multiple host adapters
* 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 (andmike@us.ibm.com)
*/
#ifndef _HOSTS_H
#define _HOSTS_H
#include <linux/config.h>
#include <scsi/scsi_host.h> #include <scsi/scsi_host.h>
/**
* 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 struct scsi_device *scsi_find_device(struct Scsi_Host *shost,
int channel, int pun, int lun) {
struct scsi_device *sdev;
list_for_each_entry (sdev, &shost->my_devices, siblings)
if (sdev->channel == channel && sdev->id == pun
&& sdev->lun ==lun)
return sdev;
return NULL;
}
#endif
/* /*
* scsi.c Copyright (C) 1992 Drew Eckhardt * scsi.c Copyright (C) 1992 Drew Eckhardt
* Copyright (C) 1993, 1994, 1995, 1999 Eric Youngdale * Copyright (C) 1993, 1994, 1995, 1999 Eric Youngdale
* Copyright (C) 2002, 2003 Christoph Hellwig
* *
* generic mid-level SCSI driver * generic mid-level SCSI driver
* Initial versions: Drew Eckhardt * Initial versions: Drew Eckhardt
...@@ -36,7 +37,6 @@ ...@@ -36,7 +37,6 @@
* out_of_space hacks, D. Gilbert (dpg) 990608 * out_of_space hacks, D. Gilbert (dpg) 990608
*/ */
#include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -54,8 +54,8 @@ ...@@ -54,8 +54,8 @@
#include <linux/kmod.h> #include <linux/kmod.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <scsi/scsi_host.h>
#include "scsi.h" #include "scsi.h"
#include "hosts.h"
#include "scsi_priv.h" #include "scsi_priv.h"
#include "scsi_logging.h" #include "scsi_logging.h"
...@@ -883,49 +883,124 @@ int scsi_track_queue_full(struct scsi_device *sdev, int depth) ...@@ -883,49 +883,124 @@ int scsi_track_queue_full(struct scsi_device *sdev, int depth)
return depth; return depth;
} }
/**
* scsi_device_get - get an addition reference to a scsi_device
* @sdev: device to get a reference to
*
* Gets a reference to the scsi_device and increments the use count
* of the underlying LLDD module. You must hold host_lock of the
* parent Scsi_Host or already have a reference when calling this.
*/
int scsi_device_get(struct scsi_device *sdev) int scsi_device_get(struct scsi_device *sdev)
{ {
struct class *class = class_get(&sdev_class);
if (!class)
goto out;
if (test_bit(SDEV_DEL, &sdev->sdev_state)) if (test_bit(SDEV_DEL, &sdev->sdev_state))
goto out; return -ENXIO;
if (!try_module_get(sdev->host->hostt->module))
goto out;
if (!get_device(&sdev->sdev_gendev)) if (!get_device(&sdev->sdev_gendev))
goto out_put_module; return -ENXIO;
atomic_inc(&sdev->access_count); if (!try_module_get(sdev->host->hostt->module)) {
class_put(&sdev_class); put_device(&sdev->sdev_gendev);
return -ENXIO;
}
return 0; return 0;
}
EXPORT_SYMBOL(scsi_device_get);
out_put_module: /**
* scsi_device_put - release a reference to a scsi_device
* @sdev: device to release a reference on.
*
* Release a reference to the scsi_device and decrements the use count
* of the underlying LLDD module. The device is freed once the last
* user vanishes.
*/
void scsi_device_put(struct scsi_device *sdev)
{
module_put(sdev->host->hostt->module); module_put(sdev->host->hostt->module);
out: put_device(&sdev->sdev_gendev);
class_put(&sdev_class);
return -ENXIO;
} }
EXPORT_SYMBOL(scsi_device_put);
void scsi_device_put(struct scsi_device *sdev) /* helper for shost_for_each_device, thus not documented */
struct scsi_device *__scsi_iterate_devices(struct Scsi_Host *shost,
struct scsi_device *prev)
{ {
struct class *class = class_get(&sdev_class); struct list_head *list = (prev ? &prev->siblings : &shost->__devices);
struct scsi_device *next = NULL;
unsigned long flags;
if (!class) spin_lock_irqsave(shost->host_lock, flags);
return; while (list->next != &shost->__devices) {
next = list_entry(list->next, struct scsi_device, siblings);
/* skip devices that we can't get a reference to */
if (!scsi_device_get(next))
break;
list = list->next;
}
spin_unlock_irqrestore(shost->host_lock, flags);
module_put(sdev->host->hostt->module); if (prev)
atomic_dec(&sdev->access_count); scsi_device_put(prev);
put_device(&sdev->sdev_gendev); return next;
class_put(&sdev_class);
} }
EXPORT_SYMBOL(__scsi_iterate_devices);
int scsi_device_cancel_cb(struct device *dev, void *data) /**
* scsi_device_lookup - find a device given the host (UNLOCKED)
* @shost: SCSI host pointer
* @channel: SCSI channel (zero if only one channel)
* @pun: SCSI target number (physical unit number)
* @lun: SCSI Logical Unit Number
*
* Looks up the scsi_device with the specified @channel, @id, @lun for a
* give host. The returned scsi_device does not have an additional reference.
* You must hold the host's host_lock over this call and any access to the
* returned scsi_device.
*
* Note: The only reason why drivers would want to use this is because
* they're need to access the device list in irq context. Otherwise you
* really want to use scsi_device_lookup instead.
**/
struct scsi_device *__scsi_device_lookup(struct Scsi_Host *shost,
uint channel, uint id, uint lun)
{ {
struct scsi_device *sdev = to_scsi_device(dev); struct scsi_device *sdev;
int recovery = *(int *)data;
list_for_each_entry(sdev, &shost->__devices, siblings) {
if (sdev->channel == channel && sdev->id == id &&
sdev->lun ==lun)
return sdev;
}
return NULL;
}
EXPORT_SYMBOL(__scsi_device_lookup);
/**
* scsi_device_lookup - find a device given the host
* @shost: SCSI host pointer
* @channel: SCSI channel (zero if only one channel)
* @id: SCSI target number (physical unit number)
* @lun: SCSI Logical Unit Number
*
* Looks up the scsi_device with the specified @channel, @id, @lun for a
* give host. The returned scsi_device has an additional reference that
* needs to be release with scsi_host_put once you're done with it.
**/
struct scsi_device *scsi_device_lookup(struct Scsi_Host *shost,
uint channel, uint id, uint lun)
{
struct scsi_device *sdev;
unsigned long flags;
spin_lock_irqsave(shost->host_lock, flags);
sdev = __scsi_device_lookup(shost, channel, id, lun);
if (sdev && scsi_device_get(sdev))
sdev = NULL;
spin_unlock_irqrestore(shost->host_lock, flags);
return scsi_device_cancel(sdev, recovery); return sdev;
} }
EXPORT_SYMBOL(scsi_device_lookup);
/** /**
* scsi_device_cancel - cancel outstanding IO to this device * scsi_device_cancel - cancel outstanding IO to this device
......
...@@ -16,9 +16,9 @@ ...@@ -16,9 +16,9 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/pci.h> #include <linux/pci.h>
#include "scsi.h"
#include "hosts.h"
#include <scsi/scsi_driver.h> #include <scsi/scsi_driver.h>
#include <scsi/scsi_host.h>
#include "scsi.h"
#include "scsi_priv.h" #include "scsi_priv.h"
#include "scsi_logging.h" #include "scsi_logging.h"
...@@ -335,13 +335,14 @@ void scsi_device_unbusy(struct scsi_device *sdev) ...@@ -335,13 +335,14 @@ void scsi_device_unbusy(struct scsi_device *sdev)
*/ */
static void scsi_single_lun_run(struct scsi_device *current_sdev) static void scsi_single_lun_run(struct scsi_device *current_sdev)
{ {
struct scsi_device *sdev; struct Scsi_Host *shost = current_sdev->host;
struct scsi_device *sdev, *tmp;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(current_sdev->host->host_lock, flags); spin_lock_irqsave(shost->host_lock, flags);
WARN_ON(!current_sdev->sdev_target->starget_sdev_user); WARN_ON(!current_sdev->sdev_target->starget_sdev_user);
current_sdev->sdev_target->starget_sdev_user = NULL; current_sdev->sdev_target->starget_sdev_user = NULL;
spin_unlock_irqrestore(current_sdev->host->host_lock, flags); spin_unlock_irqrestore(shost->host_lock, flags);
/* /*
* Call blk_run_queue for all LUNs on the target, starting with * Call blk_run_queue for all LUNs on the target, starting with
...@@ -351,21 +352,26 @@ static void scsi_single_lun_run(struct scsi_device *current_sdev) ...@@ -351,21 +352,26 @@ static void scsi_single_lun_run(struct scsi_device *current_sdev)
*/ */
blk_run_queue(current_sdev->request_queue); blk_run_queue(current_sdev->request_queue);
spin_lock_irqsave(current_sdev->host->host_lock, flags);
if (current_sdev->sdev_target->starget_sdev_user) {
/* /*
* After unlock, this races with anyone clearing * After unlock, this races with anyone clearing starget_sdev_user,
* starget_sdev_user, but we (should) always enter this * but we always enter this function again, avoiding any problems.
* function again, avoiding any problems.
*/ */
spin_unlock_irqrestore(current_sdev->host->host_lock, flags); spin_lock_irqsave(shost->host_lock, flags);
return; if (current_sdev->sdev_target->starget_sdev_user)
} goto out;
spin_unlock_irqrestore(current_sdev->host->host_lock, flags); list_for_each_entry_safe(sdev, tmp, &current_sdev->same_target_siblings,
same_target_siblings) {
if (scsi_device_get(sdev))
continue;
list_for_each_entry(sdev, &current_sdev->same_target_siblings, spin_unlock_irqrestore(shost->host_lock, flags);
same_target_siblings)
blk_run_queue(sdev->request_queue); blk_run_queue(sdev->request_queue);
spin_lock_irqsave(shost->host_lock, flags);
scsi_device_put(sdev);
}
out:
spin_unlock_irqrestore(shost->host_lock, flags);
} }
/* /*
......
...@@ -27,8 +27,8 @@ ...@@ -27,8 +27,8 @@
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <scsi/scsi_host.h>
#include "scsi.h" #include "scsi.h"
#include "hosts.h"
#include "scsi_priv.h" #include "scsi_priv.h"
#include "scsi_logging.h" #include "scsi_logging.h"
...@@ -212,15 +212,13 @@ static int scsi_remove_single_device(uint host, uint channel, uint id, uint lun) ...@@ -212,15 +212,13 @@ static int scsi_remove_single_device(uint host, uint channel, uint id, uint lun)
shost = scsi_host_lookup(host); shost = scsi_host_lookup(host);
if (IS_ERR(shost)) if (IS_ERR(shost))
return PTR_ERR(shost); return PTR_ERR(shost);
sdev = scsi_find_device(shost, channel, id, lun); sdev = scsi_device_lookup(shost, channel, id, lun);
if (!sdev) if (sdev) {
goto out;
if (atomic_read(&sdev->access_count))
goto out;
scsi_remove_device(sdev); scsi_remove_device(sdev);
scsi_device_put(sdev);
error = 0; error = 0;
out: }
scsi_host_put(shost); scsi_host_put(shost);
return error; return error;
} }
......
...@@ -32,10 +32,10 @@ ...@@ -32,10 +32,10 @@
#include <linux/blkdev.h> #include <linux/blkdev.h>
#include <asm/semaphore.h> #include <asm/semaphore.h>
#include "scsi.h"
#include "hosts.h"
#include <scsi/scsi_driver.h> #include <scsi/scsi_driver.h>
#include <scsi/scsi_devinfo.h> #include <scsi/scsi_devinfo.h>
#include <scsi/scsi_host.h>
#include "scsi.h"
#include "scsi_priv.h" #include "scsi_priv.h"
#include "scsi_logging.h" #include "scsi_logging.h"
...@@ -190,13 +190,13 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost, ...@@ -190,13 +190,13 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
uint channel, uint id, uint lun) uint channel, uint id, uint lun)
{ {
struct scsi_device *sdev, *device; struct scsi_device *sdev, *device;
unsigned long flags;
sdev = kmalloc(sizeof(*sdev), GFP_ATOMIC); sdev = kmalloc(sizeof(*sdev), GFP_ATOMIC);
if (!sdev) if (!sdev)
goto out; goto out;
memset(sdev, 0, sizeof(*sdev)); memset(sdev, 0, sizeof(*sdev));
atomic_set(&sdev->access_count, 0);
sdev->vendor = scsi_null_device_strs; sdev->vendor = scsi_null_device_strs;
sdev->model = scsi_null_device_strs; sdev->model = scsi_null_device_strs;
sdev->rev = scsi_null_device_strs; sdev->rev = scsi_null_device_strs;
...@@ -240,7 +240,8 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost, ...@@ -240,7 +240,8 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
* If there are any same target siblings, add this to the * If there are any same target siblings, add this to the
* sibling list * sibling list
*/ */
list_for_each_entry(device, &shost->my_devices, siblings) { spin_lock_irqsave(shost->host_lock, flags);
list_for_each_entry(device, &shost->__devices, siblings) {
if (device->id == sdev->id && if (device->id == sdev->id &&
device->channel == sdev->channel) { device->channel == sdev->channel) {
list_add_tail(&sdev->same_target_siblings, list_add_tail(&sdev->same_target_siblings,
...@@ -258,10 +259,8 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost, ...@@ -258,10 +259,8 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
if (!sdev->scsi_level) if (!sdev->scsi_level)
sdev->scsi_level = SCSI_2; sdev->scsi_level = SCSI_2;
/* list_add_tail(&sdev->siblings, &shost->__devices);
* Add it to the end of the shost->my_devices list. spin_unlock_irqrestore(shost->host_lock, flags);
*/
list_add_tail(&sdev->siblings, &shost->my_devices);
return sdev; return sdev;
out_free_queue: out_free_queue:
...@@ -285,21 +284,21 @@ void scsi_free_sdev(struct scsi_device *sdev) ...@@ -285,21 +284,21 @@ void scsi_free_sdev(struct scsi_device *sdev)
{ {
unsigned long flags; unsigned long flags;
spin_lock_irqsave(sdev->host->host_lock, flags);
list_del(&sdev->siblings); list_del(&sdev->siblings);
list_del(&sdev->same_target_siblings); list_del(&sdev->same_target_siblings);
spin_unlock_irqrestore(sdev->host->host_lock, flags);
if (sdev->request_queue) if (sdev->request_queue)
scsi_free_queue(sdev->request_queue); scsi_free_queue(sdev->request_queue);
if (sdev->inquiry)
kfree(sdev->inquiry);
spin_lock_irqsave(sdev->host->host_lock, flags); spin_lock_irqsave(sdev->host->host_lock, flags);
list_del(&sdev->starved_entry); list_del(&sdev->starved_entry);
if (sdev->single_lun) { if (sdev->single_lun && --sdev->sdev_target->starget_refcnt == 0)
if (--sdev->sdev_target->starget_refcnt == 0)
kfree(sdev->sdev_target); kfree(sdev->sdev_target);
}
spin_unlock_irqrestore(sdev->host->host_lock, flags); spin_unlock_irqrestore(sdev->host->host_lock, flags);
kfree(sdev->inquiry);
kfree(sdev); kfree(sdev);
} }
...@@ -678,7 +677,7 @@ static int scsi_probe_and_add_lun(struct Scsi_Host *host, ...@@ -678,7 +677,7 @@ static int scsi_probe_and_add_lun(struct Scsi_Host *host,
* host adapter calls into here with rescan == 0. * host adapter calls into here with rescan == 0.
*/ */
if (rescan) { if (rescan) {
sdev = scsi_find_device(host, channel, id, lun); sdev = scsi_device_lookup(host, channel, id, lun);
if (sdev) { if (sdev) {
SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO
"scsi scan: device exists on <%d:%d:%d:%d>\n", "scsi scan: device exists on <%d:%d:%d:%d>\n",
...@@ -689,6 +688,8 @@ static int scsi_probe_and_add_lun(struct Scsi_Host *host, ...@@ -689,6 +688,8 @@ static int scsi_probe_and_add_lun(struct Scsi_Host *host,
*bflagsp = scsi_get_device_flags(sdev, *bflagsp = scsi_get_device_flags(sdev,
sdev->vendor, sdev->vendor,
sdev->model); sdev->model);
/* XXX: bandaid until callers do refcounting */
scsi_device_put(sdev);
return SCSI_SCAN_LUN_PRESENT; return SCSI_SCAN_LUN_PRESENT;
} }
} }
...@@ -1232,14 +1233,25 @@ void scsi_scan_host(struct Scsi_Host *shost) ...@@ -1232,14 +1233,25 @@ void scsi_scan_host(struct Scsi_Host *shost)
void scsi_forget_host(struct Scsi_Host *shost) void scsi_forget_host(struct Scsi_Host *shost)
{ {
struct list_head *le, *lh; struct scsi_device *sdev, *tmp;
struct scsi_device *sdev; unsigned long flags;
list_for_each_safe(le, lh, &shost->my_devices) {
sdev = list_entry(le, struct scsi_device, siblings);
/*
* Ok, this look a bit strange. We always look for the first device
* on the list as scsi_remove_device removes them from it - thus we
* also have to release the lock.
* We don't need to get another reference to the device before
* releasing the lock as we already own the reference from
* scsi_register_device that's release in scsi_remove_device. And
* after that we don't look at sdev anymore.
*/
spin_lock_irqsave(shost->host_lock, flags);
list_for_each_entry_safe(sdev, tmp, &shost->__devices, siblings) {
spin_unlock_irqrestore(shost->host_lock, flags);
scsi_remove_device(sdev); scsi_remove_device(sdev);
spin_lock_irqsave(shost->host_lock, flags);
} }
spin_unlock_irqrestore(shost->host_lock, flags);
} }
/* /*
......
...@@ -18,13 +18,14 @@ ...@@ -18,13 +18,14 @@
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/dma.h> #include <asm/dma.h>
#include "scsi.h"
#include <scsi/scsi_driver.h> #include <scsi/scsi_driver.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_ioctl.h> #include <scsi/scsi_ioctl.h>
#include "hosts.h" #include <scsi/scsicam.h>
#include "scsi.h"
#include "scsi_logging.h" #include "scsi_logging.h"
#include <scsi/scsicam.h>
/* /*
* This source file contains the symbol table used by scsi loadable * This source file contains the symbol table used by scsi loadable
...@@ -82,8 +83,6 @@ EXPORT_SYMBOL(scsi_sleep); ...@@ -82,8 +83,6 @@ EXPORT_SYMBOL(scsi_sleep);
EXPORT_SYMBOL(scsi_io_completion); EXPORT_SYMBOL(scsi_io_completion);
EXPORT_SYMBOL(scsi_device_get);
EXPORT_SYMBOL(scsi_device_put);
EXPORT_SYMBOL(scsi_add_device); EXPORT_SYMBOL(scsi_add_device);
EXPORT_SYMBOL(scsi_remove_device); EXPORT_SYMBOL(scsi_remove_device);
EXPORT_SYMBOL(scsi_device_cancel); EXPORT_SYMBOL(scsi_device_cancel);
......
...@@ -11,8 +11,9 @@ ...@@ -11,8 +11,9 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/blkdev.h> #include <linux/blkdev.h>
#include <linux/device.h> #include <linux/device.h>
#include <scsi/scsi_host.h>
#include "scsi.h" #include "scsi.h"
#include "hosts.h"
#include "scsi_priv.h" #include "scsi_priv.h"
#include "scsi_logging.h" #include "scsi_logging.h"
...@@ -257,20 +258,12 @@ store_rescan_field (struct device *dev, const char *buf, size_t count) ...@@ -257,20 +258,12 @@ store_rescan_field (struct device *dev, const char *buf, size_t count)
scsi_rescan_device(dev); scsi_rescan_device(dev);
return count; return count;
} }
static DEVICE_ATTR(rescan, S_IWUSR, NULL, store_rescan_field) static DEVICE_ATTR(rescan, S_IWUSR, NULL, store_rescan_field)
static ssize_t sdev_store_delete(struct device *dev, const char *buf, static ssize_t sdev_store_delete(struct device *dev, const char *buf,
size_t count) size_t count)
{ {
struct scsi_device *sdev = to_scsi_device(dev); scsi_remove_device(to_scsi_device(dev));
/*
* FIXME and scsi_proc.c: racey use of access_count,
*/
if (atomic_read(&sdev->access_count))
return -EBUSY;
scsi_remove_device(sdev);
return count; return count;
}; };
static DEVICE_ATTR(delete, S_IWUSR, NULL, sdev_store_delete); static DEVICE_ATTR(delete, S_IWUSR, NULL, sdev_store_delete);
...@@ -403,22 +396,12 @@ int scsi_device_register(struct scsi_device *sdev) ...@@ -403,22 +396,12 @@ int scsi_device_register(struct scsi_device *sdev)
**/ **/
void scsi_remove_device(struct scsi_device *sdev) void scsi_remove_device(struct scsi_device *sdev)
{ {
struct class *class = class_get(&sdev_class);
class_device_unregister(&sdev->sdev_classdev); class_device_unregister(&sdev->sdev_classdev);
if (class) {
down_write(&class->subsys.rwsem);
set_bit(SDEV_DEL, &sdev->sdev_state); set_bit(SDEV_DEL, &sdev->sdev_state);
if (sdev->host->hostt->slave_destroy) if (sdev->host->hostt->slave_destroy)
sdev->host->hostt->slave_destroy(sdev); sdev->host->hostt->slave_destroy(sdev);
device_del(&sdev->sdev_gendev); device_del(&sdev->sdev_gendev);
up_write(&class->subsys.rwsem);
}
put_device(&sdev->sdev_gendev); put_device(&sdev->sdev_gendev);
class_put(&sdev_class);
} }
int scsi_register_driver(struct device_driver *drv) int scsi_register_driver(struct device_driver *drv)
......
...@@ -914,7 +914,8 @@ sg_ioctl(struct inode *inode, struct file *filp, ...@@ -914,7 +914,8 @@ sg_ioctl(struct inode *inode, struct file *filp,
case SG_GET_VERSION_NUM: case SG_GET_VERSION_NUM:
return put_user(sg_version_num, (int *) arg); return put_user(sg_version_num, (int *) arg);
case SG_GET_ACCESS_COUNT: case SG_GET_ACCESS_COUNT:
val = (sdp->device ? atomic_read(&sdp->device->access_count) : 0); /* faked - we don't have a real access count anymore */
val = (sdp->device ? 1 : 0);
return put_user(val, (int *) arg); return put_user(val, (int *) arg);
case SG_GET_REQUEST_TABLE: case SG_GET_REQUEST_TABLE:
result = verify_area(VERIFY_WRITE, (void *) arg, result = verify_area(VERIFY_WRITE, (void *) arg,
...@@ -2903,7 +2904,7 @@ sg_proc_dev_info(char *buffer, int *len, off_t * begin, off_t offset, int size) ...@@ -2903,7 +2904,7 @@ sg_proc_dev_info(char *buffer, int *len, off_t * begin, off_t offset, int size)
PRINT_PROC("%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", PRINT_PROC("%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
scsidp->host->host_no, scsidp->channel, scsidp->host->host_no, scsidp->channel,
scsidp->id, scsidp->lun, (int) scsidp->type, scsidp->id, scsidp->lun, (int) scsidp->type,
(int) atomic_read(&scsidp->access_count), 1,
(int) scsidp->queue_depth, (int) scsidp->queue_depth,
(int) scsidp->device_busy, (int) scsidp->device_busy,
(int) scsidp->online); (int) scsidp->online);
......
...@@ -22,10 +22,13 @@ enum { ...@@ -22,10 +22,13 @@ enum {
}; };
struct scsi_device { struct scsi_device {
struct list_head siblings; /* list of all devices on this host */
struct list_head same_target_siblings; /* just the devices sharing same target id */
struct Scsi_Host *host; struct Scsi_Host *host;
struct request_queue *request_queue; struct request_queue *request_queue;
/* the next two are protected by the host->host_lock */
struct list_head siblings; /* list of all devices on this host */
struct list_head same_target_siblings; /* just the devices sharing same target id */
volatile unsigned short device_busy; /* commands actually active on low-level */ volatile unsigned short device_busy; /* commands actually active on low-level */
spinlock_t sdev_lock; /* also the request queue_lock */ spinlock_t sdev_lock; /* also the request queue_lock */
spinlock_t list_lock; spinlock_t list_lock;
...@@ -45,8 +48,6 @@ struct scsi_device { ...@@ -45,8 +48,6 @@ struct scsi_device {
* vendor-specific cmd's */ * vendor-specific cmd's */
unsigned sector_size; /* size in bytes */ unsigned sector_size; /* size in bytes */
atomic_t access_count; /* Count of open channels/mounts */
void *hostdata; /* available to low-level driver */ void *hostdata; /* available to low-level driver */
char devfs_name[256]; /* devfs junk */ char devfs_name[256]; /* devfs junk */
char type; char type;
...@@ -108,14 +109,48 @@ struct scsi_device { ...@@ -108,14 +109,48 @@ struct scsi_device {
extern struct scsi_device *scsi_add_device(struct Scsi_Host *, extern struct scsi_device *scsi_add_device(struct Scsi_Host *,
uint, uint, uint); uint, uint, uint);
extern void scsi_remove_device(struct scsi_device *); extern void scsi_remove_device(struct scsi_device *);
extern int scsi_device_cancel_cb(struct device *, void *);
extern int scsi_device_cancel(struct scsi_device *, int); extern int scsi_device_cancel(struct scsi_device *, int);
extern int scsi_device_get(struct scsi_device *); extern int scsi_device_get(struct scsi_device *);
extern void scsi_device_put(struct scsi_device *); extern void scsi_device_put(struct scsi_device *);
extern struct scsi_device *scsi_device_lookup(struct Scsi_Host *,
uint, uint, uint);
extern struct scsi_device *__scsi_device_lookup(struct Scsi_Host *,
uint, uint, uint);
/* only exposed to implement shost_for_each_device */
extern struct scsi_device *__scsi_iterate_devices(struct Scsi_Host *,
struct scsi_device *);
/**
* shost_for_each_device - iterate over all devices of a host
* @sdev: iterator
* @host: host whiches devices we want to iterate over
*
* This traverses over each devices of @shost. The devices have
* a reference that must be released by scsi_host_put when breaking
* out of the loop.
*/
#define shost_for_each_device(sdev, shost) \ #define shost_for_each_device(sdev, shost) \
list_for_each_entry((sdev), &((shost)->my_devices), siblings) for ((sdev) = __scsi_iterate_devices((shost), NULL); \
(sdev); \
(sdev) = __scsi_iterate_devices((shost), (sdev)))
/**
* __shost_for_each_device - iterate over all devices of a host (UNLOCKED)
* @sdev: iterator
* @host: host whiches devices we want to iterate over
*
* This traverses over each devices of @shost. It does _not_ take a
* reference on the scsi_device, thus it the whole loop must be protected
* by shost->host_lock.
*
* Note: The only reason why drivers would want to use this is because
* they're need to access the device list in irq context. Otherwise you
* really want to use shost_for_each_device instead.
*/
#define __shost_for_each_device(sdev, shost) \
list_for_each_entry((sdev), &((shost)->__devices), siblings)
extern void scsi_adjust_queue_depth(struct scsi_device *, int, int); extern void scsi_adjust_queue_depth(struct scsi_device *, int, int);
extern int scsi_track_queue_full(struct scsi_device *, int); extern int scsi_track_queue_full(struct scsi_device *, int);
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include <linux/device.h> #include <linux/device.h>
struct module; struct module;
struct scsi_cmnd;
struct scsi_driver { struct scsi_driver {
......
...@@ -363,7 +363,16 @@ enum { ...@@ -363,7 +363,16 @@ enum {
}; };
struct Scsi_Host { struct Scsi_Host {
struct list_head my_devices; /*
* __devices is protected by the host_lock, but you should
* usually use scsi_device_lookup / shost_for_each_device
* to access it and don't care about locking yourself.
* In the rare case of beeing in irq context you can use
* their __ prefixed variants with the lock held. NEVER
* access this list directly from a driver.
*/
struct list_head __devices;
struct scsi_host_cmd_pool *cmd_pool; struct scsi_host_cmd_pool *cmd_pool;
spinlock_t free_list_lock; spinlock_t free_list_lock;
struct list_head free_list; /* backup store of cmd structs */ struct list_head free_list; /* backup store of cmd structs */
...@@ -372,10 +381,12 @@ struct Scsi_Host { ...@@ -372,10 +381,12 @@ struct Scsi_Host {
spinlock_t default_lock; spinlock_t default_lock;
spinlock_t *host_lock; spinlock_t *host_lock;
struct semaphore scan_mutex;/* serialize scanning activity */
struct list_head eh_cmd_q; struct list_head eh_cmd_q;
struct task_struct * ehandler; /* Error recovery thread. */ struct task_struct * ehandler; /* Error recovery thread. */
struct semaphore * eh_wait; /* The error recovery thread waits on struct semaphore * eh_wait; /* The error recovery thread waits
this. */ on this. */
struct completion * eh_notify; /* wait for eh to begin or end */ struct completion * eh_notify; /* wait for eh to begin or end */
struct semaphore * eh_action; /* Wait for specific actions on the struct semaphore * eh_action; /* Wait for specific actions on the
host. */ host. */
...@@ -478,12 +489,6 @@ struct Scsi_Host { ...@@ -478,12 +489,6 @@ struct Scsi_Host {
*/ */
struct list_head sht_legacy_list; struct list_head sht_legacy_list;
/*
* This mutex serializes all scsi scanning activity from kernel- and
* userspace.
*/
struct semaphore scan_mutex;
/* /*
* We should ensure that this is aligned, both for better performance * We should ensure that this is aligned, both for better performance
* and also because some compilers (m68k) don't automatically force * and also because some compilers (m68k) don't automatically force
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment