2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2024-12-19 02:34:01 +08:00

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
This commit is contained in:
Linus Torvalds 2008-02-26 07:48:27 -08:00
commit 98c1fc934c
7 changed files with 350 additions and 109 deletions

View File

@ -36,14 +36,15 @@ available (notebooks) or too slow for extensive debug information (like ACPI).
Drivers
-------
The OHCI-1394 drivers in drivers/firewire and drivers/ieee1394 initialize
the OHCI-1394 controllers to a working state and can be used to enable
physical DMA. By default you only have to load the driver, and physical
DMA access will be granted to all remote nodes, but it can be turned off
when using the ohci1394 driver.
The ohci1394 driver in drivers/ieee1394 initializes the OHCI-1394 controllers
to a working state and enables physical DMA by default for all remote nodes.
This can be turned off by ohci1394's module parameter phys_dma=0.
Because these drivers depend on the PCI enumeration to be completed, an
initialization routine which can runs pretty early (long before console_init(),
The alternative firewire-ohci driver in drivers/firewire uses filtered physical
DMA, hence is not yet suitable for remote debugging.
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.
To activate it, enable CONFIG_PROVIDE_OHCI1394_DMA_INIT (Kernel hacking menu:

View File

@ -109,15 +109,17 @@ static int fw_device_op_open(struct inode *inode, struct file *file)
struct client *client;
unsigned long flags;
device = fw_device_from_devt(inode->i_rdev);
device = fw_device_get_by_devt(inode->i_rdev);
if (device == NULL)
return -ENODEV;
client = kzalloc(sizeof(*client), GFP_KERNEL);
if (client == NULL)
if (client == NULL) {
fw_device_put(device);
return -ENOMEM;
}
client->device = fw_device_get(device);
client->device = device;
INIT_LIST_HEAD(&client->event_list);
INIT_LIST_HEAD(&client->resource_list);
spin_lock_init(&client->lock);
@ -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_iso_context *context;
/* We only support one context at this time. */
if (client->iso_context != NULL)
return -EBUSY;
if (request->channel > 63)
return -EINVAL;
@ -790,8 +796,9 @@ static int ioctl_start_iso(struct client *client, void *buffer)
{
struct fw_cdev_start_iso *request = buffer;
if (request->handle != 0)
if (client->iso_context == NULL || request->handle != 0)
return -EINVAL;
if (client->iso_context->type == FW_ISO_CONTEXT_RECEIVE) {
if (request->tags == 0 || request->tags > 15)
return -EINVAL;
@ -808,7 +815,7 @@ static int ioctl_stop_iso(struct client *client, void *buffer)
{
struct fw_cdev_stop_iso *request = buffer;
if (request->handle != 0)
if (client->iso_context == NULL || request->handle != 0)
return -EINVAL;
return fw_iso_context_stop(client->iso_context);

View File

@ -358,12 +358,9 @@ static ssize_t
guid_show(struct device *dev, struct device_attribute *attr, char *buf)
{
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%016llx\n",
(unsigned long long)guid);
return snprintf(buf, PAGE_SIZE, "0x%08x%08x\n",
device->config_rom[3], device->config_rom[4]);
}
static struct device_attribute fw_device_attributes[] = {
@ -610,12 +607,14 @@ static DECLARE_RWSEM(idr_rwsem);
static DEFINE_IDR(fw_device_idr);
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;
down_read(&idr_rwsem);
device = idr_find(&fw_device_idr, MINOR(devt));
if (device)
fw_device_get(device);
up_read(&idr_rwsem);
return device;
@ -627,13 +626,14 @@ static void fw_device_shutdown(struct work_struct *work)
container_of(work, struct fw_device, work.work);
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);
device_for_each_child(&device->device, NULL, shutdown_unit);
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 = {
@ -682,10 +682,13 @@ static void fw_device_init(struct work_struct *work)
}
err = -ENOMEM;
fw_device_get(device);
down_write(&idr_rwsem);
if (idr_pre_get(&fw_device_idr, GFP_KERNEL))
err = idr_get_new(&fw_device_idr, device, &minor);
up_write(&idr_rwsem);
if (err < 0)
goto error;
@ -717,13 +720,22 @@ static void fw_device_init(struct work_struct *work)
*/
if (atomic_cmpxchg(&device->state,
FW_DEVICE_INITIALIZING,
FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN)
FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN) {
fw_device_shutdown(&device->work.work);
else
fw_notify("created new fw device %s "
"(%d config rom retries, S%d00)\n",
device->device.bus_id, device->config_rom_retries,
1 << device->max_speed);
} else {
if (device->config_rom_retries)
fw_notify("created device %s: GUID %08x%08x, S%d00, "
"%d config ROM retries\n",
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
@ -741,7 +753,9 @@ static void fw_device_init(struct work_struct *work)
idr_remove(&fw_device_idr, minor);
up_write(&idr_rwsem);
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)

View File

@ -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_by_devt(dev_t devt);
void fw_device_put(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_remove(struct fw_device *device);
struct fw_device *fw_device_from_devt(dev_t devt);
extern int fw_cdev_major;
struct fw_unit {

View File

@ -28,14 +28,15 @@
* 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/mod_devicetable.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mod_devicetable.h>
#include <linux/device.h>
#include <linux/scatterlist.h>
#include <linux/dma-mapping.h>
#include <linux/blkdev.h>
#include <linux/string.h>
#include <linux/stringify.h>
#include <linux/timer.h>
@ -47,9 +48,9 @@
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include "fw-transaction.h"
#include "fw-topology.h"
#include "fw-device.h"
#include "fw-topology.h"
#include "fw-transaction.h"
/*
* 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 "
* 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.
*
* - delay inquiry
* Wait extra SBP2_INQUIRY_DELAY seconds after login before SCSI inquiry.
*
* - override internal blacklist
* Instead of adding to the built-in blacklist, use only the workarounds
* specified in the module load parameter.
@ -91,6 +95,8 @@ MODULE_PARM_DESC(exclusive_login, "Exclusive login to sbp2 device "
#define SBP2_WORKAROUND_INQUIRY_36 0x2
#define SBP2_WORKAROUND_MODE_SENSE_8 0x4
#define SBP2_WORKAROUND_FIX_CAPACITY 0x8
#define SBP2_WORKAROUND_DELAY_INQUIRY 0x10
#define SBP2_INQUIRY_DELAY 12
#define SBP2_WORKAROUND_OVERRIDE 0x100
static int sbp2_param_workarounds;
@ -100,6 +106,7 @@ MODULE_PARM_DESC(workarounds, "Work around device bugs (default = 0"
", 36 byte inquiry = " __stringify(SBP2_WORKAROUND_INQUIRY_36)
", skip mode page 8 = " __stringify(SBP2_WORKAROUND_MODE_SENSE_8)
", fix capacity = " __stringify(SBP2_WORKAROUND_FIX_CAPACITY)
", delay inquiry = " __stringify(SBP2_WORKAROUND_DELAY_INQUIRY)
", override internal blacklist = " __stringify(SBP2_WORKAROUND_OVERRIDE)
", or a combination)");
@ -132,6 +139,7 @@ struct sbp2_logical_unit {
int generation;
int retries;
struct delayed_work work;
bool blocked;
};
/*
@ -141,16 +149,18 @@ struct sbp2_logical_unit {
struct sbp2_target {
struct kref kref;
struct fw_unit *unit;
const char *bus_id;
struct list_head lu_list;
u64 management_agent_address;
int directory_id;
int node_id;
int address_high;
unsigned workarounds;
struct list_head lu_list;
unsigned int workarounds;
unsigned int mgt_orb_timeout;
int dont_block; /* counter for each logical unit */
int blocked; /* ditto */
};
/*
@ -160,7 +170,7 @@ struct sbp2_target {
*/
#define SBP2_MIN_LOGIN_ORB_TIMEOUT 5000U /* 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_MAX_SG_ELEMENT_LENGTH 0xf000
@ -297,7 +307,7 @@ struct sbp2_command_orb {
static const struct {
u32 firmware_revision;
u32 model;
unsigned workarounds;
unsigned int workarounds;
} sbp2_workarounds_table[] = {
/* DViCO Momobay CX-1 with TSB42AA9 bridge */ {
.firmware_revision = 0x002800,
@ -305,6 +315,11 @@ static const struct {
.workarounds = SBP2_WORKAROUND_INQUIRY_36 |
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 */ {
.firmware_revision = 0x000200,
.model = ~0,
@ -501,6 +516,9 @@ sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id,
unsigned int timeout;
int retval = -ENOMEM;
if (function == SBP2_LOGOUT_REQUEST && fw_device_is_shutdown(device))
return 0;
orb = kzalloc(sizeof(*orb), GFP_ATOMIC);
if (orb == NULL)
return -ENOMEM;
@ -553,20 +571,20 @@ sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id,
retval = -EIO;
if (sbp2_cancel_orbs(lu) == 0) {
fw_error("orb reply timed out, rcode=0x%02x\n",
orb->base.rcode);
fw_error("%s: orb reply timed out, rcode=0x%02x\n",
lu->tgt->bus_id, orb->base.rcode);
goto out;
}
if (orb->base.rcode != RCODE_COMPLETE) {
fw_error("management write failed, rcode 0x%02x\n",
orb->base.rcode);
fw_error("%s: management write failed, rcode 0x%02x\n",
lu->tgt->bus_id, orb->base.rcode);
goto out;
}
if (STATUS_GET_RESPONSE(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_SBP_STATUS(orb->status));
goto out;
@ -590,29 +608,147 @@ sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id,
static void
complete_agent_reset_write(struct fw_card *card, int rcode,
void *payload, size_t length, void *data)
void *payload, size_t length, void *done)
{
struct fw_transaction *t = data;
kfree(t);
complete(done);
}
static int sbp2_agent_reset(struct sbp2_logical_unit *lu)
static void sbp2_agent_reset(struct sbp2_logical_unit *lu)
{
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);
}
static void
complete_agent_reset_write_no_wait(struct fw_card *card, int rcode,
void *payload, size_t length, void *data)
{
kfree(data);
}
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_transaction *t;
static u32 zero;
static u32 z;
t = kzalloc(sizeof(*t), GFP_ATOMIC);
t = kmalloc(sizeof(*t), GFP_ATOMIC);
if (t == NULL)
return -ENOMEM;
return;
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,
&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)
@ -621,23 +757,24 @@ static void sbp2_release_target(struct kref *kref)
struct sbp2_logical_unit *lu, *next;
struct Scsi_Host *shost =
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) {
if (lu->sdev)
if (lu->sdev) {
scsi_remove_device(lu->sdev);
if (!fw_device_is_shutdown(device))
sbp2_send_management_orb(lu, tgt->node_id,
lu->generation, SBP2_LOGOUT_REQUEST,
lu->login_id, NULL);
scsi_device_put(lu->sdev);
}
sbp2_send_management_orb(lu, tgt->node_id, lu->generation,
SBP2_LOGOUT_REQUEST, lu->login_id, NULL);
fw_core_remove_address_handler(&lu->address_handler);
list_del(&lu->link);
kfree(lu);
}
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);
scsi_host_put(shost);
@ -666,33 +803,43 @@ static void sbp2_login(struct work_struct *work)
{
struct sbp2_logical_unit *lu =
container_of(work, struct sbp2_logical_unit, work.work);
struct Scsi_Host *shost =
container_of((void *)lu->tgt, struct Scsi_Host, hostdata[0]);
struct sbp2_target *tgt = lu->tgt;
struct fw_device *device = fw_device(tgt->unit->device.parent);
struct Scsi_Host *shost;
struct scsi_device *sdev;
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;
int generation, node_id, local_node_id;
if (fw_device_is_shutdown(device))
goto out;
generation = device->generation;
smp_rmb(); /* node_id must not be older than generation */
node_id = device->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,
SBP2_LOGIN_REQUEST, lu->lun, &response) < 0) {
if (lu->retries++ < 5)
if (lu->retries++ < 5) {
sbp2_queue_work(lu, DIV_ROUND_UP(HZ, 5));
else
fw_error("failed to login to %s LUN %04x\n",
unit->device.bus_id, lu->lun);
} else {
fw_error("%s: failed to login to LUN %04x\n",
tgt->bus_id, lu->lun);
/* Let any waiting I/O fail from now on. */
sbp2_unblock(lu->tgt);
}
goto out;
}
lu->generation = generation;
lu->tgt->node_id = node_id;
lu->tgt->address_high = local_node_id << 16;
tgt->node_id = node_id;
tgt->address_high = local_node_id << 16;
sbp2_set_generation(lu, generation);
/* Get command block agent offset and login id. */
lu->command_block_agent_address =
@ -700,8 +847,8 @@ static void sbp2_login(struct work_struct *work)
response.command_block_agent.low;
lu->login_id = LOGIN_RESPONSE_GET_LOGIN_ID(response);
fw_notify("logged in to %s LUN %04x (%d retries)\n",
unit->device.bus_id, lu->lun, lu->retries);
fw_notify("%s: logged in to LUN %04x (%d retries)\n",
tgt->bus_id, lu->lun, lu->retries);
#if 0
/* FIXME: The linux1394 sbp2 does this last step. */
@ -711,26 +858,62 @@ static void sbp2_login(struct work_struct *work)
PREPARE_DELAYED_WORK(&lu->work, sbp2_reconnect);
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));
eight_bytes_lun.scsi_lun[0] = (lu->lun >> 8) & 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,
scsilun_to_int(&eight_bytes_lun), lu);
if (IS_ERR(sdev)) {
sbp2_send_management_orb(lu, node_id, generation,
SBP2_LOGOUT_REQUEST, lu->login_id, NULL);
/*
* Set this back to sbp2_login so we fall back and
* retry login on bus reset.
*/
PREPARE_DELAYED_WORK(&lu->work, sbp2_login);
} else {
lu->sdev = sdev;
/*
* FIXME: We are unable to perform reconnects while in sbp2_login().
* Therefore __scsi_add_device() will get into trouble if a bus reset
* happens in parallel. It will either fail or leave us with an
* unusable sdev. As a workaround we check for this and retry the
* whole login and SCSI probing.
*/
/* Reported error during __scsi_add_device() */
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);
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:
sbp2_target_put(lu->tgt);
sbp2_target_put(tgt);
}
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->lun = lun_entry & 0xffff;
lu->retries = 0;
lu->blocked = false;
++tgt->dont_block;
INIT_LIST_HEAD(&lu->orb_list);
INIT_DELAYED_WORK(&lu->work, sbp2_login);
@ -813,7 +998,7 @@ static int sbp2_scan_unit_dir(struct sbp2_target *tgt, u32 *directory,
if (timeout > tgt->mgt_orb_timeout)
fw_notify("%s: config rom contains %ds "
"management ORB timeout, limiting "
"to %ds\n", tgt->unit->device.bus_id,
"to %ds\n", tgt->bus_id,
timeout / 1000,
tgt->mgt_orb_timeout / 1000);
break;
@ -836,12 +1021,12 @@ static void sbp2_init_workarounds(struct sbp2_target *tgt, u32 model,
u32 firmware_revision)
{
int i;
unsigned w = sbp2_param_workarounds;
unsigned int w = sbp2_param_workarounds;
if (w)
fw_notify("Please notify linux1394-devel@lists.sourceforge.net "
"if you need the workarounds parameter for %s\n",
tgt->unit->device.bus_id);
tgt->bus_id);
if (w & SBP2_WORKAROUND_OVERRIDE)
goto out;
@ -863,8 +1048,7 @@ static void sbp2_init_workarounds(struct sbp2_target *tgt, u32 model,
if (w)
fw_notify("Workarounds for %s: 0x%x "
"(firmware_revision 0x%06x, model_id 0x%06x)\n",
tgt->unit->device.bus_id,
w, firmware_revision, model);
tgt->bus_id, w, firmware_revision, model);
tgt->workarounds = w;
}
@ -888,6 +1072,7 @@ static int sbp2_probe(struct device *dev)
tgt->unit = unit;
kref_init(&tgt->kref);
INIT_LIST_HEAD(&tgt->lu_list);
tgt->bus_id = unit->device.bus_id;
if (fw_device_enable_phys_dma(device) < 0)
goto fail_shost_put;
@ -938,10 +1123,13 @@ static void sbp2_reconnect(struct work_struct *work)
{
struct sbp2_logical_unit *lu =
container_of(work, struct sbp2_logical_unit, work.work);
struct fw_unit *unit = lu->tgt->unit;
struct fw_device *device = fw_device(unit->device.parent);
struct sbp2_target *tgt = lu->tgt;
struct fw_device *device = fw_device(tgt->unit->device.parent);
int generation, node_id, local_node_id;
if (fw_device_is_shutdown(device))
goto out;
generation = device->generation;
smp_rmb(); /* node_id must not be older than generation */
node_id = device->node_id;
@ -950,10 +1138,17 @@ static void sbp2_reconnect(struct work_struct *work)
if (sbp2_send_management_orb(lu, node_id, generation,
SBP2_RECONNECT_REQUEST,
lu->login_id, NULL) < 0) {
if (lu->retries++ >= 5) {
fw_error("failed to reconnect to %s\n",
unit->device.bus_id);
/* Fall back and try to log in again. */
/*
* If reconnect was impossible even though we are in the
* current generation, 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;
PREPARE_DELAYED_WORK(&lu->work, sbp2_login);
}
@ -961,17 +1156,18 @@ static void sbp2_reconnect(struct work_struct *work)
goto out;
}
lu->generation = generation;
lu->tgt->node_id = node_id;
lu->tgt->address_high = local_node_id << 16;
tgt->node_id = node_id;
tgt->address_high = local_node_id << 16;
sbp2_set_generation(lu, generation);
fw_notify("reconnected to %s LUN %04x (%d retries)\n",
unit->device.bus_id, lu->lun, lu->retries);
fw_notify("%s: reconnected to LUN %04x (%d retries)\n",
tgt->bus_id, lu->lun, lu->retries);
sbp2_agent_reset(lu);
sbp2_cancel_orbs(lu);
sbp2_conditionally_unblock(lu);
out:
sbp2_target_put(lu->tgt);
sbp2_target_put(tgt);
}
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.
*/
list_for_each_entry(lu, &tgt->lu_list, link) {
sbp2_conditionally_block(lu);
lu->retries = 0;
sbp2_queue_work(lu, 0);
}
@ -1063,7 +1260,7 @@ complete_command_orb(struct sbp2_orb *base_orb, struct sbp2_status *status)
if (status != NULL) {
if (STATUS_GET_DEAD(*status))
sbp2_agent_reset(orb->lu);
sbp2_agent_reset_no_wait(orb->lu);
switch (STATUS_GET_RESPONSE(*status)) {
case SBP2_STATUS_REQUEST_COMPLETE:
@ -1089,6 +1286,7 @@ complete_command_orb(struct sbp2_orb *base_orb, struct sbp2_status *status)
* or when sending the write (less likely).
*/
result = DID_BUS_BUSY << 16;
sbp2_conditionally_block(orb->lu);
}
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)
struct sbp2_logical_unit *lu = cmd->device->hostdata;
struct fw_device *device = fw_device(lu->tgt->unit->device.parent);
struct sbp2_command_orb *orb;
unsigned max_payload;
unsigned int max_payload;
int retval = SCSI_MLQUEUE_HOST_BUSY;
/*
@ -1275,6 +1473,10 @@ static int sbp2_scsi_slave_alloc(struct scsi_device *sdev)
{
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;
/*
@ -1319,7 +1521,7 @@ static int sbp2_scsi_abort(struct scsi_cmnd *cmd)
{
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_cancel_orbs(lu);

View File

@ -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.
* 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
* Instead of adding to the built-in blacklist, use only the workarounds
* specified in the module load parameter.
@ -195,6 +198,7 @@ MODULE_PARM_DESC(workarounds, "Work around device bugs (default = 0"
", 36 byte inquiry = " __stringify(SBP2_WORKAROUND_INQUIRY_36)
", skip mode page 8 = " __stringify(SBP2_WORKAROUND_MODE_SENSE_8)
", fix capacity = " __stringify(SBP2_WORKAROUND_FIX_CAPACITY)
", delay inquiry = " __stringify(SBP2_WORKAROUND_DELAY_INQUIRY)
", override internal blacklist = " __stringify(SBP2_WORKAROUND_OVERRIDE)
", or a combination)");
@ -357,6 +361,11 @@ static const struct {
.workarounds = SBP2_WORKAROUND_INQUIRY_36 |
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 */ {
.firmware_revision = 0x000200,
.model_id = SBP2_ROM_VALUE_WILDCARD,
@ -914,6 +923,9 @@ static int sbp2_start_device(struct sbp2_lu *lu)
sbp2_agent_reset(lu, 1);
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);
if (error) {
SBP2_ERR("scsi_add_device failed");
@ -1962,6 +1974,9 @@ static int sbp2scsi_slave_alloc(struct scsi_device *sdev)
{
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;
sdev->allow_restart = 1;

View File

@ -343,6 +343,8 @@ enum sbp2lu_state_types {
#define SBP2_WORKAROUND_INQUIRY_36 0x2
#define SBP2_WORKAROUND_MODE_SENSE_8 0x4
#define SBP2_WORKAROUND_FIX_CAPACITY 0x8
#define SBP2_WORKAROUND_DELAY_INQUIRY 0x10
#define SBP2_INQUIRY_DELAY 12
#define SBP2_WORKAROUND_OVERRIDE 0x100
#endif /* SBP2_H */