Commit 98c1fc93 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394-2.6

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394-2.6:
  firewire: fix NULL pointer deref. and resource leak
  Documentation: correction to debugging-via-ohci1394
  ieee1394: sbp2: fix rescan-scsi-bus
  firewire: fw-sbp2: fix NULL pointer deref. in scsi_remove_device
  firewire: fw-sbp2: fix NULL pointer deref. in slave_alloc
  firewire: fw-sbp2: (try to) avoid I/O errors during reconnect
  firewire: fw-sbp2: enforce a retry of __scsi_add_device if bus generation changed
  firewire: fw-sbp2: sort includes
  firewire: fw-sbp2: logout and login after failed reconnect
  firewire: fw-sbp2: don't add scsi_device twice
  firewire: fw-sbp2: log bus_id at management request failures
  firewire: fw-sbp2: wait for completion of fetch agent reset
  ieee1394: sbp2: add INQUIRY delay workaround
  firewire: fw-sbp2: add INQUIRY delay workaround
  firewire: log GUID of new devices
  firewire: fw-sbp2: don't retry login or reconnect after unplug
  firewire: fix "kobject_add failed for fw* with -EEXIST"
  firewire: fw-sbp2: fix logout before login retry
  firewire: fw-sbp2: unsigned int vs. unsigned
