/*
* Bottom half interrupt handlers
*/
-static void bh_handler(void* Context);
+static void bh_handler(struct work_struct *work);
static void bh_transmit(MGSLPC_INFO *info);
static void bh_status(MGSLPC_INFO *info);
memset(info, 0, sizeof(MGSLPC_INFO));
info->magic = MGSLPC_MAGIC;
- INIT_WORK(&info->task, bh_handler, info);
+ INIT_WORK(&info->task, bh_handler);
info->max_frame_size = 4096;
info->close_delay = 5*HZ/10;
info->closing_wait = 30*HZ;
if (debug_level >= DEBUG_LEVEL_INFO)
printk("mgslpc_config(0x%p)\n", link);
- /* read CONFIG tuple to find its configuration registers */
- tuple.DesiredTuple = CISTPL_CONFIG;
tuple.Attributes = 0;
tuple.TupleData = buf;
tuple.TupleDataMax = sizeof(buf);
tuple.TupleOffset = 0;
- CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
- CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
- CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
- link->conf.ConfigBase = parse.config.base;
- link->conf.Present = parse.config.rmask[0];
/* get CIS configuration entry */
return rc;
}
-static void bh_handler(void* Context)
+static void bh_handler(struct work_struct *work)
{
- MGSLPC_INFO *info = (MGSLPC_INFO*)Context;
+ MGSLPC_INFO *info = container_of(work, MGSLPC_INFO, task);
int action;
if (!info)
*/
typedef struct local_info_t {
+ struct net_device *dev;
struct pcmcia_device *p_dev;
dev_node_t node;
struct net_device_stats stats;
*/
static int do_start_xmit(struct sk_buff *skb, struct net_device *dev);
static void do_tx_timeout(struct net_device *dev);
-static void xirc2ps_tx_timeout_task(void *data);
+static void xirc2ps_tx_timeout_task(struct work_struct *work);
static struct net_device_stats *do_get_stats(struct net_device *dev);
static void set_addresses(struct net_device *dev);
static void set_multicast_list(struct net_device *dev);
if (!dev)
return -ENOMEM;
local = netdev_priv(dev);
+ local->dev = dev;
local->p_dev = link;
link->priv = dev;
#ifdef HAVE_TX_TIMEOUT
dev->tx_timeout = do_tx_timeout;
dev->watchdog_timeo = TX_TIMEOUT;
- INIT_WORK(&local->tx_timeout_task, xirc2ps_tx_timeout_task, dev);
+ INIT_WORK(&local->tx_timeout_task, xirc2ps_tx_timeout_task);
#endif
return xirc2ps_config(link);
* Returns: true if this is a CE2
*/
static int
- has_ce2_string(struct pcmcia_device * link)
+ has_ce2_string(struct pcmcia_device * p_dev)
{
- tuple_t tuple;
- cisparse_t parse;
- u_char buf[256];
-
- tuple.Attributes = 0;
- tuple.TupleData = buf;
- tuple.TupleDataMax = 254;
- tuple.TupleOffset = 0;
- tuple.DesiredTuple = CISTPL_VERS_1;
- if (!first_tuple(link, &tuple, &parse) && parse.version_1.ns > 2) {
- if (strstr(parse.version_1.str + parse.version_1.ofs[2], "CE2"))
- return 1;
- }
- return 0;
+ if (p_dev->prod_id[2] && strstr(p_dev->prod_id[2], "CE2"))
+ return 1;
+ return 0;
}
/****************
goto failure;
}
- /* get configuration stuff */
- tuple.DesiredTuple = CISTPL_CONFIG;
- if ((err=first_tuple(link, &tuple, &parse)))
- goto cis_error;
- link->conf.ConfigBase = parse.config.base;
- link->conf.Present = parse.config.rmask[0];
-
/* get the ethernet address from the CIS */
tuple.DesiredTuple = CISTPL_FUNCE;
for (err = first_tuple(link, &tuple, &parse); !err;
xirc2ps_release(link);
return -ENODEV;
- cis_error:
- printk(KNOT_XIRC "unable to parse CIS\n");
failure:
return -ENODEV;
} /* xirc2ps_config */
/*====================================================================*/
static void
-xirc2ps_tx_timeout_task(void *data)
+xirc2ps_tx_timeout_task(struct work_struct *work)
{
- struct net_device *dev = data;
+ local_info_t *local =
+ container_of(work, local_info_t, tx_timeout_task);
+ struct net_device *dev = local->dev;
/* reset the card */
do_reset(dev,1);
dev->trans_start = jiffies;
}
- #ifdef CONFIG_PCMCIA_LOAD_CIS
-
- /**
- * pcmcia_load_firmware - load CIS from userspace if device-provided is broken
- * @dev - the pcmcia device which needs a CIS override
- * @filename - requested filename in /lib/firmware/
- *
- * This uses the in-kernel firmware loading mechanism to use a "fake CIS" if
- * the one provided by the card is broken. The firmware files reside in
- * /lib/firmware/ in userspace.
- */
- static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename)
- {
- struct pcmcia_socket *s = dev->socket;
- const struct firmware *fw;
- char path[20];
- int ret=-ENOMEM;
- cisdump_t *cis;
-
- if (!filename)
- return -EINVAL;
-
- ds_dbg(1, "trying to load firmware %s\n", filename);
-
- if (strlen(filename) > 14)
- return -EINVAL;
-
- snprintf(path, 20, "%s", filename);
-
- if (request_firmware(&fw, path, &dev->dev) == 0) {
- if (fw->size >= CISTPL_MAX_CIS_SIZE)
- goto release;
-
- cis = kzalloc(sizeof(cisdump_t), GFP_KERNEL);
- if (!cis)
- goto release;
-
- cis->Length = fw->size + 1;
- memcpy(cis->Data, fw->data, fw->size);
-
- if (!pcmcia_replace_cis(s, cis))
- ret = 0;
- }
- release:
- release_firmware(fw);
-
- return (ret);
- }
-
- #else /* !CONFIG_PCMCIA_LOAD_CIS */
-
- static inline int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename)
- {
- return -ENODEV;
- }
-
- #endif
-
-
/*======================================================================*/
driver->drv.bus = &pcmcia_bus_type;
driver->drv.owner = driver->owner;
+ ds_dbg(3, "registering driver %s\n", driver->drv.name);
+
return driver_register(&driver->drv);
}
EXPORT_SYMBOL(pcmcia_register_driver);
*/
void pcmcia_unregister_driver(struct pcmcia_driver *driver)
{
+ ds_dbg(3, "unregistering driver %s\n", driver->drv.name);
driver_unregister(&driver->drv);
}
EXPORT_SYMBOL(pcmcia_unregister_driver);
static void pcmcia_release_function(struct kref *ref)
{
struct config_t *c = container_of(ref, struct config_t, ref);
+ ds_dbg(1, "releasing config_t\n");
kfree(c);
}
static void pcmcia_release_dev(struct device *dev)
{
struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
- ds_dbg(1, "releasing dev %p\n", p_dev);
+ ds_dbg(1, "releasing device %s\n", p_dev->dev.bus_id);
pcmcia_put_socket(p_dev->socket);
kfree(p_dev->devname);
kref_put(&p_dev->function_config->ref, pcmcia_release_function);
kfree(p_dev);
}
- static void pcmcia_add_pseudo_device(struct pcmcia_socket *s)
+ static void pcmcia_add_device_later(struct pcmcia_socket *s, int mfc)
{
if (!s->pcmcia_state.device_add_pending) {
+ ds_dbg(1, "scheduling to add %s secondary"
+ " device to %d\n", mfc ? "mfc" : "pfc", s->sock);
s->pcmcia_state.device_add_pending = 1;
+ s->pcmcia_state.mfc_pfc = mfc;
schedule_work(&s->device_add);
}
return;
struct pcmcia_driver *p_drv;
struct pcmcia_device_id *did;
struct pcmcia_socket *s;
+ cistpl_config_t cis_config;
int ret = 0;
dev = get_device(dev);
p_drv = to_pcmcia_drv(dev->driver);
s = p_dev->socket;
+ ds_dbg(1, "trying to bind %s to %s\n", p_dev->dev.bus_id,
+ p_drv->drv.name);
+
if ((!p_drv->probe) || (!p_dev->function_config) ||
(!try_module_get(p_drv->owner))) {
ret = -EINVAL;
goto put_dev;
}
+ /* set up some more device information */
+ ret = pccard_read_tuple(p_dev->socket, p_dev->func, CISTPL_CONFIG,
+ &cis_config);
+ if (!ret) {
+ p_dev->conf.ConfigBase = cis_config.base;
+ p_dev->conf.Present = cis_config.rmask[0];
+ } else {
+ printk(KERN_INFO "pcmcia: could not parse base and rmask0 of CIS\n");
+ p_dev->conf.ConfigBase = 0;
+ p_dev->conf.Present = 0;
+ }
+
ret = p_drv->probe(p_dev);
- if (ret)
+ if (ret) {
+ ds_dbg(1, "binding %s to %s failed with %d\n",
+ p_dev->dev.bus_id, p_drv->drv.name, ret);
goto put_module;
+ }
/* handle pseudo multifunction devices:
* there are at most two pseudo multifunction devices.
did = p_dev->dev.driver_data;
if (did && (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) &&
(p_dev->socket->device_count == 1) && (p_dev->device_no == 0))
- pcmcia_add_pseudo_device(p_dev->socket);
+ pcmcia_add_device_later(p_dev->socket, 0);
put_module:
if (ret)
struct pcmcia_device *tmp;
unsigned long flags;
- ds_dbg(2, "unbind_request(%d)\n", s->sock);
-
+ ds_dbg(2, "pcmcia_card_remove(%d) %s\n", s->sock,
+ leftover ? leftover->devname : "");
if (!leftover)
s->device_count = 0;
p_dev->_removed=1;
spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+ ds_dbg(2, "unregistering device %s\n", p_dev->dev.bus_id);
device_unregister(&p_dev->dev);
}
p_dev = to_pcmcia_dev(dev);
p_drv = to_pcmcia_drv(dev->driver);
+ ds_dbg(1, "removing device %s\n", p_dev->dev.bus_id);
+
/* If we're removing the primary module driving a
* pseudo multi-function card, we need to unbind
* all devices
mutex_lock(&device_add_lock);
- /* max of 2 devices per card */
- if (s->device_count == 2)
+ ds_dbg(3, "adding device to %d, function %d\n", s->sock, function);
+
+ /* max of 4 devices per card */
+ if (s->device_count == 4)
goto err_put;
p_dev = kzalloc(sizeof(struct pcmcia_device), GFP_KERNEL);
p_dev->socket = s;
p_dev->device_no = (s->device_count++);
p_dev->func = function;
- if (s->functions <= function)
- s->functions = function + 1;
p_dev->dev.bus = &pcmcia_bus_type;
p_dev->dev.parent = s->dev.dev;
if (!p_dev->devname)
goto err_free;
sprintf (p_dev->devname, "pcmcia%s", p_dev->dev.bus_id);
+ ds_dbg(3, "devname is %s\n", p_dev->devname);
- /* compat */
spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
/*
spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
if (!p_dev->function_config) {
+ ds_dbg(3, "creating config_t for %s\n", p_dev->dev.bus_id);
p_dev->function_config = kzalloc(sizeof(struct config_t),
GFP_KERNEL);
if (!p_dev->function_config)
unsigned int no_funcs, i;
int ret = 0;
- if (!(s->resource_setup_done))
+ if (!(s->resource_setup_done)) {
+ ds_dbg(3, "no resources available, delaying card_add\n");
return -EAGAIN; /* try again, but later... */
+ }
- if (pcmcia_validate_mem(s))
+ if (pcmcia_validate_mem(s)) {
+ ds_dbg(3, "validating mem resources failed, "
+ "delaying card_add\n");
return -EAGAIN; /* try again, but later... */
+ }
ret = pccard_validate_cis(s, BIND_FN_ALL, &cisinfo);
if (ret || !cisinfo.Chains) {
no_funcs = mfc.nfn;
else
no_funcs = 1;
+ s->functions = no_funcs;
for (i=0; i < no_funcs; i++)
pcmcia_device_add(s, i);
}
- static void pcmcia_delayed_add_pseudo_device(struct work_struct *work)
-static void pcmcia_delayed_add_device(void *data)
++static void pcmcia_delayed_add_device(struct work_struct *work)
{
- struct pcmcia_socket *s = data;
+ struct pcmcia_socket *s =
+ container_of(work, struct pcmcia_socket, device_add);
- pcmcia_device_add(s, 0);
+ ds_dbg(1, "adding additional device to %d\n", s->sock);
+ pcmcia_device_add(s, s->pcmcia_state.mfc_pfc);
s->pcmcia_state.device_add_pending = 0;
+ s->pcmcia_state.mfc_pfc = 0;
}
static int pcmcia_requery(struct device *dev, void * _data)
{
struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
- if (!p_dev->dev.driver)
+ if (!p_dev->dev.driver) {
+ ds_dbg(1, "update device information for %s\n",
+ p_dev->dev.bus_id);
pcmcia_device_query(p_dev);
+ }
return 0;
}
- static void pcmcia_bus_rescan(struct pcmcia_socket *skt)
+ static void pcmcia_bus_rescan(struct pcmcia_socket *skt, int new_cis)
{
- int no_devices=0;
+ int no_devices = 0;
int ret = 0;
unsigned long flags;
/* must be called with skt_mutex held */
+ ds_dbg(0, "re-scanning socket %d\n", skt->sock);
+
spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
if (list_empty(&skt->devices_list))
- no_devices=1;
+ no_devices = 1;
spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+ /* If this is because of a CIS override, start over */
+ if (new_cis && !no_devices)
+ pcmcia_card_remove(skt, NULL);
+
/* if no devices were added for this socket yet because of
* missing resource information or other trouble, we need to
* do this now. */
- if (no_devices) {
+ if (no_devices || new_cis) {
ret = pcmcia_card_add(skt);
if (ret)
return;
printk(KERN_INFO "pcmcia: bus_rescan_devices failed\n");
}
+ #ifdef CONFIG_PCMCIA_LOAD_CIS
+
+ /**
+ * pcmcia_load_firmware - load CIS from userspace if device-provided is broken
+ * @dev - the pcmcia device which needs a CIS override
+ * @filename - requested filename in /lib/firmware/
+ *
+ * This uses the in-kernel firmware loading mechanism to use a "fake CIS" if
+ * the one provided by the card is broken. The firmware files reside in
+ * /lib/firmware/ in userspace.
+ */
+ static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename)
+ {
+ struct pcmcia_socket *s = dev->socket;
+ const struct firmware *fw;
+ char path[20];
+ int ret = -ENOMEM;
+ int no_funcs;
+ int old_funcs;
+ cisdump_t *cis;
+ cistpl_longlink_mfc_t mfc;
+
+ if (!filename)
+ return -EINVAL;
+
+ ds_dbg(1, "trying to load CIS file %s\n", filename);
+
+ if (strlen(filename) > 14) {
+ printk(KERN_WARNING "pcmcia: CIS filename is too long\n");
+ return -EINVAL;
+ }
+
+ snprintf(path, 20, "%s", filename);
+
+ if (request_firmware(&fw, path, &dev->dev) == 0) {
+ if (fw->size >= CISTPL_MAX_CIS_SIZE) {
+ ret = -EINVAL;
+ printk(KERN_ERR "pcmcia: CIS override is too big\n");
+ goto release;
+ }
+
+ cis = kzalloc(sizeof(cisdump_t), GFP_KERNEL);
+ if (!cis) {
+ ret = -ENOMEM;
+ goto release;
+ }
+
+ cis->Length = fw->size + 1;
+ memcpy(cis->Data, fw->data, fw->size);
+
+ if (!pcmcia_replace_cis(s, cis))
+ ret = 0;
+ else {
+ printk(KERN_ERR "pcmcia: CIS override failed\n");
+ goto release;
+ }
+
+
+ /* update information */
+ pcmcia_device_query(dev);
+
+ /* does this cis override add or remove functions? */
+ old_funcs = s->functions;
+
+ if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC, &mfc))
+ no_funcs = mfc.nfn;
+ else
+ no_funcs = 1;
+ s->functions = no_funcs;
+
+ if (old_funcs > no_funcs)
+ pcmcia_card_remove(s, dev);
+ else if (no_funcs > old_funcs)
+ pcmcia_add_device_later(s, 1);
+ }
+ release:
+ release_firmware(fw);
+
+ return (ret);
+ }
+
+ #else /* !CONFIG_PCMCIA_LOAD_CIS */
+
+ static inline int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename)
+ {
+ return -ENODEV;
+ }
+
+ #endif
+
+
static inline int pcmcia_devmatch(struct pcmcia_device *dev,
struct pcmcia_device_id *did)
{
* after it has re-checked that there is no possible module
* with a prod_id/manf_id/card_id match.
*/
+ ds_dbg(0, "skipping FUNC_ID match for %s until userspace "
+ "interaction\n", dev->dev.bus_id);
if (!dev->allow_func_id_match)
return 0;
}
if (did->match_flags & PCMCIA_DEV_ID_MATCH_FAKE_CIS) {
+ ds_dbg(0, "device %s needs a fake CIS\n", dev->dev.bus_id);
if (!dev->socket->fake_cis)
pcmcia_load_firmware(dev, did->cisfile);
#ifdef CONFIG_PCMCIA_IOCTL
/* matching by cardmgr */
- if (p_dev->cardmgr == p_drv)
+ if (p_dev->cardmgr == p_drv) {
+ ds_dbg(0, "cardmgr matched %s to %s\n", dev->bus_id,
+ drv->name);
return 1;
+ }
#endif
while (did && did->match_flags) {
- if (pcmcia_devmatch(p_dev, did))
+ ds_dbg(3, "trying to match %s to %s\n", dev->bus_id,
+ drv->name);
+ if (pcmcia_devmatch(p_dev, did)) {
+ ds_dbg(0, "matched %s to %s\n", dev->bus_id,
+ drv->name);
return 1;
+ }
did++;
}
struct pcmcia_driver *p_drv = NULL;
int ret = 0;
+ ds_dbg(2, "suspending %s\n", dev->bus_id);
+
if (dev->driver)
p_drv = to_pcmcia_drv(dev->driver);
if (p_drv->suspend) {
ret = p_drv->suspend(p_dev);
- if (ret)
+ if (ret) {
+ printk(KERN_ERR "pcmcia: device %s (driver %s) did "
+ "not want to go to sleep (%d)\n",
+ p_dev->devname, p_drv->drv.name, ret);
goto out;
+ }
}
- if (p_dev->device_no == p_dev->func)
+ if (p_dev->device_no == p_dev->func) {
+ ds_dbg(2, "releasing configuration for %s\n", dev->bus_id);
pcmcia_release_configuration(p_dev);
+ }
out:
if (!ret)
struct pcmcia_driver *p_drv = NULL;
int ret = 0;
+ ds_dbg(2, "resuming %s\n", dev->bus_id);
+
if (dev->driver)
p_drv = to_pcmcia_drv(dev->driver);
goto out;
if (p_dev->device_no == p_dev->func) {
+ ds_dbg(2, "requesting configuration for %s\n", dev->bus_id);
ret = pcmcia_request_configuration(p_dev, &p_dev->conf);
if (ret)
goto out;
static int pcmcia_bus_resume(struct pcmcia_socket *skt)
{
+ ds_dbg(2, "resuming socket %d\n", skt->sock);
bus_for_each_dev(&pcmcia_bus_type, NULL, skt, pcmcia_bus_resume_callback);
return 0;
}
static int pcmcia_bus_suspend(struct pcmcia_socket *skt)
{
+ ds_dbg(2, "suspending socket %d\n", skt->sock);
if (bus_for_each_dev(&pcmcia_bus_type, NULL, skt,
pcmcia_bus_suspend_callback)) {
pcmcia_bus_resume(skt);
init_waitqueue_head(&socket->queue);
#endif
INIT_LIST_HEAD(&socket->devices_list);
- INIT_WORK(&socket->device_add, pcmcia_delayed_add_pseudo_device);
- INIT_WORK(&socket->device_add, pcmcia_delayed_add_device, socket);
++ INIT_WORK(&socket->device_add, pcmcia_delayed_add_device);
memset(&socket->pcmcia_state, 0, sizeof(u8));
socket->device_count = 0;
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_cmnd.h>
- #include <scsi/scsi_transport.h>
#include "ipr.h"
/*
/* This table describes the differences between DMA controller chips */
static const struct ipr_chip_cfg_t ipr_chip_cfg[] = {
- { /* Gemstone, Citrine, and Obsidian */
+ { /* Gemstone, Citrine, Obsidian, and Obsidian-E */
.mailbox = 0x0042C,
.cache_line_size = 0x20,
{
{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CITRINE, &ipr_chip_cfg[0] },
{ PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_OBSIDIAN, &ipr_chip_cfg[0] },
{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN, &ipr_chip_cfg[0] },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN_E, &ipr_chip_cfg[0] },
{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_SNIPE, &ipr_chip_cfg[1] },
{ PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_SCAMP, &ipr_chip_cfg[1] }
};
/**
* ipr_log_hex_data - Log additional hex IOA error data.
+ * @ioa_cfg: ioa config struct
* @data: IOA error data
* @len: data length
*
* Return value:
* none
**/
- static void ipr_log_hex_data(u32 *data, int len)
+ static void ipr_log_hex_data(struct ipr_ioa_cfg *ioa_cfg, u32 *data, int len)
{
int i;
if (len == 0)
return;
+ if (ioa_cfg->log_level <= IPR_DEFAULT_LOG_LEVEL)
+ len = min_t(int, len, IPR_DEFAULT_MAX_ERROR_DUMP);
+
for (i = 0; i < len / 4; i += 4) {
ipr_err("%08X: %08X %08X %08X %08X\n", i*4,
be32_to_cpu(data[i]),
ipr_err("%s\n", error->failure_reason);
ipr_err("Remote Adapter VPD:\n");
ipr_log_ext_vpd(&error->vpd);
- ipr_log_hex_data(error->data,
+ ipr_log_hex_data(ioa_cfg, error->data,
be32_to_cpu(hostrcb->hcam.length) -
(offsetof(struct ipr_hostrcb_error, u) +
offsetof(struct ipr_hostrcb_type_17_error, data)));
ipr_err("%s\n", error->failure_reason);
ipr_err("Remote Adapter VPD:\n");
ipr_log_vpd(&error->vpd);
- ipr_log_hex_data(error->data,
+ ipr_log_hex_data(ioa_cfg, error->data,
be32_to_cpu(hostrcb->hcam.length) -
(offsetof(struct ipr_hostrcb_error, u) +
offsetof(struct ipr_hostrcb_type_07_error, data)));
}
+ static const struct {
+ u8 active;
+ char *desc;
+ } path_active_desc[] = {
+ { IPR_PATH_NO_INFO, "Path" },
+ { IPR_PATH_ACTIVE, "Active path" },
+ { IPR_PATH_NOT_ACTIVE, "Inactive path" }
+ };
+
+ static const struct {
+ u8 state;
+ char *desc;
+ } path_state_desc[] = {
+ { IPR_PATH_STATE_NO_INFO, "has no path state information available" },
+ { IPR_PATH_HEALTHY, "is healthy" },
+ { IPR_PATH_DEGRADED, "is degraded" },
+ { IPR_PATH_FAILED, "is failed" }
+ };
+
+ /**
+ * ipr_log_fabric_path - Log a fabric path error
+ * @hostrcb: hostrcb struct
+ * @fabric: fabric descriptor
+ *
+ * Return value:
+ * none
+ **/
+ static void ipr_log_fabric_path(struct ipr_hostrcb *hostrcb,
+ struct ipr_hostrcb_fabric_desc *fabric)
+ {
+ int i, j;
+ u8 path_state = fabric->path_state;
+ u8 active = path_state & IPR_PATH_ACTIVE_MASK;
+ u8 state = path_state & IPR_PATH_STATE_MASK;
+
+ for (i = 0; i < ARRAY_SIZE(path_active_desc); i++) {
+ if (path_active_desc[i].active != active)
+ continue;
+
+ for (j = 0; j < ARRAY_SIZE(path_state_desc); j++) {
+ if (path_state_desc[j].state != state)
+ continue;
+
+ if (fabric->cascaded_expander == 0xff && fabric->phy == 0xff) {
+ ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d\n",
+ path_active_desc[i].desc, path_state_desc[j].desc,
+ fabric->ioa_port);
+ } else if (fabric->cascaded_expander == 0xff) {
+ ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d, Phy=%d\n",
+ path_active_desc[i].desc, path_state_desc[j].desc,
+ fabric->ioa_port, fabric->phy);
+ } else if (fabric->phy == 0xff) {
+ ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d, Cascade=%d\n",
+ path_active_desc[i].desc, path_state_desc[j].desc,
+ fabric->ioa_port, fabric->cascaded_expander);
+ } else {
+ ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d, Cascade=%d, Phy=%d\n",
+ path_active_desc[i].desc, path_state_desc[j].desc,
+ fabric->ioa_port, fabric->cascaded_expander, fabric->phy);
+ }
+ return;
+ }
+ }
+
+ ipr_err("Path state=%02X IOA Port=%d Cascade=%d Phy=%d\n", path_state,
+ fabric->ioa_port, fabric->cascaded_expander, fabric->phy);
+ }
+
+ static const struct {
+ u8 type;
+ char *desc;
+ } path_type_desc[] = {
+ { IPR_PATH_CFG_IOA_PORT, "IOA port" },
+ { IPR_PATH_CFG_EXP_PORT, "Expander port" },
+ { IPR_PATH_CFG_DEVICE_PORT, "Device port" },
+ { IPR_PATH_CFG_DEVICE_LUN, "Device LUN" }
+ };
+
+ static const struct {
+ u8 status;
+ char *desc;
+ } path_status_desc[] = {
+ { IPR_PATH_CFG_NO_PROB, "Functional" },
+ { IPR_PATH_CFG_DEGRADED, "Degraded" },
+ { IPR_PATH_CFG_FAILED, "Failed" },
+ { IPR_PATH_CFG_SUSPECT, "Suspect" },
+ { IPR_PATH_NOT_DETECTED, "Missing" },
+ { IPR_PATH_INCORRECT_CONN, "Incorrectly connected" }
+ };
+
+ static const char *link_rate[] = {
+ "unknown",
+ "disabled",
+ "phy reset problem",
+ "spinup hold",
+ "port selector",
+ "unknown",
+ "unknown",
+ "unknown",
+ "1.5Gbps",
+ "3.0Gbps",
+ "unknown",
+ "unknown",
+ "unknown",
+ "unknown",
+ "unknown",
+ "unknown"
+ };
+
+ /**
+ * ipr_log_path_elem - Log a fabric path element.
+ * @hostrcb: hostrcb struct
+ * @cfg: fabric path element struct
+ *
+ * Return value:
+ * none
+ **/
+ static void ipr_log_path_elem(struct ipr_hostrcb *hostrcb,
+ struct ipr_hostrcb_config_element *cfg)
+ {
+ int i, j;
+ u8 type = cfg->type_status & IPR_PATH_CFG_TYPE_MASK;
+ u8 status = cfg->type_status & IPR_PATH_CFG_STATUS_MASK;
+
+ if (type == IPR_PATH_CFG_NOT_EXIST)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(path_type_desc); i++) {
+ if (path_type_desc[i].type != type)
+ continue;
+
+ for (j = 0; j < ARRAY_SIZE(path_status_desc); j++) {
+ if (path_status_desc[j].status != status)
+ continue;
+
+ if (type == IPR_PATH_CFG_IOA_PORT) {
+ ipr_hcam_err(hostrcb, "%s %s: Phy=%d, Link rate=%s, WWN=%08X%08X\n",
+ path_status_desc[j].desc, path_type_desc[i].desc,
+ cfg->phy, link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
+ be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
+ } else {
+ if (cfg->cascaded_expander == 0xff && cfg->phy == 0xff) {
+ ipr_hcam_err(hostrcb, "%s %s: Link rate=%s, WWN=%08X%08X\n",
+ path_status_desc[j].desc, path_type_desc[i].desc,
+ link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
+ be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
+ } else if (cfg->cascaded_expander == 0xff) {
+ ipr_hcam_err(hostrcb, "%s %s: Phy=%d, Link rate=%s, "
+ "WWN=%08X%08X\n", path_status_desc[j].desc,
+ path_type_desc[i].desc, cfg->phy,
+ link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
+ be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
+ } else if (cfg->phy == 0xff) {
+ ipr_hcam_err(hostrcb, "%s %s: Cascade=%d, Link rate=%s, "
+ "WWN=%08X%08X\n", path_status_desc[j].desc,
+ path_type_desc[i].desc, cfg->cascaded_expander,
+ link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
+ be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
+ } else {
+ ipr_hcam_err(hostrcb, "%s %s: Cascade=%d, Phy=%d, Link rate=%s "
+ "WWN=%08X%08X\n", path_status_desc[j].desc,
+ path_type_desc[i].desc, cfg->cascaded_expander, cfg->phy,
+ link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
+ be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
+ }
+ }
+ return;
+ }
+ }
+
+ ipr_hcam_err(hostrcb, "Path element=%02X: Cascade=%d Phy=%d Link rate=%s "
+ "WWN=%08X%08X\n", cfg->type_status, cfg->cascaded_expander, cfg->phy,
+ link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
+ be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
+ }
+
+ /**
+ * ipr_log_fabric_error - Log a fabric error.
+ * @ioa_cfg: ioa config struct
+ * @hostrcb: hostrcb struct
+ *
+ * Return value:
+ * none
+ **/
+ static void ipr_log_fabric_error(struct ipr_ioa_cfg *ioa_cfg,
+ struct ipr_hostrcb *hostrcb)
+ {
+ struct ipr_hostrcb_type_20_error *error;
+ struct ipr_hostrcb_fabric_desc *fabric;
+ struct ipr_hostrcb_config_element *cfg;
+ int i, add_len;
+
+ error = &hostrcb->hcam.u.error.u.type_20_error;
+ error->failure_reason[sizeof(error->failure_reason) - 1] = '\0';
+ ipr_hcam_err(hostrcb, "%s\n", error->failure_reason);
+
+ add_len = be32_to_cpu(hostrcb->hcam.length) -
+ (offsetof(struct ipr_hostrcb_error, u) +
+ offsetof(struct ipr_hostrcb_type_20_error, desc));
+
+ for (i = 0, fabric = error->desc; i < error->num_entries; i++) {
+ ipr_log_fabric_path(hostrcb, fabric);
+ for_each_fabric_cfg(fabric, cfg)
+ ipr_log_path_elem(hostrcb, cfg);
+
+ add_len -= be16_to_cpu(fabric->length);
+ fabric = (struct ipr_hostrcb_fabric_desc *)
+ ((unsigned long)fabric + be16_to_cpu(fabric->length));
+ }
+
+ ipr_log_hex_data(ioa_cfg, (u32 *)fabric, add_len);
+ }
+
/**
* ipr_log_generic_error - Log an adapter error.
* @ioa_cfg: ioa config struct
static void ipr_log_generic_error(struct ipr_ioa_cfg *ioa_cfg,
struct ipr_hostrcb *hostrcb)
{
- ipr_log_hex_data(hostrcb->hcam.u.raw.data,
+ ipr_log_hex_data(ioa_cfg, hostrcb->hcam.u.raw.data,
be32_to_cpu(hostrcb->hcam.length));
}
if (!ipr_error_table[error_index].log_hcam)
return;
- if (ipr_is_device(&hostrcb->hcam.u.error.failing_dev_res_addr)) {
- ipr_ra_err(ioa_cfg, hostrcb->hcam.u.error.failing_dev_res_addr,
- "%s\n", ipr_error_table[error_index].error);
- } else {
- dev_err(&ioa_cfg->pdev->dev, "%s\n",
- ipr_error_table[error_index].error);
- }
+ ipr_hcam_err(hostrcb, "%s\n", ipr_error_table[error_index].error);
/* Set indication we have logged an error */
ioa_cfg->errors_logged++;
case IPR_HOST_RCB_OVERLAY_ID_17:
ipr_log_enhanced_dual_ioa_error(ioa_cfg, hostrcb);
break;
+ case IPR_HOST_RCB_OVERLAY_ID_20:
+ ipr_log_fabric_error(ioa_cfg, hostrcb);
+ break;
case IPR_HOST_RCB_OVERLAY_ID_1:
case IPR_HOST_RCB_OVERLAY_ID_DEFAULT:
default:
/**
* ipr_worker_thread - Worker thread
- * @data: ioa config struct
+ * @work: ioa config struct
*
* Called at task level from a work thread. This function takes care
* of adding and removing device from the mid-layer as configuration
* Return value:
* nothing
**/
-static void ipr_worker_thread(void *data)
+static void ipr_worker_thread(struct work_struct *work)
{
unsigned long lock_flags;
struct ipr_resource_entry *res;
struct scsi_device *sdev;
struct ipr_dump *dump;
- struct ipr_ioa_cfg *ioa_cfg = data;
+ struct ipr_ioa_cfg *ioa_cfg =
+ container_of(work, struct ipr_ioa_cfg, work_q);
u8 bus, target, lun;
int did_work;
struct ipr_dump *dump;
unsigned long lock_flags = 0;
- ENTER;
dump = kzalloc(sizeof(struct ipr_dump), GFP_KERNEL);
if (!dump) {
}
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
- LEAVE;
return 0;
}
ENTER;
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ while(ioa_cfg->in_reset_reload) {
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ }
+
res = sata_port->res;
if (res) {
rc = ipr_device_reset(ioa_cfg, res);
if (ipr_cmd->ioarcb.res_handle == res->cfgte.res_handle) {
if (ipr_cmd->scsi_cmd)
ipr_cmd->done = ipr_scsi_eh_done;
+ if (ipr_cmd->qc && !(ipr_cmd->qc->flags & ATA_QCFLAG_FAILED)) {
+ ipr_cmd->qc->err_mask |= AC_ERR_TIMEOUT;
+ ipr_cmd->qc->flags |= ATA_QCFLAG_FAILED;
+ }
}
}
*/
if (ioa_cfg->in_reset_reload || ioa_cfg->ioa_is_dead)
return FAILED;
- if (!res || (!ipr_is_gscsi(res) && !ipr_is_vset_device(res)))
+ if (!res || !ipr_is_gscsi(res))
return FAILED;
list_for_each_entry(ipr_cmd, &ioa_cfg->pending_q, queue) {
* Return value:
* 0 on success / other on failure
**/
- int ipr_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
+ static int ipr_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
{
struct ipr_resource_entry *res;
return buffer;
}
- /**
- * ipr_scsi_timed_out - Handle scsi command timeout
- * @scsi_cmd: scsi command struct
- *
- * Return value:
- * EH_NOT_HANDLED
- **/
- enum scsi_eh_timer_return ipr_scsi_timed_out(struct scsi_cmnd *scsi_cmd)
- {
- struct ipr_ioa_cfg *ioa_cfg;
- struct ipr_cmnd *ipr_cmd;
- unsigned long flags;
-
- ENTER;
- spin_lock_irqsave(scsi_cmd->device->host->host_lock, flags);
- ioa_cfg = (struct ipr_ioa_cfg *)scsi_cmd->device->host->hostdata;
-
- list_for_each_entry(ipr_cmd, &ioa_cfg->pending_q, queue) {
- if (ipr_cmd->qc && ipr_cmd->qc->scsicmd == scsi_cmd) {
- ipr_cmd->qc->err_mask |= AC_ERR_TIMEOUT;
- ipr_cmd->qc->flags |= ATA_QCFLAG_FAILED;
- break;
- }
- }
-
- spin_unlock_irqrestore(scsi_cmd->device->host->host_lock, flags);
- LEAVE;
- return EH_NOT_HANDLED;
- }
-
- static struct scsi_transport_template ipr_transport_template = {
- .eh_timed_out = ipr_scsi_timed_out
- };
-
static struct scsi_host_template driver_template = {
.module = THIS_MODULE,
.name = "IPR",
unsigned long flags;
spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
+ while(ioa_cfg->in_reset_reload) {
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
+ wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
+ spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
+ }
+
list_for_each_entry(ipr_cmd, &ioa_cfg->pending_q, queue) {
if (ipr_cmd->qc == qc) {
ipr_device_reset(ioa_cfg, sata_port->res);
ioa_cfg->hostrcb[i]->hostrcb_dma =
ioa_cfg->hostrcb_dma[i] + offsetof(struct ipr_hostrcb, hcam);
+ ioa_cfg->hostrcb[i]->ioa_cfg = ioa_cfg;
list_add_tail(&ioa_cfg->hostrcb[i]->queue, &ioa_cfg->hostrcb_free_q);
}
INIT_LIST_HEAD(&ioa_cfg->hostrcb_pending_q);
INIT_LIST_HEAD(&ioa_cfg->free_res_q);
INIT_LIST_HEAD(&ioa_cfg->used_res_q);
- INIT_WORK(&ioa_cfg->work_q, ipr_worker_thread, ioa_cfg);
+ INIT_WORK(&ioa_cfg->work_q, ipr_worker_thread);
init_waitqueue_head(&ioa_cfg->reset_wait_q);
ioa_cfg->sdt_state = INACTIVE;
if (ipr_enable_cache)
ioa_cfg = (struct ipr_ioa_cfg *)host->hostdata;
memset(ioa_cfg, 0, sizeof(struct ipr_ioa_cfg));
- host->transportt = &ipr_transport_template;
ata_host_init(&ioa_cfg->ata_host, &pdev->dev,
sata_port_info.flags, &ipr_sata_ops);
{ PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_OBSIDIAN,
PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_572B,
0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] },
+ { PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_OBSIDIAN,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_575C,
+ 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] },
{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN,
PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_572A,
0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] },
{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN,
PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_572B,
0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_575C,
+ 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57B8,
+ 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN_E,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57B7,
+ 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] },
{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_SNIPE,
PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_2780,
0, 0, (kernel_ulong_t)&ipr_chip_cfg[1] },
{ PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_SCAMP,
PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_571F,
0, 0, (kernel_ulong_t)&ipr_chip_cfg[1] },
+ { PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_SCAMP,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_572F,
+ 0, 0, (kernel_ulong_t)&ipr_chip_cfg[1] },
{ }
};
MODULE_DEVICE_TABLE(pci, ipr_pci_table);
/* ---------- HA events ---------- */
-void sas_hae_reset(void *data)
+void sas_hae_reset(struct work_struct *work)
{
- struct sas_ha_struct *ha = data;
+ struct sas_ha_event *ev =
+ container_of(work, struct sas_ha_event, work);
+ struct sas_ha_struct *ha = ev->ha;
sas_begin_event(HAE_RESET, &ha->event_lock,
&ha->pending);
}
}
+ INIT_LIST_HEAD(&sas_ha->eh_done_q);
+
return 0;
Undo_ports:
return sas_smp_get_phy_events(phy);
}
- static int sas_phy_reset(struct sas_phy *phy, int hard_reset)
+ int sas_phy_reset(struct sas_phy *phy, int hard_reset)
{
int ret;
enum phy_func reset_type;
}
/**
- * qla4010_soft_reset - performs soft reset.
+ * qla4xxx_soft_reset - performs soft reset.
* @ha: Pointer to host adapter structure.
**/
- static int qla4010_soft_reset(struct scsi_qla_host *ha)
+ int qla4xxx_soft_reset(struct scsi_qla_host *ha)
{
uint32_t max_wait_time;
unsigned long flags = 0;
return status;
}
- /**
- * qla4xxx_topcat_reset - performs hard reset of TopCat Chip.
- * @ha: Pointer to host adapter structure.
- **/
- static int qla4xxx_topcat_reset(struct scsi_qla_host *ha)
- {
- unsigned long flags;
-
- ql4xxx_lock_nvram(ha);
- spin_lock_irqsave(&ha->hardware_lock, flags);
- writel(set_rmask(GPOR_TOPCAT_RESET), isp_gp_out(ha));
- readl(isp_gp_out(ha));
- mdelay(1);
-
- writel(clr_rmask(GPOR_TOPCAT_RESET), isp_gp_out(ha));
- readl(isp_gp_out(ha));
- spin_unlock_irqrestore(&ha->hardware_lock, flags);
- mdelay(2523);
-
- ql4xxx_unlock_nvram(ha);
- return QLA_SUCCESS;
- }
-
/**
* qla4xxx_flush_active_srbs - returns all outstanding i/o requests to O.S.
* @ha: Pointer to host adapter structure.
}
- /**
- * qla4xxx_hard_reset - performs HBA Hard Reset
- * @ha: Pointer to host adapter structure.
- **/
- static int qla4xxx_hard_reset(struct scsi_qla_host *ha)
- {
- /* The QLA4010 really doesn't have an equivalent to a hard reset */
- qla4xxx_flush_active_srbs(ha);
- if (test_bit(AF_TOPCAT_CHIP_PRESENT, &ha->flags)) {
- int status = QLA_ERROR;
-
- if ((qla4010_soft_reset(ha) == QLA_SUCCESS) &&
- (qla4xxx_topcat_reset(ha) == QLA_SUCCESS) &&
- (qla4010_soft_reset(ha) == QLA_SUCCESS))
- status = QLA_SUCCESS;
- return status;
- } else
- return qla4010_soft_reset(ha);
- }
-
/**
* qla4xxx_recover_adapter - recovers adapter after a fatal error
* @ha: Pointer to host adapter structure.
if (status == QLA_SUCCESS) {
DEBUG2(printk("scsi%ld: %s - Performing soft reset..\n",
ha->host_no, __func__));
- status = qla4xxx_soft_reset(ha);
- }
- /* FIXMEkaren: Do we want to keep interrupts enabled and process
- AENs after soft reset */
-
- /* If firmware (SOFT) reset failed, or if all outstanding
- * commands have not returned, then do a HARD reset.
- */
- if (status == QLA_ERROR) {
- DEBUG2(printk("scsi%ld: %s - Performing hard reset..\n",
- ha->host_no, __func__));
- status = qla4xxx_hard_reset(ha);
+ qla4xxx_flush_active_srbs(ha);
+ if (ql4xxx_lock_drvr_wait(ha) == QLA_SUCCESS)
+ status = qla4xxx_soft_reset(ha);
+ else
+ status = QLA_ERROR;
}
/* Flush any pending ddb changed AENs */
* the mid-level tries to sleep when it reaches the driver threshold
* "host->can_queue". This can cause a panic if we were in our interrupt code.
**/
-static void qla4xxx_do_dpc(void *data)
+static void qla4xxx_do_dpc(struct work_struct *work)
{
- struct scsi_qla_host *ha = (struct scsi_qla_host *) data;
+ struct scsi_qla_host *ha =
+ container_of(work, struct scsi_qla_host, dpc_work);
struct ddb_entry *ddb_entry, *dtemp;
- DEBUG2(printk("scsi%ld: %s: DPC handler waking up.\n",
- ha->host_no, __func__));
-
- DEBUG2(printk("scsi%ld: %s: ha->flags = 0x%08lx\n",
- ha->host_no, __func__, ha->flags));
- DEBUG2(printk("scsi%ld: %s: ha->dpc_flags = 0x%08lx\n",
- ha->host_no, __func__, ha->dpc_flags));
+ DEBUG2(printk("scsi%ld: %s: DPC handler waking up."
+ "flags = 0x%08lx, dpc_flags = 0x%08lx\n",
+ ha->host_no, __func__, ha->flags, ha->dpc_flags));
/* Initialization not yet finished. Don't do anything yet. */
if (!test_bit(AF_INIT_DONE, &ha->flags))
test_bit(DPC_RESET_HA, &ha->dpc_flags) ||
test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags) ||
test_bit(DPC_RESET_HA_DESTROY_DDB_LIST, &ha->dpc_flags)) {
- if (test_bit(DPC_RESET_HA_DESTROY_DDB_LIST, &ha->dpc_flags))
- /*
- * dg 09/23 Never initialize ddb list
- * once we up and running
- * qla4xxx_recover_adapter(ha,
- * REBUILD_DDB_LIST);
- */
- qla4xxx_recover_adapter(ha, PRESERVE_DDB_LIST);
-
- if (test_bit(DPC_RESET_HA, &ha->dpc_flags))
+ if (test_bit(DPC_RESET_HA_DESTROY_DDB_LIST, &ha->dpc_flags) ||
+ test_bit(DPC_RESET_HA, &ha->dpc_flags))
qla4xxx_recover_adapter(ha, PRESERVE_DDB_LIST);
if (test_and_clear_bit(DPC_RESET_HA_INTR, &ha->dpc_flags)) {
destroy_workqueue(ha->dpc_thread);
/* Issue Soft Reset to put firmware in unknown state */
- qla4xxx_soft_reset(ha);
+ if (ql4xxx_lock_drvr_wait(ha) == QLA_SUCCESS)
+ qla4xxx_soft_reset(ha);
/* Remove timer thread, if present */
if (ha->timer_active)
init_waitqueue_head(&ha->mailbox_wait_queue);
spin_lock_init(&ha->hardware_lock);
- spin_lock_init(&ha->list_lock);
/* Allocate dma buffers */
if (qla4xxx_mem_alloc(ha)) {
ret = -ENODEV;
goto probe_failed;
}
- INIT_WORK(&ha->dpc_work, qla4xxx_do_dpc, ha);
+ INIT_WORK(&ha->dpc_work, qla4xxx_do_dpc);
ret = request_irq(pdev->irq, qla4xxx_intr_handler,
SA_INTERRUPT|SA_SHIRQ, "qla4xxx", ha);
return srb;
}
- /**
- * qla4xxx_soft_reset - performs a SOFT RESET of hba.
- * @ha: Pointer to host adapter structure.
- **/
- int qla4xxx_soft_reset(struct scsi_qla_host *ha)
- {
-
- DEBUG2(printk(KERN_WARNING "scsi%ld: %s: chip reset!\n", ha->host_no,
- __func__));
- if (test_bit(AF_TOPCAT_CHIP_PRESENT, &ha->flags)) {
- int status = QLA_ERROR;
-
- if ((qla4010_soft_reset(ha) == QLA_SUCCESS) &&
- (qla4xxx_topcat_reset(ha) == QLA_SUCCESS) &&
- (qla4010_soft_reset(ha) == QLA_SUCCESS) )
- status = QLA_SUCCESS;
- return status;
- } else
- return qla4010_soft_reset(ha);
- }
-
/**
* qla4xxx_eh_wait_on_command - waits for command to be returned by firmware
* @ha: actual ha whose done queue will contain the comd returned by firmware.
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
+ {
+ .vendor = PCI_VENDOR_ID_QLOGIC,
+ .device = PCI_DEVICE_ID_QLOGIC_ISP4032,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
{0, 0},
};
MODULE_DEVICE_TABLE(pci, qla4xxx_pci_tbl);
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/blkdev.h>
- #include <asm/semaphore.h>
+ #include <linux/delay.h>
+ #include <linux/kthread.h>
+ #include <linux/spinlock.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
MODULE_PARM_DESC(max_luns,
"last scsi LUN (should be between 1 and 2^32-1)");
+ #ifdef CONFIG_SCSI_SCAN_ASYNC
+ #define SCSI_SCAN_TYPE_DEFAULT "async"
+ #else
+ #define SCSI_SCAN_TYPE_DEFAULT "sync"
+ #endif
+
+ static char scsi_scan_type[6] = SCSI_SCAN_TYPE_DEFAULT;
+
+ module_param_string(scan, scsi_scan_type, sizeof(scsi_scan_type), S_IRUGO);
+ MODULE_PARM_DESC(scan, "sync, async or none");
+
/*
* max_scsi_report_luns: the maximum number of LUNS that will be
* returned from the REPORT LUNS command. 8 times this value must
"Timeout (in seconds) waiting for devices to answer INQUIRY."
" Default is 5. Some non-compliant devices need more.");
+ static DEFINE_SPINLOCK(async_scan_lock);
+ static LIST_HEAD(scanning_hosts);
+
+ struct async_scan_data {
+ struct list_head list;
+ struct Scsi_Host *shost;
+ struct completion prev_finished;
+ };
+
+ /**
+ * scsi_complete_async_scans - Wait for asynchronous scans to complete
+ *
+ * Asynchronous scans add themselves to the scanning_hosts list. Once
+ * that list is empty, we know that the scans are complete. Rather than
+ * waking up periodically to check the state of the list, we pretend to be
+ * a scanning task by adding ourselves at the end of the list and going to
+ * sleep. When the task before us wakes us up, we take ourselves off the
+ * list and return.
+ */
+ int scsi_complete_async_scans(void)
+ {
+ struct async_scan_data *data;
+
+ do {
+ if (list_empty(&scanning_hosts))
+ return 0;
+ /* If we can't get memory immediately, that's OK. Just
+ * sleep a little. Even if we never get memory, the async
+ * scans will finish eventually.
+ */
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ msleep(1);
+ } while (!data);
+
+ data->shost = NULL;
+ init_completion(&data->prev_finished);
+
+ spin_lock(&async_scan_lock);
+ /* Check that there's still somebody else on the list */
+ if (list_empty(&scanning_hosts))
+ goto done;
+ list_add_tail(&data->list, &scanning_hosts);
+ spin_unlock(&async_scan_lock);
+
+ printk(KERN_INFO "scsi: waiting for bus probes to complete ...\n");
+ wait_for_completion(&data->prev_finished);
+
+ spin_lock(&async_scan_lock);
+ list_del(&data->list);
+ done:
+ spin_unlock(&async_scan_lock);
+
+ kfree(data);
+ return 0;
+ }
+
+ #ifdef MODULE
+ /* Only exported for the benefit of scsi_wait_scan */
+ EXPORT_SYMBOL_GPL(scsi_complete_async_scans);
+ #endif
+
/**
* scsi_unlock_floptical - unlock device via a special MODE SENSE command
* @sdev: scsi device to send command to
goto retry;
}
-static void scsi_target_reap_usercontext(void *data)
+static void scsi_target_reap_usercontext(struct work_struct *work)
{
- struct scsi_target *starget = data;
+ struct scsi_target *starget =
+ container_of(work, struct scsi_target, ew.work);
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
unsigned long flags;
starget->state = STARGET_DEL;
spin_unlock_irqrestore(shost->host_lock, flags);
execute_in_process_context(scsi_target_reap_usercontext,
- starget, &starget->ew);
+ &starget->ew);
return;
}
* SCSI_SCAN_LUN_PRESENT: a new scsi_device was allocated and initialized
**/
static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
- int *bflags)
+ int *bflags, int async)
{
/*
* XXX do not save the inquiry, since it can change underneath us,
* register it and tell the rest of the kernel
* about it.
*/
- if (scsi_sysfs_add_sdev(sdev) != 0)
+ if (!async && scsi_sysfs_add_sdev(sdev) != 0)
return SCSI_SCAN_NO_RESPONSE;
return SCSI_SCAN_LUN_PRESENT;
goto out_free_result;
}
- res = scsi_add_lun(sdev, result, &bflags);
+ res = scsi_add_lun(sdev, result, &bflags, shost->async_scan);
if (res == SCSI_SCAN_LUN_PRESENT) {
if (bflags & BLIST_KEY) {
sdev->lockable = 0;
{
struct Scsi_Host *shost = dev_to_shost(parent);
+ if (strncmp(scsi_scan_type, "none", 4) == 0)
+ return;
+
+ if (!shost->async_scan)
+ scsi_complete_async_scans();
+
mutex_lock(&shost->scan_mutex);
if (scsi_host_scan_allowed(shost))
__scsi_scan_target(parent, channel, id, lun, rescan);
"%s: <%u:%u:%u>\n",
__FUNCTION__, channel, id, lun));
+ if (!shost->async_scan)
+ scsi_complete_async_scans();
+
if (((channel != SCAN_WILD_CARD) && (channel > shost->max_channel)) ||
((id != SCAN_WILD_CARD) && (id >= shost->max_id)) ||
((lun != SCAN_WILD_CARD) && (lun > shost->max_lun)))
return 0;
}
+ static void scsi_sysfs_add_devices(struct Scsi_Host *shost)
+ {
+ struct scsi_device *sdev;
+ shost_for_each_device(sdev, shost) {
+ if (scsi_sysfs_add_sdev(sdev) != 0)
+ scsi_destroy_sdev(sdev);
+ }
+ }
+
+ /**
+ * scsi_prep_async_scan - prepare for an async scan
+ * @shost: the host which will be scanned
+ * Returns: a cookie to be passed to scsi_finish_async_scan()
+ *
+ * Tells the midlayer this host is going to do an asynchronous scan.
+ * It reserves the host's position in the scanning list and ensures
+ * that other asynchronous scans started after this one won't affect the
+ * ordering of the discovered devices.
+ */
+ static struct async_scan_data *scsi_prep_async_scan(struct Scsi_Host *shost)
+ {
+ struct async_scan_data *data;
+
+ if (strncmp(scsi_scan_type, "sync", 4) == 0)
+ return NULL;
+
+ if (shost->async_scan) {
+ printk("%s called twice for host %d", __FUNCTION__,
+ shost->host_no);
+ dump_stack();
+ return NULL;
+ }
+
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ goto err;
+ data->shost = scsi_host_get(shost);
+ if (!data->shost)
+ goto err;
+ init_completion(&data->prev_finished);
+
+ spin_lock(&async_scan_lock);
+ shost->async_scan = 1;
+ if (list_empty(&scanning_hosts))
+ complete(&data->prev_finished);
+ list_add_tail(&data->list, &scanning_hosts);
+ spin_unlock(&async_scan_lock);
+
+ return data;
+
+ err:
+ kfree(data);
+ return NULL;
+ }
+
+ /**
+ * scsi_finish_async_scan - asynchronous scan has finished
+ * @data: cookie returned from earlier call to scsi_prep_async_scan()
+ *
+ * All the devices currently attached to this host have been found.
+ * This function announces all the devices it has found to the rest
+ * of the system.
+ */
+ static void scsi_finish_async_scan(struct async_scan_data *data)
+ {
+ struct Scsi_Host *shost;
+
+ if (!data)
+ return;
+
+ shost = data->shost;
+ if (!shost->async_scan) {
+ printk("%s called twice for host %d", __FUNCTION__,
+ shost->host_no);
+ dump_stack();
+ return;
+ }
+
+ wait_for_completion(&data->prev_finished);
+
+ scsi_sysfs_add_devices(shost);
+
+ spin_lock(&async_scan_lock);
+ shost->async_scan = 0;
+ list_del(&data->list);
+ if (!list_empty(&scanning_hosts)) {
+ struct async_scan_data *next = list_entry(scanning_hosts.next,
+ struct async_scan_data, list);
+ complete(&next->prev_finished);
+ }
+ spin_unlock(&async_scan_lock);
+
+ scsi_host_put(shost);
+ kfree(data);
+ }
+
+ static void do_scsi_scan_host(struct Scsi_Host *shost)
+ {
+ if (shost->hostt->scan_finished) {
+ unsigned long start = jiffies;
+ if (shost->hostt->scan_start)
+ shost->hostt->scan_start(shost);
+
+ while (!shost->hostt->scan_finished(shost, jiffies - start))
+ msleep(10);
+ } else {
+ scsi_scan_host_selected(shost, SCAN_WILD_CARD, SCAN_WILD_CARD,
+ SCAN_WILD_CARD, 0);
+ }
+ }
+
+ static int do_scan_async(void *_data)
+ {
+ struct async_scan_data *data = _data;
+ do_scsi_scan_host(data->shost);
+ scsi_finish_async_scan(data);
+ return 0;
+ }
+
/**
* scsi_scan_host - scan the given adapter
* @shost: adapter to scan
**/
void scsi_scan_host(struct Scsi_Host *shost)
{
- scsi_scan_host_selected(shost, SCAN_WILD_CARD, SCAN_WILD_CARD,
- SCAN_WILD_CARD, 0);
+ struct async_scan_data *data;
+
+ if (strncmp(scsi_scan_type, "none", 4) == 0)
+ return;
+
+ data = scsi_prep_async_scan(shost);
+ if (!data) {
+ do_scsi_scan_host(shost);
+ return;
+ }
+
+ kthread_run(do_scan_async, data, "scsi_scan_%d", shost->host_no);
}
EXPORT_SYMBOL(scsi_scan_host);
void *lldd_dev;
};
+struct sas_discovery_event {
+ struct work_struct work;
+ struct asd_sas_port *port;
+};
+
struct sas_discovery {
spinlock_t disc_event_lock;
- struct work_struct disc_work[DISC_NUM_EVENTS];
+ struct sas_discovery_event disc_work[DISC_NUM_EVENTS];
unsigned long pending;
u8 fanout_sas_addr[8];
u8 eeds_a[8];
void *lldd_port; /* not touched by the sas class code */
};
+struct asd_sas_event {
+ struct work_struct work;
+ struct asd_sas_phy *phy;
+};
+
/* The phy pretty much is controlled by the LLDD.
* The class only reads those fields.
*/
struct asd_sas_phy {
/* private: */
/* protected by ha->event_lock */
- struct work_struct port_events[PORT_NUM_EVENTS];
- struct work_struct phy_events[PHY_NUM_EVENTS];
+ struct asd_sas_event port_events[PORT_NUM_EVENTS];
+ struct asd_sas_event phy_events[PHY_NUM_EVENTS];
unsigned long port_events_pending;
unsigned long phy_events_pending;
int queue_thread_kill;
};
+struct sas_ha_event {
+ struct work_struct work;
+ struct sas_ha_struct *ha;
+};
+
struct sas_ha_struct {
/* private: */
spinlock_t event_lock;
- struct work_struct ha_events[HA_NUM_EVENTS];
+ struct sas_ha_event ha_events[HA_NUM_EVENTS];
unsigned long pending;
struct scsi_core core;
void (*notify_phy_event)(struct asd_sas_phy *, enum phy_event);
void *lldd_ha; /* not touched by sas class code */
+
+ struct list_head eh_done_q;
};
#define SHOST_TO_SAS_HA(_shost) (*(struct sas_ha_struct **)(_shost)->hostdata)
void *lldd_task; /* for use by LLDDs */
void *uldd_task;
+
+ struct work_struct abort_work;
};
- #define SAS_TASK_STATE_PENDING 1
- #define SAS_TASK_STATE_DONE 2
- #define SAS_TASK_STATE_ABORTED 4
+ #define SAS_TASK_STATE_PENDING 1
+ #define SAS_TASK_STATE_DONE 2
+ #define SAS_TASK_STATE_ABORTED 4
+ #define SAS_TASK_INITIATOR_ABORTED 8
static inline struct sas_task *sas_alloc_task(gfp_t flags)
{
extern int sas_register_ha(struct sas_ha_struct *);
extern int sas_unregister_ha(struct sas_ha_struct *);
+ int sas_phy_reset(struct sas_phy *phy, int hard_reset);
extern int sas_queuecommand(struct scsi_cmnd *,
void (*scsi_done)(struct scsi_cmnd *));
extern int sas_target_alloc(struct scsi_target *);
void sas_init_dev(struct domain_device *);
+ void sas_task_abort(struct sas_task *task);
+
#endif /* _SASLIB_H_ */