parents 7c811e4b fae60312
...@@ -36,14 +36,15 @@ available (notebooks) or too slow for extensive debug information (like ACPI). ...@@ -36,14 +36,15 @@ available (notebooks) or too slow for extensive debug information (like ACPI).
Drivers Drivers
------- -------
The OHCI-1394 drivers in drivers/firewire and drivers/ieee1394 initialize The ohci1394 driver in drivers/ieee1394 initializes the OHCI-1394 controllers
the OHCI-1394 controllers to a working state and can be used to enable to a working state and enables physical DMA by default for all remote nodes.
physical DMA. By default you only have to load the driver, and physical This can be turned off by ohci1394's module parameter phys_dma=0.
DMA access will be granted to all remote nodes, but it can be turned off
when using the ohci1394 driver. The alternative firewire-ohci driver in drivers/firewire uses filtered physical
DMA, hence is not yet suitable for remote debugging.
Because these drivers depend on the PCI enumeration to be completed, an
initialization routine which can runs pretty early (long before console_init(), Because ohci1394 depends on the PCI enumeration to be completed, an
initialization routine which runs pretty early (long before console_init()
which makes the printk buffer appear on the console can be called) was written. which makes the printk buffer appear on the console can be called) was written.
To activate it, enable CONFIG_PROVIDE_OHCI1394_DMA_INIT (Kernel hacking menu: To activate it, enable CONFIG_PROVIDE_OHCI1394_DMA_INIT (Kernel hacking menu:
......
...@@ -109,15 +109,17 @@ static int fw_device_op_open(struct inode *inode, struct file *file) ...@@ -109,15 +109,17 @@ static int fw_device_op_open(struct inode *inode, struct file *file)
struct client *client; struct client *client;
unsigned long flags; unsigned long flags;
device = fw_device_from_devt(inode->i_rdev); device = fw_device_get_by_devt(inode->i_rdev);
if (device == NULL) if (device == NULL)
return -ENODEV; return -ENODEV;
client = kzalloc(sizeof(*client), GFP_KERNEL); client = kzalloc(sizeof(*client), GFP_KERNEL);
if (client == NULL) if (client == NULL) {
fw_device_put(device);
return -ENOMEM; return -ENOMEM;
}
client->device = fw_device_get(device); client->device = device;
INIT_LIST_HEAD(&client->event_list); INIT_LIST_HEAD(&client->event_list);
INIT_LIST_HEAD(&client->resource_list); INIT_LIST_HEAD(&client->resource_list);
spin_lock_init(&client->lock); spin_lock_init(&client->lock);
...@@ -644,6 +646,10 @@ static int ioctl_create_iso_context(struct client *client, void *buffer) ...@@ -644,6 +646,10 @@ static int ioctl_create_iso_context(struct client *client, void *buffer)
struct fw_cdev_create_iso_context *request = buffer; struct fw_cdev_create_iso_context *request = buffer;
struct fw_iso_context *context; struct fw_iso_context *context;
/* We only support one context at this time. */
if (client->iso_context != NULL)
return -EBUSY;
if (request->channel > 63) if (request->channel > 63)
return -EINVAL; return -EINVAL;
...@@ -790,8 +796,9 @@ static int ioctl_start_iso(struct client *client, void *buffer) ...@@ -790,8 +796,9 @@ static int ioctl_start_iso(struct client *client, void *buffer)
{ {
struct fw_cdev_start_iso *request = buffer; struct fw_cdev_start_iso *request = buffer;
if (request->handle != 0) if (client->iso_context == NULL || request->handle != 0)
return -EINVAL; return -EINVAL;
if (client->iso_context->type == FW_ISO_CONTEXT_RECEIVE) { if (client->iso_context->type == FW_ISO_CONTEXT_RECEIVE) {
if (request->tags == 0 || request->tags > 15) if (request->tags == 0 || request->tags > 15)
return -EINVAL; return -EINVAL;
...@@ -808,7 +815,7 @@ static int ioctl_stop_iso(struct client *client, void *buffer) ...@@ -808,7 +815,7 @@ static int ioctl_stop_iso(struct client *client, void *buffer)
{ {
struct fw_cdev_stop_iso *request = buffer; struct fw_cdev_stop_iso *request = buffer;
if (request->handle != 0) if (client->iso_context == NULL || request->handle != 0)
return -EINVAL; return -EINVAL;
return fw_iso_context_stop(client->iso_context); return fw_iso_context_stop(client->iso_context);
......
...@@ -358,12 +358,9 @@ static ssize_t ...@@ -358,12 +358,9 @@ static ssize_t
guid_show(struct device *dev, struct device_attribute *attr, char *buf) guid_show(struct device *dev, struct device_attribute *attr, char *buf)
{ {
struct fw_device *device = fw_device(dev); struct fw_device *device = fw_device(dev);
u64 guid;
guid = ((u64)device->config_rom[3] << 32) | device->config_rom[4]; return snprintf(buf, PAGE_SIZE, "0x%08x%08x\n",
device->config_rom[3], device->config_rom[4]);
return snprintf(buf, PAGE_SIZE, "0x%016llx\n",
(unsigned long long)guid);
} }
static struct device_attribute fw_device_attributes[] = { static struct device_attribute fw_device_attributes[] = {
...@@ -610,12 +607,14 @@ static DECLARE_RWSEM(idr_rwsem); ...@@ -610,12 +607,14 @@ static DECLARE_RWSEM(idr_rwsem);
static DEFINE_IDR(fw_device_idr); static DEFINE_IDR(fw_device_idr);
int fw_cdev_major; int fw_cdev_major;
struct fw_device *fw_device_from_devt(dev_t devt) struct fw_device *fw_device_get_by_devt(dev_t devt)
{ {
struct fw_device *device; struct fw_device *device;
down_read(&idr_rwsem); down_read(&idr_rwsem);
device = idr_find(&fw_device_idr, MINOR(devt)); device = idr_find(&fw_device_idr, MINOR(devt));
if (device)
fw_device_get(device);
up_read(&idr_rwsem); up_read(&idr_rwsem);
return device; return device;
...@@ -627,13 +626,14 @@ static void fw_device_shutdown(struct work_struct *work) ...@@ -627,13 +626,14 @@ static void fw_device_shutdown(struct work_struct *work)
container_of(work, struct fw_device, work.work); container_of(work, struct fw_device, work.work);
int minor = MINOR(device->device.devt); int minor = MINOR(device->device.devt);
down_write(&idr_rwsem);
idr_remove(&fw_device_idr, minor);
up_write(&idr_rwsem);
fw_device_cdev_remove(device); fw_device_cdev_remove(device);
device_for_each_child(&device->device, NULL, shutdown_unit); device_for_each_child(&device->device, NULL, shutdown_unit);
device_unregister(&device->device); device_unregister(&device->device);
down_write(&idr_rwsem);
idr_remove(&fw_device_idr, minor);
up_write(&idr_rwsem);
fw_device_put(device);
} }
static struct device_type fw_device_type = { static struct device_type fw_device_type = {
...@@ -682,10 +682,13 @@ static void fw_device_init(struct work_struct *work) ...@@ -682,10 +682,13 @@ static void fw_device_init(struct work_struct *work)
} }
err = -ENOMEM; err = -ENOMEM;
fw_device_get(device);
down_write(&idr_rwsem); down_write(&idr_rwsem);
if (idr_pre_get(&fw_device_idr, GFP_KERNEL)) if (idr_pre_get(&fw_device_idr, GFP_KERNEL))
err = idr_get_new(&fw_device_idr, device, &minor); err = idr_get_new(&fw_device_idr, device, &minor);
up_write(&idr_rwsem); up_write(&idr_rwsem);
if (err < 0) if (err < 0)
goto error; goto error;
...@@ -717,13 +720,22 @@ static void fw_device_init(struct work_struct *work) ...@@ -717,13 +720,22 @@ static void fw_device_init(struct work_struct *work)
*/ */
if (atomic_cmpxchg(&device->state, if (atomic_cmpxchg(&device->state,
FW_DEVICE_INITIALIZING, FW_DEVICE_INITIALIZING,
FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN) FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN) {
fw_device_shutdown(&device->work.work); fw_device_shutdown(&device->work.work);
else } else {
fw_notify("created new fw device %s " if (device->config_rom_retries)
"(%d config rom retries, S%d00)\n", fw_notify("created device %s: GUID %08x%08x, S%d00, "
device->device.bus_id, device->config_rom_retries, "%d config ROM retries\n",
1 << device->max_speed); device->device.bus_id,
device->config_rom[3], device->config_rom[4],
1 << device->max_speed,
device->config_rom_retries);
else
fw_notify("created device %s: GUID %08x%08x, S%d00\n",
device->device.bus_id,
device->config_rom[3], device->config_rom[4],
1 << device->max_speed);
}
/* /*
* Reschedule the IRM work if we just finished reading the * Reschedule the IRM work if we just finished reading the
...@@ -741,7 +753,9 @@ static void fw_device_init(struct work_struct *work) ...@@ -741,7 +753,9 @@ static void fw_device_init(struct work_struct *work)
idr_remove(&fw_device_idr, minor); idr_remove(&fw_device_idr, minor);
up_write(&idr_rwsem); up_write(&idr_rwsem);
error: error:
put_device(&device->device); fw_device_put(device); /* fw_device_idr's reference */
put_device(&device->device); /* our reference */
} }
static int update_unit(struct device *dev, void *data) static int update_unit(struct device *dev, void *data)
......
...@@ -77,13 +77,13 @@ fw_device_is_shutdown(struct fw_device *device) ...@@ -77,13 +77,13 @@ fw_device_is_shutdown(struct fw_device *device)
} }
struct fw_device *fw_device_get(struct fw_device *device); struct fw_device *fw_device_get(struct fw_device *device);
struct fw_device *fw_device_get_by_devt(dev_t devt);
void fw_device_put(struct fw_device *device); void fw_device_put(struct fw_device *device);
int fw_device_enable_phys_dma(struct fw_device *device); int fw_device_enable_phys_dma(struct fw_device *device);
void fw_device_cdev_update(struct fw_device *device); void fw_device_cdev_update(struct fw_device *device);
void fw_device_cdev_remove(struct fw_device *device); void fw_device_cdev_remove(struct fw_device *device);
struct fw_device *fw_device_from_devt(dev_t devt);
extern int fw_cdev_major; extern int fw_cdev_major;
struct fw_unit { struct fw_unit {
......
...@@ -28,14 +28,15 @@ ...@@ -28,14 +28,15 @@
* and many others. * and many others.
*/ */
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/mod_devicetable.h>
#include <linux/device.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/dma-mapping.h>
#include <linux/blkdev.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/stringify.h> #include <linux/stringify.h>
#include <linux/timer.h> #include <linux/timer.h>
...@@ -47,9 +48,9 @@ ...@@ -47,9 +48,9 @@
#include <scsi/scsi_device.h> #include <scsi/scsi_device.h>
#include <scsi/scsi_host.h> #include <scsi/scsi_host.h>
#include "fw-transaction.h"
#include "fw-topology.h"
#include "fw-device.h" #include "fw-device.h"
#include "fw-topology.h"
#include "fw-transaction.h"
/* /*
* So far only bridges from Oxford Semiconductor are known to support * So far only bridges from Oxford Semiconductor are known to support
...@@ -82,6 +83,9 @@ MODULE_PARM_DESC(exclusive_login, "Exclusive login to sbp2 device " ...@@ -82,6 +83,9 @@ MODULE_PARM_DESC(exclusive_login, "Exclusive login to sbp2 device "
* Avoids access beyond actual disk limits on devices with an off-by-one bug. * Avoids access beyond actual disk limits on devices with an off-by-one bug.
* Don't use this with devices which don't have this bug. * Don't use this with devices which don't have this bug.
* *
* - delay inquiry
* Wait extra SBP2_INQUIRY_DELAY seconds after login before SCSI inquiry.
*
* - override internal blacklist * - override internal blacklist
* Instead of adding to the built-in blacklist, use only the workarounds * Instead of adding to the built-in blacklist, use only the workarounds
* specified in the module load parameter. * specified in the module load parameter.
...@@ -91,6 +95,8 @@ MODULE_PARM_DESC(exclusive_login, "Exclusive login to sbp2 device " ...@@ -91,6 +95,8 @@ MODULE_PARM_DESC(exclusive_login, "Exclusive login to sbp2 device "
#define SBP2_WORKAROUND_INQUIRY_36 0x2 #define SBP2_WORKAROUND_INQUIRY_36 0x2
#define SBP2_WORKAROUND_MODE_SENSE_8 0x4 #define SBP2_WORKAROUND_MODE_SENSE_8 0x4
#define SBP2_WORKAROUND_FIX_CAPACITY 0x8 #define SBP2_WORKAROUND_FIX_CAPACITY 0x8
#define SBP2_WORKAROUND_DELAY_INQUIRY 0x10
#define SBP2_INQUIRY_DELAY 12
#define SBP2_WORKAROUND_OVERRIDE 0x100 #define SBP2_WORKAROUND_OVERRIDE 0x100
static int sbp2_param_workarounds; static int sbp2_param_workarounds;
...@@ -100,6 +106,7 @@ MODULE_PARM_DESC(workarounds, "Work around device bugs (default = 0" ...@@ -100,6 +106,7 @@ MODULE_PARM_DESC(workarounds, "Work around device bugs (default = 0"
", 36 byte inquiry = " __stringify(SBP2_WORKAROUND_INQUIRY_36) ", 36 byte inquiry = " __stringify(SBP2_WORKAROUND_INQUIRY_36)
", skip mode page 8 = " __stringify(SBP2_WORKAROUND_MODE_SENSE_8) ", skip mode page 8 = " __stringify(SBP2_WORKAROUND_MODE_SENSE_8)
", fix capacity = " __stringify(SBP2_WORKAROUND_FIX_CAPACITY) ", fix capacity = " __stringify(SBP2_WORKAROUND_FIX_CAPACITY)
", delay inquiry = " __stringify(SBP2_WORKAROUND_DELAY_INQUIRY)
", override internal blacklist = " __stringify(SBP2_WORKAROUND_OVERRIDE) ", override internal blacklist = " __stringify(SBP2_WORKAROUND_OVERRIDE)
", or a combination)"); ", or a combination)");
...@@ -132,6 +139,7 @@ struct sbp2_logical_unit { ...@@ -132,6 +139,7 @@ struct sbp2_logical_unit {
int generation; int generation;
int retries; int retries;
struct delayed_work work; struct delayed_work work;
bool blocked;
}; };
/* /*
...@@ -141,16 +149,18 @@ struct sbp2_logical_unit { ...@@ -141,16 +149,18 @@ struct sbp2_logical_unit {
struct sbp2_target { struct sbp2_target {
struct kref kref; struct kref kref;
struct fw_unit *unit; struct fw_unit *unit;
const char *bus_id;
struct list_head lu_list;
u64 management_agent_address; u64 management_agent_address;
int directory_id; int directory_id;
int node_id; int node_id;
int address_high; int address_high;
unsigned int workarounds;
unsigned workarounds;
struct list_head lu_list;
unsigned int mgt_orb_timeout; unsigned int mgt_orb_timeout;
int dont_block; /* counter for each logical unit */
int blocked; /* ditto */
}; };
/* /*
...@@ -160,7 +170,7 @@ struct sbp2_target { ...@@ -160,7 +170,7 @@ struct sbp2_target {
*/ */
#define SBP2_MIN_LOGIN_ORB_TIMEOUT 5000U /* Timeout in ms */ #define SBP2_MIN_LOGIN_ORB_TIMEOUT 5000U /* Timeout in ms */
#define SBP2_MAX_LOGIN_ORB_TIMEOUT 40000U /* Timeout in ms */ #define SBP2_MAX_LOGIN_ORB_TIMEOUT 40000U /* Timeout in ms */
#define SBP2_ORB_TIMEOUT 2000 /* Timeout in ms */ #define SBP2_ORB_TIMEOUT 2000U /* Timeout in ms */
#define SBP2_ORB_NULL 0x80000000 #define SBP2_ORB_NULL 0x80000000
#define SBP2_MAX_SG_ELEMENT_LENGTH 0xf000 #define SBP2_MAX_SG_ELEMENT_LENGTH 0xf000
...@@ -297,7 +307,7 @@ struct sbp2_command_orb { ...@@ -297,7 +307,7 @@ struct sbp2_command_orb {
static const struct { static const struct {
u32 firmware_revision; u32 firmware_revision;
u32 model; u32 model;
unsigned workarounds; unsigned int workarounds;
} sbp2_workarounds_table[] = { } sbp2_workarounds_table[] = {
/* DViCO Momobay CX-1 with TSB42AA9 bridge */ { /* DViCO Momobay CX-1 with TSB42AA9 bridge */ {
.firmware_revision = 0x002800, .firmware_revision = 0x002800,
...@@ -305,6 +315,11 @@ static const struct { ...@@ -305,6 +315,11 @@ static const struct {
.workarounds = SBP2_WORKAROUND_INQUIRY_36 | .workarounds = SBP2_WORKAROUND_INQUIRY_36 |
SBP2_WORKAROUND_MODE_SENSE_8, SBP2_WORKAROUND_MODE_SENSE_8,
}, },
/* DViCO Momobay FX-3A with TSB42AA9A bridge */ {
.firmware_revision = 0x002800,
.model = 0x000000,
.workarounds = SBP2_WORKAROUND_DELAY_INQUIRY,
},
/* Initio bridges, actually only needed for some older ones */ { /* Initio bridges, actually only needed for some older ones */ {
.firmware_revision = 0x000200, .firmware_revision = 0x000200,
.model = ~0, .model = ~0,
...@@ -501,6 +516,9 @@ sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id, ...@@ -501,6 +516,9 @@ sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id,
unsigned int timeout; unsigned int timeout;
int retval = -ENOMEM; int retval = -ENOMEM;
if (function == SBP2_LOGOUT_REQUEST && fw_device_is_shutdown(device))
return 0;
orb = kzalloc(sizeof(*orb), GFP_ATOMIC); orb = kzalloc(sizeof(*orb), GFP_ATOMIC);
if (orb == NULL) if (orb == NULL)
return -ENOMEM; return -ENOMEM;
...@@ -553,20 +571,20 @@ sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id, ...@@ -553,20 +571,20 @@ sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id,
retval = -EIO; retval = -EIO;
if (sbp2_cancel_orbs(lu) == 0) { if (sbp2_cancel_orbs(lu) == 0) {
fw_error("orb reply timed out, rcode=0x%02x\n", fw_error("%s: orb reply timed out, rcode=0x%02x\n",
orb->base.rcode); lu->tgt->bus_id, orb->base.rcode);
goto out; goto out;
} }
if (orb->base.rcode != RCODE_COMPLETE) { if (orb->base.rcode != RCODE_COMPLETE) {
fw_error("management write failed, rcode 0x%02x\n", fw_error("%s: management write failed, rcode 0x%02x\n",
orb->base.rcode); lu->tgt->bus_id, orb->base.rcode);
goto out; goto out;
} }
if (STATUS_GET_RESPONSE(orb->status) != 0 || if (STATUS_GET_RESPONSE(orb->status) != 0 ||
STATUS_GET_SBP_STATUS(orb->status) != 0) { STATUS_GET_SBP_STATUS(orb->status) != 0) {
fw_error("error status: %d:%d\n", fw_error("%s: error status: %d:%d\n", lu->tgt->bus_id,
STATUS_GET_RESPONSE(orb->status), STATUS_GET_RESPONSE(orb->status),
STATUS_GET_SBP_STATUS(orb->status)); STATUS_GET_SBP_STATUS(orb->status));
goto out; goto out;
...@@ -590,29 +608,147 @@ sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id, ...@@ -590,29 +608,147 @@ sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id,
static void static void
complete_agent_reset_write(struct fw_card *card, int rcode, complete_agent_reset_write(struct fw_card *card, int rcode,
void *payload, size_t length, void *data) void *payload, size_t length, void *done)
{
complete(done);
}
static void sbp2_agent_reset(struct sbp2_logical_unit *lu)
{ {
struct fw_transaction *t = data; struct fw_device *device = fw_device(lu->tgt->unit->device.parent);
DECLARE_COMPLETION_ONSTACK(done);
struct fw_transaction t;
static u32 z;
fw_send_request(device->card, &t, TCODE_WRITE_QUADLET_REQUEST,
lu->tgt->node_id, lu->generation, device->max_speed,
lu->command_block_agent_address + SBP2_AGENT_RESET,
&z, sizeof(z), complete_agent_reset_write, &done);
wait_for_completion(&done);
}
kfree(t); static void
complete_agent_reset_write_no_wait(struct fw_card *card, int rcode,
void *payload, size_t length, void *data)
{
kfree(data);
} }
static int sbp2_agent_reset(struct sbp2_logical_unit *lu) static void sbp2_agent_reset_no_wait(struct sbp2_logical_unit *lu)
{ {
struct fw_device *device = fw_device(lu->tgt->unit->device.parent); struct fw_device *device = fw_device(lu->tgt->unit->device.parent);
struct fw_transaction *t; struct fw_transaction *t;
static u32 zero; static u32 z;
t = kzalloc(sizeof(*t), GFP_ATOMIC); t = kmalloc(sizeof(*t), GFP_ATOMIC);
if (t == NULL) if (t == NULL)
return -ENOMEM; return;
fw_send_request(device->card, t, TCODE_WRITE_QUADLET_REQUEST, fw_send_request(device->card, t, TCODE_WRITE_QUADLET_REQUEST,
lu->tgt->node_id, lu->generation, device->max_speed, lu->tgt->node_id, lu->generation, device->max_speed,
lu->command_block_agent_address + SBP2_AGENT_RESET, lu->command_block_agent_address + SBP2_AGENT_RESET,
&zero, sizeof(zero), complete_agent_reset_write, t); &z, sizeof(z), complete_agent_reset_write_no_wait, t);
}
return 0; static void sbp2_set_generation(struct sbp2_logical_unit *lu, int generation)
{
struct fw_card *card = fw_device(lu->tgt->unit->device.parent)->card;
unsigned long flags;
/* serialize with comparisons of lu->generation and card->generation */
spin_lock_irqsave(&card->lock, flags);
lu->generation = generation;
spin_unlock_irqrestore(&card->lock, flags);
}
static inline void sbp2_allow_block(struct sbp2_logical_unit *lu)
{
/*
* We may access dont_block without taking card->lock here:
* All callers of sbp2_allow_block() and all callers of sbp2_unblock()
* are currently serialized against each other.
* And a wrong result in sbp2_conditionally_block()'s access of
* dont_block is rather harmless, it simply misses its first chance.
*/
--lu->tgt->dont_block;
}
/*
* Blocks lu->tgt if all of the following conditions are met:
* - Login, INQUIRY, and high-level SCSI setup of all of the target's
* logical units have been finished (indicated by dont_block == 0).
* - lu->generation is stale.
*
* Note, scsi_block_requests() must be called while holding card->lock,
* otherwise it might foil sbp2_[conditionally_]unblock()'s attempt to
* unblock the target.
*/
static void sbp2_conditionally_block(struct sbp2_logical_unit *lu)
{
struct sbp2_target *tgt = lu->tgt;
struct fw_card *card = fw_device(tgt->unit->device.parent)->card;
struct Scsi_Host *shost =
container_of((void *)tgt, struct Scsi_Host, hostdata[0]);
unsigned long flags;
spin_lock_irqsave(&card->lock, flags);
if (!tgt->dont_block && !lu->blocked &&
lu->generation != card->generation) {
lu->blocked = true;
if (++tgt->blocked == 1) {
scsi_block_requests(shost);
fw_notify("blocked %s\n", lu->tgt->bus_id);
}
}
spin_unlock_irqrestore(&card->lock, flags);
}
/*
* Unblocks lu->tgt as soon as all its logical units can be unblocked.
* Note, it is harmless to run scsi_unblock_requests() outside the
* card->lock protected section. On the other hand, running it inside
* the section might clash with shost->host_lock.
*/
static void sbp2_conditionally_unblock(struct sbp2_logical_unit *lu)
{
struct sbp2_target *tgt = lu->tgt;
struct fw_card *card = fw_device(tgt->unit->device.parent)->card;
struct Scsi_Host *shost =
container_of((void *)tgt, struct Scsi_Host, hostdata[0]);
unsigned long flags;
bool unblock = false;
spin_lock_irqsave(&card->lock, flags);
if (lu->blocked && lu->generation == card->generation) {
lu->blocked = false;
unblock = --tgt->blocked == 0;
}
spin_unlock_irqrestore(&card->lock, flags);
if (unblock) {
scsi_unblock_requests(shost);
fw_notify("unblocked %s\n", lu->tgt->bus_id);
}
}
/*
* Prevents future blocking of tgt and unblocks it.
* Note, it is harmless to run scsi_unblock_requests() outside the
* card->lock protected section. On the other hand, running it inside
* the section might clash with shost->host_lock.
*/
static void sbp2_unblock(struct sbp2_target *tgt)
{
struct fw_card *card = fw_device(tgt->unit->device.parent)->card;
struct Scsi_Host *shost =
container_of((void *)tgt, struct Scsi_Host, hostdata[0]);
unsigned long flags;
spin_lock_irqsave(&card->lock, flags);
++tgt->dont_block;
spin_unlock_irqrestore(&card->lock, flags);
scsi_unblock_requests(shost);
} }
static void sbp2_release_target(struct kref *kref) static void sbp2_release_target(struct kref *kref)
...@@ -621,23 +757,24 @@ static void sbp2_release_target(struct kref *kref) ...@@ -621,23 +757,24 @@ static void sbp2_release_target(struct kref *kref)
struct sbp2_logical_unit *lu, *next; struct sbp2_logical_unit *lu, *next;
struct Scsi_Host *shost = struct Scsi_Host *shost =
container_of((void *)tgt, struct Scsi_Host, hostdata[0]); container_of((void *)tgt, struct Scsi_Host, hostdata[0]);
struct fw_device *device = fw_device(tgt->unit->device.parent);
/* prevent deadlocks */
sbp2_unblock(tgt);
list_for_each_entry_safe(lu, next, &tgt->lu_list, link) { list_for_each_entry_safe(lu, next, &tgt->lu_list, link) {
if (lu->sdev) if (lu->sdev) {
scsi_remove_device(lu->sdev); scsi_remove_device(lu->sdev);
scsi_device_put(lu->sdev);
if (!fw_device_is_shutdown(device)) }
sbp2_send_management_orb(lu, tgt->node_id, sbp2_send_management_orb(lu, tgt->node_id, lu->generation,
lu->generation, SBP2_LOGOUT_REQUEST, SBP2_LOGOUT_REQUEST, lu->login_id, NULL);
lu->login_id, NULL);
fw_core_remove_address_handler(&lu->address_handler); fw_core_remove_address_handler(&lu->address_handler);
list_del(&lu->link); list_del(&lu->link);
kfree(lu); kfree(lu);
} }
scsi_remove_host(shost); scsi_remove_host(shost);
fw_notify("released %s\n", tgt->unit->device.bus_id); fw_notify("released %s\n", tgt->bus_id);
put_device(&tgt->unit->device); put_device(&tgt->unit->device);
scsi_host_put(shost); scsi_host_put(shost);
...@@ -666,33 +803,43 @@ static void sbp2_login(struct work_struct *work) ...@@ -666,33 +803,43 @@ static void sbp2_login(struct work_struct *work)
{ {
struct sbp2_logical_unit *lu = struct sbp2_logical_unit *lu =
container_of(work, struct sbp2_logical_unit, work.work); container_of(work, struct sbp2_logical_unit, work.work);
struct Scsi_Host *shost = struct sbp2_target *tgt = lu->tgt;
container_of((void *)lu->tgt, struct Scsi_Host, hostdata[0]); struct fw_device *device = fw_device(tgt->unit->device.parent);
struct Scsi_Host *shost;
struct scsi_device *sdev; struct scsi_device *sdev;
struct scsi_lun eight_bytes_lun; struct scsi_lun eight_bytes_lun;
struct fw_unit *unit = lu->tgt->unit;
struct fw_device *device = fw_device(unit->device.parent);
struct sbp2_login_response response; struct sbp2_login_response response;
int generation, node_id, local_node_id; int generation, node_id, local_node_id;
if (fw_device_is_shutdown(device))
goto out;
generation = device->generation; generation = device->generation;
smp_rmb(); /* node_id must not be older than generation */ smp_rmb(); /* node_id must not be older than generation */
node_id = device->node_id; node_id = device->node_id;
local_node_id = device->card->node_id; local_node_id = device->card->node_id;
/* If this is a re-login attempt, log out, or we might be rejected. */
if (lu->sdev)
sbp2_send_management_orb(lu, device->node_id, generation,
SBP2_LOGOUT_REQUEST, lu->login_id, NULL);
if (sbp2_send_management_orb(lu, node_id, generation, if (sbp2_send_management_orb(lu, node_id, generation,
SBP2_LOGIN_REQUEST, lu->lun, &response) < 0) { SBP2_LOGIN_REQUEST, lu->lun, &response) < 0) {
if (lu->retries++ < 5) if (lu->retries++ < 5) {
sbp2_queue_work(lu, DIV_ROUND_UP(HZ, 5)); sbp2_queue_work(lu, DIV_ROUND_UP(HZ, 5));
else } else {
fw_error("failed to login to %s LUN %04x\n", fw_error("%s: failed to login to LUN %04x\n",
unit->device.bus_id, lu->lun); tgt->bus_id, lu->lun);
/* Let any waiting I/O fail from now on. */
sbp2_unblock(lu->tgt);
}
goto out; goto out;
} }
lu->generation = generation; tgt->node_id = node_id;
lu->tgt->node_id = node_id; tgt->address_high = local_node_id << 16;
lu->tgt->address_high = local_node_id << 16; sbp2_set_generation(lu, generation);
/* Get command block agent offset and login id. */ /* Get command block agent offset and login id. */
lu->command_block_agent_address = lu->command_block_agent_address =
...@@ -700,8 +847,8 @@ static void sbp2_login(struct work_struct *work) ...@@ -700,8 +847,8 @@ static void sbp2_login(struct work_struct *work)
response.command_block_agent.low; response.command_block_agent.low;
lu->login_id = LOGIN_RESPONSE_GET_LOGIN_ID(response); lu->login_id = LOGIN_RESPONSE_GET_LOGIN_ID(response);
fw_notify("logged in to %s LUN %04x (%d retries)\n", fw_notify("%s: logged in to LUN %04x (%d retries)\n",
unit->device.bus_id, lu->lun, lu->retries); tgt->bus_id, lu->lun, lu->retries);
#if 0 #if 0
/* FIXME: The linux1394 sbp2 does this last step. */ /* FIXME: The linux1394 sbp2 does this last step. */
...@@ -711,26 +858,62 @@ static void sbp2_login(struct work_struct *work) ...@@ -711,26 +858,62 @@ static void sbp2_login(struct work_struct *work)
PREPARE_DELAYED_WORK(&lu->work, sbp2_reconnect); PREPARE_DELAYED_WORK(&lu->work, sbp2_reconnect);
sbp2_agent_reset(lu); sbp2_agent_reset(lu);
/* This was a re-login. */
if (lu->sdev) {
sbp2_cancel_orbs(lu);
sbp2_conditionally_unblock(lu);
goto out;
}
if (lu->tgt->workarounds & SBP2_WORKAROUND_DELAY_INQUIRY)
ssleep(SBP2_INQUIRY_DELAY);
memset(&eight_bytes_lun, 0, sizeof(eight_bytes_lun)); memset(&eight_bytes_lun, 0, sizeof(eight_bytes_lun));
eight_bytes_lun.scsi_lun[0] = (lu->lun >> 8) & 0xff; eight_bytes_lun.scsi_lun[0] = (lu->lun >> 8) & 0xff;
eight_bytes_lun.scsi_lun[1] = lu->lun & 0xff; eight_bytes_lun.scsi_lun[1] = lu->lun & 0xff;
shost = container_of((void *)tgt, struct Scsi_Host, hostdata[0]);
sdev = __scsi_add_device(shost, 0, 0, sdev = __scsi_add_device(shost, 0, 0,
scsilun_to_int(&eight_bytes_lun), lu); scsilun_to_int(&eight_bytes_lun), lu);
if (IS_ERR(sdev)) { /*
sbp2_send_management_orb(lu, node_id, generation, * FIXME: We are unable to perform reconnects while in sbp2_login().
SBP2_LOGOUT_REQUEST, lu->login_id, NULL); * Therefore __scsi_add_device() will get into trouble if a bus reset
/* * happens in parallel. It will either fail or leave us with an
* Set this back to sbp2_login so we fall back and * unusable sdev. As a workaround we check for this and retry the
* retry login on bus reset. * whole login and SCSI probing.
*/ */
PREPARE_DELAYED_WORK(&lu->work, sbp2_login);
} else { /* Reported error during __scsi_add_device() */
lu->sdev = sdev; if (IS_ERR(sdev))
goto out_logout_login;
/* Unreported error during __scsi_add_device() */
smp_rmb(); /* get current card generation */
if (generation != device->card->generation) {
scsi_remove_device(sdev);
scsi_device_put(sdev); scsi_device_put(sdev);
goto out_logout_login;
} }
/* No error during __scsi_add_device() */
lu->sdev = sdev;
sbp2_allow_block(lu);
goto out;
out_logout_login:
smp_rmb(); /* generation may have changed */
generation = device->generation;
smp_rmb(); /* node_id must not be older than generation */
sbp2_send_management_orb(lu, device->node_id, generation,
SBP2_LOGOUT_REQUEST, lu->login_id, NULL);
/*
* If a bus reset happened, sbp2_update will have requeued
* lu->work already. Reset the work from reconnect to login.
*/
PREPARE_DELAYED_WORK(&lu->work, sbp2_login);
out: out:
sbp2_target_put(lu->tgt); sbp2_target_put(tgt);
} }
static int sbp2_add_logical_unit(struct sbp2_target *tgt, int lun_entry) static int sbp2_add_logical_unit(struct sbp2_target *tgt, int lun_entry)
...@@ -755,6 +938,8 @@ static int sbp2_add_logical_unit(struct sbp2_target *tgt, int lun_entry) ...@@ -755,6 +938,8 @@ static int sbp2_add_logical_unit(struct sbp2_target *tgt, int lun_entry)
lu->sdev = NULL; lu->sdev = NULL;
lu->lun = lun_entry & 0xffff; lu->lun = lun_entry & 0xffff;
lu->retries = 0; lu->retries = 0;
lu->blocked = false;
++tgt->dont_block;
INIT_LIST_HEAD(&lu->orb_list); INIT_LIST_HEAD(&lu->orb_list);
INIT_DELAYED_WORK(&lu->work, sbp2_login); INIT_DELAYED_WORK(&lu->work, sbp2_login);
...@@ -813,7 +998,7 @@ static int sbp2_scan_unit_dir(struct sbp2_target *tgt, u32 *directory, ...@@ -813,7 +998,7 @@ static int sbp2_scan_unit_dir(struct sbp2_target *tgt, u32 *directory,
if (timeout > tgt->mgt_orb_timeout) if (timeout > tgt->mgt_orb_timeout)
fw_notify("%s: config rom contains %ds " fw_notify("%s: config rom contains %ds "
"management ORB timeout, limiting " "management ORB timeout, limiting "
"to %ds\n", tgt->unit->device.bus_id, "to %ds\n", tgt->bus_id,
timeout / 1000, timeout / 1000,
tgt->mgt_orb_timeout / 1000); tgt->mgt_orb_timeout / 1000);
break; break;
...@@ -836,12 +1021,12 @@ static void sbp2_init_workarounds(struct sbp2_target *tgt, u32 model, ...@@ -836,12 +1021,12 @@ static void sbp2_init_workarounds(struct sbp2_target *tgt, u32 model,
u32 firmware_revision) u32 firmware_revision)
{ {
int i; int i;
unsigned w = sbp2_param_workarounds; unsigned int w = sbp2_param_workarounds;
if (w) if (w)
fw_notify("Please notify linux1394-devel@lists.sourceforge.net " fw_notify("Please notify linux1394-devel@lists.sourceforge.net "
"if you need the workarounds parameter for %s\n", "if you need the workarounds parameter for %s\n",
tgt->unit->device.bus_id); tgt->bus_id);
if (w & SBP2_WORKAROUND_OVERRIDE) if (w & SBP2_WORKAROUND_OVERRIDE)
goto out; goto out;
...@@ -863,8 +1048,7 @@ static void sbp2_init_workarounds(struct sbp2_target *tgt, u32 model, ...@@ -863,8 +1048,7 @@ static void sbp2_init_workarounds(struct sbp2_target *tgt, u32 model,
if (w) if (w)
fw_notify("Workarounds for %s: 0x%x " fw_notify("Workarounds for %s: 0x%x "
"(firmware_revision 0x%06x, model_id 0x%06x)\n", "(firmware_revision 0x%06x, model_id 0x%06x)\n",
tgt->unit->device.bus_id, tgt->bus_id, w, firmware_revision, model);
w, firmware_revision, model);
tgt->workarounds = w; tgt->workarounds = w;
} }
...@@ -888,6 +1072,7 @@ static int sbp2_probe(struct device *dev) ...@@ -888,6 +1072,7 @@ static int sbp2_probe(struct device *dev)
tgt->unit = unit; tgt->unit = unit;
kref_init(&tgt->kref); kref_init(&tgt->kref);
INIT_LIST_HEAD(&tgt->lu_list); INIT_LIST_HEAD(&tgt->lu_list);
tgt->bus_id = unit->device.bus_id;
if (fw_device_enable_phys_dma(device) < 0) if (fw_device_enable_phys_dma(device) < 0)
goto fail_shost_put; goto fail_shost_put;
...@@ -938,10 +1123,13 @@ static void sbp2_reconnect(struct work_struct *work) ...@@ -938,10 +1123,13 @@ static void sbp2_reconnect(struct work_struct *work)
{ {
struct sbp2_logical_unit *lu = struct sbp2_logical_unit *lu =
container_of(work, struct sbp2_logical_unit, work.work); container_of(work, struct sbp2_logical_unit, work.work);
struct fw_unit *unit = lu->tgt->unit; struct sbp2_target *tgt = lu->tgt;
struct fw_device *device = fw_device(unit->device.parent); struct fw_device *device = fw_device(tgt->unit->device.parent);
int generation, node_id, local_node_id; int generation, node_id, local_node_id;
if (fw_device_is_shutdown(device))
goto out;
generation = device->generation; generation = device->generation;
smp_rmb(); /* node_id must not be older than generation */ smp_rmb(); /* node_id must not be older than generation */
node_id = device->node_id; node_id = device->node_id;
...@@ -950,10 +1138,17 @@ static void sbp2_reconnect(struct work_struct *work) ...@@ -950,10 +1138,17 @@ static void sbp2_reconnect(struct work_struct *work)
if (sbp2_send_management_orb(lu, node_id, generation, if (sbp2_send_management_orb(lu, node_id, generation,
SBP2_RECONNECT_REQUEST, SBP2_RECONNECT_REQUEST,
lu->login_id, NULL) < 0) { lu->login_id, NULL) < 0) {
if (lu->retries++ >= 5) { /*
fw_error("failed to reconnect to %s\n", * If reconnect was impossible even though we are in the
unit->device.bus_id); * current generation, fall back and try to log in again.
/* Fall back and try to log in again. */ *
* We could check for "Function rejected" status, but
* looking at the bus generation as simpler and more general.
*/
smp_rmb(); /* get current card generation */
if (generation == device->card->generation ||
lu->retries++ >= 5) {
fw_error("%s: failed to reconnect\n", tgt->bus_id);
lu->retries = 0; lu->retries = 0;
PREPARE_DELAYED_WORK(&lu->work, sbp2_login); PREPARE_DELAYED_WORK(&lu->work, sbp2_login);
} }
...@@ -961,17 +1156,18 @@ static void sbp2_reconnect(struct work_struct *work) ...@@ -961,17 +1156,18 @@ static void sbp2_reconnect(struct work_struct *work)
goto out; goto out;
} }
lu->generation = generation; tgt->node_id = node_id;
lu->tgt->node_id = node_id; tgt->address_high = local_node_id << 16;
lu->tgt->address_high = local_node_id << 16; sbp2_set_generation(lu, generation);
fw_notify("reconnected to %s LUN %04x (%d retries)\n", fw_notify("%s: reconnected to LUN %04x (%d retries)\n",
unit->device.bus_id, lu->lun, lu->retries); tgt->bus_id, lu->lun, lu->retries);
sbp2_agent_reset(lu); sbp2_agent_reset(lu);
sbp2_cancel_orbs(lu); sbp2_cancel_orbs(lu);
sbp2_conditionally_unblock(lu);
out: out:
sbp2_target_put(lu->tgt); sbp2_target_put(tgt);
} }
static void sbp2_update(struct fw_unit *unit) static void sbp2_update(struct fw_unit *unit)
...@@ -986,6 +1182,7 @@ static void sbp2_update(struct fw_unit *unit) ...@@ -986,6 +1182,7 @@ static void sbp2_update(struct fw_unit *unit)
* Iteration over tgt->lu_list is therefore safe here. * Iteration over tgt->lu_list is therefore safe here.
*/ */
list_for_each_entry(lu, &tgt->lu_list, link) { list_for_each_entry(lu, &tgt->lu_list, link) {
sbp2_conditionally_block(lu);
lu->retries = 0; lu->retries = 0;
sbp2_queue_work(lu, 0); sbp2_queue_work(lu, 0);
} }
...@@ -1063,7 +1260,7 @@ complete_command_orb(struct sbp2_orb *base_orb, struct sbp2_status *status) ...@@ -1063,7 +1260,7 @@ complete_command_orb(struct sbp2_orb *base_orb, struct sbp2_status *status)
if (status != NULL) { if (status != NULL) {
if (STATUS_GET_DEAD(*status)) if (STATUS_GET_DEAD(*status))
sbp2_agent_reset(orb->lu); sbp2_agent_reset_no_wait(orb->lu);
switch (STATUS_GET_RESPONSE(*status)) { switch (STATUS_GET_RESPONSE(*status)) {
case SBP2_STATUS_REQUEST_COMPLETE: case SBP2_STATUS_REQUEST_COMPLETE:
...@@ -1089,6 +1286,7 @@ complete_command_orb(struct sbp2_orb *base_orb, struct sbp2_status *status) ...@@ -1089,6 +1286,7 @@ complete_command_orb(struct sbp2_orb *base_orb, struct sbp2_status *status)
* or when sending the write (less likely). * or when sending the write (less likely).
*/ */
result = DID_BUS_BUSY << 16; result = DID_BUS_BUSY << 16;
sbp2_conditionally_block(orb->lu);
} }
dma_unmap_single(device->card->device, orb->base.request_bus, dma_unmap_single(device->card->device, orb->base.request_bus,
...@@ -1197,7 +1395,7 @@ static int sbp2_scsi_queuecommand(struct scsi_cmnd *cmd, scsi_done_fn_t done) ...@@ -1197,7 +1395,7 @@ static int sbp2_scsi_queuecommand(struct scsi_cmnd *cmd, scsi_done_fn_t done)
struct sbp2_logical_unit *lu = cmd->device->hostdata; struct sbp2_logical_unit *lu = cmd->device->hostdata;
struct fw_device *device = fw_device(lu->tgt->unit->device.parent); struct fw_device *device = fw_device(lu->tgt->unit->device.parent);
struct sbp2_command_orb *orb; struct sbp2_command_orb *orb;
unsigned max_payload; unsigned int max_payload;
int retval = SCSI_MLQUEUE_HOST_BUSY; int retval = SCSI_MLQUEUE_HOST_BUSY;
/* /*
...@@ -1275,6 +1473,10 @@ static int sbp2_scsi_slave_alloc(struct scsi_device *sdev) ...@@ -1275,6 +1473,10 @@ static int sbp2_scsi_slave_alloc(struct scsi_device *sdev)
{ {
struct sbp2_logical_unit *lu = sdev->hostdata; struct sbp2_logical_unit *lu = sdev->hostdata;
/* (Re-)Adding logical units via the SCSI stack is not supported. */
if (!lu)
return -ENOSYS;
sdev->allow_restart = 1; sdev->allow_restart = 1;
/* /*
...@@ -1319,7 +1521,7 @@ static int sbp2_scsi_abort(struct scsi_cmnd *cmd) ...@@ -1319,7 +1521,7 @@ static int sbp2_scsi_abort(struct scsi_cmnd *cmd)
{ {
struct sbp2_logical_unit *lu = cmd->device->hostdata; struct sbp2_logical_unit *lu = cmd->device->hostdata;
fw_notify("sbp2_scsi_abort\n"); fw_notify("%s: sbp2_scsi_abort\n", lu->tgt->bus_id);
sbp2_agent_reset(lu); sbp2_agent_reset(lu);
sbp2_cancel_orbs(lu); sbp2_cancel_orbs(lu);
......
...@@ -183,6 +183,9 @@ MODULE_PARM_DESC(exclusive_login, "Exclusive login to sbp2 device " ...@@ -183,6 +183,9 @@ MODULE_PARM_DESC(exclusive_login, "Exclusive login to sbp2 device "
* Avoids access beyond actual disk limits on devices with an off-by-one bug. * Avoids access beyond actual disk limits on devices with an off-by-one bug.
* Don't use this with devices which don't have this bug. * Don't use this with devices which don't have this bug.
* *
* - delay inquiry
* Wait extra SBP2_INQUIRY_DELAY seconds after login before SCSI inquiry.
*
* - override internal blacklist * - override internal blacklist
* Instead of adding to the built-in blacklist, use only the workarounds * Instead of adding to the built-in blacklist, use only the workarounds
* specified in the module load parameter. * specified in the module load parameter.
...@@ -195,6 +198,7 @@ MODULE_PARM_DESC(workarounds, "Work around device bugs (default = 0" ...@@ -195,6 +198,7 @@ MODULE_PARM_DESC(workarounds, "Work around device bugs (default = 0"
", 36 byte inquiry = " __stringify(SBP2_WORKAROUND_INQUIRY_36) ", 36 byte inquiry = " __stringify(SBP2_WORKAROUND_INQUIRY_36)
", skip mode page 8 = " __stringify(SBP2_WORKAROUND_MODE_SENSE_8) ", skip mode page 8 = " __stringify(SBP2_WORKAROUND_MODE_SENSE_8)
", fix capacity = " __stringify(SBP2_WORKAROUND_FIX_CAPACITY) ", fix capacity = " __stringify(SBP2_WORKAROUND_FIX_CAPACITY)
", delay inquiry = " __stringify(SBP2_WORKAROUND_DELAY_INQUIRY)
", override internal blacklist = " __stringify(SBP2_WORKAROUND_OVERRIDE) ", override internal blacklist = " __stringify(SBP2_WORKAROUND_OVERRIDE)
", or a combination)"); ", or a combination)");
...@@ -357,6 +361,11 @@ static const struct { ...@@ -357,6 +361,11 @@ static const struct {
.workarounds = SBP2_WORKAROUND_INQUIRY_36 | .workarounds = SBP2_WORKAROUND_INQUIRY_36 |
SBP2_WORKAROUND_MODE_SENSE_8, SBP2_WORKAROUND_MODE_SENSE_8,
}, },
/* DViCO Momobay FX-3A with TSB42AA9A bridge */ {
.firmware_revision = 0x002800,
.model_id = 0x000000,
.workarounds = SBP2_WORKAROUND_DELAY_INQUIRY,
},
/* Initio bridges, actually only needed for some older ones */ { /* Initio bridges, actually only needed for some older ones */ {
.firmware_revision = 0x000200, .firmware_revision = 0x000200,
.model_id = SBP2_ROM_VALUE_WILDCARD, .model_id = SBP2_ROM_VALUE_WILDCARD,
...@@ -914,6 +923,9 @@ static int sbp2_start_device(struct sbp2_lu *lu) ...@@ -914,6 +923,9 @@ static int sbp2_start_device(struct sbp2_lu *lu)
sbp2_agent_reset(lu, 1); sbp2_agent_reset(lu, 1);
sbp2_max_speed_and_size(lu); sbp2_max_speed_and_size(lu);
if (lu->workarounds & SBP2_WORKAROUND_DELAY_INQUIRY)
ssleep(SBP2_INQUIRY_DELAY);
error = scsi_add_device(lu->shost, 0, lu->ud->id, 0); error = scsi_add_device(lu->shost, 0, lu->ud->id, 0);
if (error) { if (error) {
SBP2_ERR("scsi_add_device failed"); SBP2_ERR("scsi_add_device failed");
...@@ -1962,6 +1974,9 @@ static int sbp2scsi_slave_alloc(struct scsi_device *sdev) ...@@ -1962,6 +1974,9 @@ static int sbp2scsi_slave_alloc(struct scsi_device *sdev)
{ {
struct sbp2_lu *lu = (struct sbp2_lu *)sdev->host->hostdata[0]; struct sbp2_lu *lu = (struct sbp2_lu *)sdev->host->hostdata[0];
if (sdev->lun != 0 || sdev->id != lu->ud->id || sdev->channel != 0)
return -ENODEV;
lu->sdev = sdev; lu->sdev = sdev;
sdev->allow_restart = 1; sdev->allow_restart = 1;
......
...@@ -343,6 +343,8 @@ enum sbp2lu_state_types { ...@@ -343,6 +343,8 @@ enum sbp2lu_state_types {
#define SBP2_WORKAROUND_INQUIRY_36 0x2 #define SBP2_WORKAROUND_INQUIRY_36 0x2
#define SBP2_WORKAROUND_MODE_SENSE_8 0x4 #define SBP2_WORKAROUND_MODE_SENSE_8 0x4
#define SBP2_WORKAROUND_FIX_CAPACITY 0x8 #define SBP2_WORKAROUND_FIX_CAPACITY 0x8
#define SBP2_WORKAROUND_DELAY_INQUIRY 0x10
#define SBP2_INQUIRY_DELAY 12
#define SBP2_WORKAROUND_OVERRIDE 0x100 #define SBP2_WORKAROUND_OVERRIDE 0x100
#endif /* SBP2_H */ #endif /* SBP2_H */
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