Commit 29c97684 authored by Salyzyn, Mark's avatar Salyzyn, Mark Committed by James Bottomley

[SCSI] aacraid: add user initiated reset

Add the ability for an application to issue a hardware reset to the
adapter via sysfs. Typical uses include restarting the adapter after it
has been flashed. Bumped revision number for the driver and added a
feature to periodically check the adapter's health (check_interval),
update the adapter's concept of time (update_interval) and block
checking/resetting of the adapter (check_reset).
Signed-off-by: default avatarMark Salyzyn <aacraid@adaptec.com>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
parent 1a655040
...@@ -169,6 +169,18 @@ int acbsize = -1; ...@@ -169,6 +169,18 @@ int acbsize = -1;
module_param(acbsize, int, S_IRUGO|S_IWUSR); module_param(acbsize, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(acbsize, "Request a specific adapter control block (FIB) size. Valid values are 512, 2048, 4096 and 8192. Default is to use suggestion from Firmware."); MODULE_PARM_DESC(acbsize, "Request a specific adapter control block (FIB) size. Valid values are 512, 2048, 4096 and 8192. Default is to use suggestion from Firmware.");
int update_interval = 30 * 60;
module_param(update_interval, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(update_interval, "Interval in seconds between time sync updates issued to adapter.");
int check_interval = 24 * 60 * 60;
module_param(check_interval, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(check_interval, "Interval in seconds between adapter health checks.");
int check_reset = 1;
module_param(check_reset, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(check_reset, "If adapter fails health check, reset the adapter.");
int expose_physicals = -1; int expose_physicals = -1;
module_param(expose_physicals, int, S_IRUGO|S_IWUSR); module_param(expose_physicals, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(expose_physicals, "Expose physical components of the arrays. -1=protect 0=off, 1=on"); MODULE_PARM_DESC(expose_physicals, "Expose physical components of the arrays. -1=protect 0=off, 1=on");
...@@ -1197,6 +1209,12 @@ int aac_get_adapter_info(struct aac_dev* dev) ...@@ -1197,6 +1209,12 @@ int aac_get_adapter_info(struct aac_dev* dev)
(int)sizeof(dev->supplement_adapter_info.VpdInfo.Tsid), (int)sizeof(dev->supplement_adapter_info.VpdInfo.Tsid),
dev->supplement_adapter_info.VpdInfo.Tsid); dev->supplement_adapter_info.VpdInfo.Tsid);
} }
if (!check_reset ||
(dev->supplement_adapter_info.SupportedOptions2 &
le32_to_cpu(AAC_OPTION_IGNORE_RESET))) {
printk(KERN_INFO "%s%d: Reset Adapter Ignored\n",
dev->name, dev->id);
}
} }
dev->nondasd_support = 0; dev->nondasd_support = 0;
......
...@@ -12,8 +12,8 @@ ...@@ -12,8 +12,8 @@
*----------------------------------------------------------------------------*/ *----------------------------------------------------------------------------*/
#ifndef AAC_DRIVER_BUILD #ifndef AAC_DRIVER_BUILD
# define AAC_DRIVER_BUILD 2437 # define AAC_DRIVER_BUILD 2447
# define AAC_DRIVER_BRANCH "-mh4" # define AAC_DRIVER_BRANCH "-ms"
#endif #endif
#define MAXIMUM_NUM_CONTAINERS 32 #define MAXIMUM_NUM_CONTAINERS 32
...@@ -860,10 +860,12 @@ struct aac_supplement_adapter_info ...@@ -860,10 +860,12 @@ struct aac_supplement_adapter_info
__le32 FlashFirmwareBootBuild; __le32 FlashFirmwareBootBuild;
u8 MfgPcbaSerialNo[12]; u8 MfgPcbaSerialNo[12];
u8 MfgWWNName[8]; u8 MfgWWNName[8];
__le32 MoreFeatureBits; __le32 SupportedOptions2;
__le32 ReservedGrowth[1]; __le32 ReservedGrowth[1];
}; };
#define AAC_FEATURE_FALCON 0x00000010 #define AAC_FEATURE_FALCON 0x00000010
#define AAC_OPTION_MU_RESET 0x00000001
#define AAC_OPTION_IGNORE_RESET 0x00000002
#define AAC_SIS_VERSION_V3 3 #define AAC_SIS_VERSION_V3 3
#define AAC_SIS_SLOT_UNKNOWN 0xFF #define AAC_SIS_SLOT_UNKNOWN 0xFF
...@@ -1260,6 +1262,19 @@ struct aac_synchronize_reply { ...@@ -1260,6 +1262,19 @@ struct aac_synchronize_reply {
u8 data[16]; u8 data[16];
}; };
#define CT_PAUSE_IO 65
#define CT_RELEASE_IO 66
struct aac_pause {
__le32 command; /* VM_ContainerConfig */
__le32 type; /* CT_PAUSE_IO */
__le32 timeout; /* 10ms ticks */
__le32 min;
__le32 noRescan;
__le32 parm3;
__le32 parm4;
__le32 count; /* sizeof(((struct aac_pause_reply *)NULL)->data) */
};
struct aac_srb struct aac_srb
{ {
__le32 function; __le32 function;
...@@ -1816,6 +1831,7 @@ int aac_queue_get(struct aac_dev * dev, u32 * index, u32 qid, struct hw_fib * hw ...@@ -1816,6 +1831,7 @@ int aac_queue_get(struct aac_dev * dev, u32 * index, u32 qid, struct hw_fib * hw
unsigned int aac_response_normal(struct aac_queue * q); unsigned int aac_response_normal(struct aac_queue * q);
unsigned int aac_command_normal(struct aac_queue * q); unsigned int aac_command_normal(struct aac_queue * q);
unsigned int aac_intr_normal(struct aac_dev * dev, u32 Index); unsigned int aac_intr_normal(struct aac_dev * dev, u32 Index);
int aac_reset_adapter(struct aac_dev * dev, int forced);
int aac_check_health(struct aac_dev * dev); int aac_check_health(struct aac_dev * dev);
int aac_command_thread(void *data); int aac_command_thread(void *data);
int aac_close_fib_context(struct aac_dev * dev, struct aac_fib_context *fibctx); int aac_close_fib_context(struct aac_dev * dev, struct aac_fib_context *fibctx);
...@@ -1835,3 +1851,6 @@ extern int aif_timeout; ...@@ -1835,3 +1851,6 @@ extern int aif_timeout;
extern int expose_physicals; extern int expose_physicals;
extern int aac_reset_devices; extern int aac_reset_devices;
extern int aac_commit; extern int aac_commit;
extern int update_interval;
extern int check_interval;
extern int check_reset;
...@@ -1021,7 +1021,7 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr) ...@@ -1021,7 +1021,7 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr)
} }
static int _aac_reset_adapter(struct aac_dev *aac) static int _aac_reset_adapter(struct aac_dev *aac, int forced)
{ {
int index, quirks; int index, quirks;
int retval; int retval;
...@@ -1029,25 +1029,32 @@ static int _aac_reset_adapter(struct aac_dev *aac) ...@@ -1029,25 +1029,32 @@ static int _aac_reset_adapter(struct aac_dev *aac)
struct scsi_device *dev; struct scsi_device *dev;
struct scsi_cmnd *command; struct scsi_cmnd *command;
struct scsi_cmnd *command_list; struct scsi_cmnd *command_list;
int jafo = 0;
/* /*
* Assumptions: * Assumptions:
* - host is locked. * - host is locked, unless called by the aacraid thread.
* (a matter of convenience, due to legacy issues surrounding
* eh_host_adapter_reset).
* - in_reset is asserted, so no new i/o is getting to the * - in_reset is asserted, so no new i/o is getting to the
* card. * card.
* - The card is dead. * - The card is dead, or will be very shortly ;-/ so no new
* commands are completing in the interrupt service.
*/ */
host = aac->scsi_host_ptr; host = aac->scsi_host_ptr;
scsi_block_requests(host); scsi_block_requests(host);
aac_adapter_disable_int(aac); aac_adapter_disable_int(aac);
if (aac->thread->pid != current->pid) {
spin_unlock_irq(host->host_lock); spin_unlock_irq(host->host_lock);
kthread_stop(aac->thread); kthread_stop(aac->thread);
jafo = 1;
}
/* /*
* If a positive health, means in a known DEAD PANIC * If a positive health, means in a known DEAD PANIC
* state and the adapter could be reset to `try again'. * state and the adapter could be reset to `try again'.
*/ */
retval = aac_adapter_restart(aac, aac_adapter_check_health(aac)); retval = aac_adapter_restart(aac, forced ? 0 : aac_adapter_check_health(aac));
if (retval) if (retval)
goto out; goto out;
...@@ -1104,11 +1111,13 @@ static int _aac_reset_adapter(struct aac_dev *aac) ...@@ -1104,11 +1111,13 @@ static int _aac_reset_adapter(struct aac_dev *aac)
if (aac_get_driver_ident(index)->quirks & AAC_QUIRK_31BIT) if (aac_get_driver_ident(index)->quirks & AAC_QUIRK_31BIT)
if ((retval = pci_set_dma_mask(aac->pdev, DMA_32BIT_MASK))) if ((retval = pci_set_dma_mask(aac->pdev, DMA_32BIT_MASK)))
goto out; goto out;
if (jafo) {
aac->thread = kthread_run(aac_command_thread, aac, aac->name); aac->thread = kthread_run(aac_command_thread, aac, aac->name);
if (IS_ERR(aac->thread)) { if (IS_ERR(aac->thread)) {
retval = PTR_ERR(aac->thread); retval = PTR_ERR(aac->thread);
goto out; goto out;
} }
}
(void)aac_get_adapter_info(aac); (void)aac_get_adapter_info(aac);
quirks = aac_get_driver_ident(index)->quirks; quirks = aac_get_driver_ident(index)->quirks;
if ((quirks & AAC_QUIRK_34SG) && (host->sg_tablesize > 34)) { if ((quirks & AAC_QUIRK_34SG) && (host->sg_tablesize > 34)) {
...@@ -1150,7 +1159,98 @@ static int _aac_reset_adapter(struct aac_dev *aac) ...@@ -1150,7 +1159,98 @@ static int _aac_reset_adapter(struct aac_dev *aac)
out: out:
aac->in_reset = 0; aac->in_reset = 0;
scsi_unblock_requests(host); scsi_unblock_requests(host);
if (jafo) {
spin_lock_irq(host->host_lock); spin_lock_irq(host->host_lock);
}
return retval;
}
int aac_reset_adapter(struct aac_dev * aac, int forced)
{
unsigned long flagv = 0;
int retval;
struct Scsi_Host * host;
if (spin_trylock_irqsave(&aac->fib_lock, flagv) == 0)
return -EBUSY;
if (aac->in_reset) {
spin_unlock_irqrestore(&aac->fib_lock, flagv);
return -EBUSY;
}
aac->in_reset = 1;
spin_unlock_irqrestore(&aac->fib_lock, flagv);
/*
* Wait for all commands to complete to this specific
* target (block maximum 60 seconds). Although not necessary,
* it does make us a good storage citizen.
*/
host = aac->scsi_host_ptr;
scsi_block_requests(host);
if (forced < 2) for (retval = 60; retval; --retval) {
struct scsi_device * dev;
struct scsi_cmnd * command;
int active = 0;
__shost_for_each_device(dev, host) {
spin_lock_irqsave(&dev->list_lock, flagv);
list_for_each_entry(command, &dev->cmd_list, list) {
if (command->SCp.phase == AAC_OWNER_FIRMWARE) {
active++;
break;
}
}
spin_unlock_irqrestore(&dev->list_lock, flagv);
if (active)
break;
}
/*
* We can exit If all the commands are complete
*/
if (active == 0)
break;
ssleep(1);
}
/* Quiesce build, flush cache, write through mode */
aac_send_shutdown(aac);
spin_lock_irqsave(host->host_lock, flagv);
retval = _aac_reset_adapter(aac, forced);
spin_unlock_irqrestore(host->host_lock, flagv);
if (retval == -ENODEV) {
/* Unwind aac_send_shutdown() IOP_RESET unsupported/disabled */
struct fib * fibctx = aac_fib_alloc(aac);
if (fibctx) {
struct aac_pause *cmd;
int status;
aac_fib_init(fibctx);
cmd = (struct aac_pause *) fib_data(fibctx);
cmd->command = cpu_to_le32(VM_ContainerConfig);
cmd->type = cpu_to_le32(CT_PAUSE_IO);
cmd->timeout = cpu_to_le32(1);
cmd->min = cpu_to_le32(1);
cmd->noRescan = cpu_to_le32(1);
cmd->count = cpu_to_le32(0);
status = aac_fib_send(ContainerCommand,
fibctx,
sizeof(struct aac_pause),
FsaNormal,
-2 /* Timeout silently */, 1,
NULL, NULL);
if (status >= 0)
aac_fib_complete(fibctx);
aac_fib_free(fibctx);
}
}
return retval; return retval;
} }
...@@ -1270,9 +1370,14 @@ int aac_check_health(struct aac_dev * aac) ...@@ -1270,9 +1370,14 @@ int aac_check_health(struct aac_dev * aac)
printk(KERN_ERR "%s: Host adapter BLINK LED 0x%x\n", aac->name, BlinkLED); printk(KERN_ERR "%s: Host adapter BLINK LED 0x%x\n", aac->name, BlinkLED);
if (!check_reset || (aac->supplement_adapter_info.SupportedOptions2 &
le32_to_cpu(AAC_OPTION_IGNORE_RESET)))
goto out;
host = aac->scsi_host_ptr; host = aac->scsi_host_ptr;
if (aac->thread->pid != current->pid)
spin_lock_irqsave(host->host_lock, flagv); spin_lock_irqsave(host->host_lock, flagv);
BlinkLED = _aac_reset_adapter(aac); BlinkLED = _aac_reset_adapter(aac, 0);
if (aac->thread->pid != current->pid)
spin_unlock_irqrestore(host->host_lock, flagv); spin_unlock_irqrestore(host->host_lock, flagv);
return BlinkLED; return BlinkLED;
...@@ -1300,6 +1405,9 @@ int aac_command_thread(void *data) ...@@ -1300,6 +1405,9 @@ int aac_command_thread(void *data)
struct aac_fib_context *fibctx; struct aac_fib_context *fibctx;
unsigned long flags; unsigned long flags;
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
unsigned long next_jiffies = jiffies + HZ;
unsigned long next_check_jiffies = next_jiffies;
long difference = HZ;
/* /*
* We can only have one thread per adapter for AIF's. * We can only have one thread per adapter for AIF's.
...@@ -1507,11 +1615,79 @@ int aac_command_thread(void *data) ...@@ -1507,11 +1615,79 @@ int aac_command_thread(void *data)
* There are no more AIF's * There are no more AIF's
*/ */
spin_unlock_irqrestore(dev->queues->queue[HostNormCmdQueue].lock, flags); spin_unlock_irqrestore(dev->queues->queue[HostNormCmdQueue].lock, flags);
schedule();
if (kthread_should_stop()) /*
* Background activity
*/
if ((time_before(next_check_jiffies,next_jiffies))
&& ((difference = next_check_jiffies - jiffies) <= 0)) {
next_check_jiffies = next_jiffies;
if (aac_check_health(dev) == 0) {
difference = ((long)(unsigned)check_interval)
* HZ;
next_check_jiffies = jiffies + difference;
} else if (!dev->queues)
break; break;
}
if (!time_before(next_check_jiffies,next_jiffies)
&& ((difference = next_jiffies - jiffies) <= 0)) {
struct timeval now;
int ret;
/* Don't even try to talk to adapter if its sick */
ret = aac_check_health(dev);
if (!ret && !dev->queues)
break;
next_check_jiffies = jiffies
+ ((long)(unsigned)check_interval)
* HZ;
do_gettimeofday(&now);
/* Synchronize our watches */
if (((1000000 - (1000000 / HZ)) > now.tv_usec)
&& (now.tv_usec > (1000000 / HZ)))
difference = (((1000000 - now.tv_usec) * HZ)
+ 500000) / 1000000;
else if (ret == 0) {
struct fib *fibptr;
if ((fibptr = aac_fib_alloc(dev))) {
u32 * info;
aac_fib_init(fibptr);
info = (u32 *) fib_data(fibptr);
if (now.tv_usec > 500000)
++now.tv_sec;
*info = cpu_to_le32(now.tv_sec);
(void)aac_fib_send(SendHostTime,
fibptr,
sizeof(*info),
FsaNormal,
1, 1,
NULL,
NULL);
aac_fib_complete(fibptr);
aac_fib_free(fibptr);
}
difference = (long)(unsigned)update_interval*HZ;
} else {
/* retry shortly */
difference = 10 * HZ;
}
next_jiffies = jiffies + difference;
if (time_before(next_check_jiffies,next_jiffies))
difference = next_check_jiffies - jiffies;
}
if (difference <= 0)
difference = 1;
set_current_state(TASK_INTERRUPTIBLE); set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(difference);
if (kthread_should_stop())
break;
} }
if (dev->queues) if (dev->queues)
remove_wait_queue(&dev->queues->queue[HostNormCmdQueue].cmdready, &wait); remove_wait_queue(&dev->queues->queue[HostNormCmdQueue].cmdready, &wait);
......
...@@ -39,10 +39,8 @@ ...@@ -39,10 +39,8 @@
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/dma-mapping.h>
#include <linux/syscalls.h> #include <linux/syscalls.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/smp_lock.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <asm/semaphore.h> #include <asm/semaphore.h>
...@@ -581,6 +579,14 @@ static int aac_eh_reset(struct scsi_cmnd* cmd) ...@@ -581,6 +579,14 @@ static int aac_eh_reset(struct scsi_cmnd* cmd)
ssleep(1); ssleep(1);
} }
printk(KERN_ERR "%s: SCSI bus appears hung\n", AAC_DRIVERNAME); printk(KERN_ERR "%s: SCSI bus appears hung\n", AAC_DRIVERNAME);
/*
* This adapter needs a blind reset, only do so for Adapters that
* support a register, instead of a commanded, reset.
*/
if ((aac->supplement_adapter_info.SupportedOptions2 &
le32_to_cpu(AAC_OPTION_MU_RESET|AAC_OPTION_IGNORE_RESET)) ==
le32_to_cpu(AAC_OPTION_MU_RESET))
aac_reset_adapter(aac, 2); /* Bypass wait for command quiesce */
return SUCCESS; /* Cause an immediate retry of the command with a ten second delay after successful tur */ return SUCCESS; /* Cause an immediate retry of the command with a ten second delay after successful tur */
} }
...@@ -788,6 +794,31 @@ static ssize_t aac_show_max_id(struct class_device *class_dev, char *buf) ...@@ -788,6 +794,31 @@ static ssize_t aac_show_max_id(struct class_device *class_dev, char *buf)
class_to_shost(class_dev)->max_id); class_to_shost(class_dev)->max_id);
} }
static ssize_t aac_store_reset_adapter(struct class_device *class_dev,
const char *buf, size_t count)
{
int retval = -EACCES;
if (!capable(CAP_SYS_ADMIN))
return retval;
retval = aac_reset_adapter((struct aac_dev*)class_to_shost(class_dev)->hostdata, buf[0] == '!');
if (retval >= 0)
retval = count;
return retval;
}
static ssize_t aac_show_reset_adapter(struct class_device *class_dev,
char *buf)
{
struct aac_dev *dev = (struct aac_dev*)class_to_shost(class_dev)->hostdata;
int len, tmp;
tmp = aac_adapter_check_health(dev);
if ((tmp == 0) && dev->in_reset)
tmp = -EBUSY;
len = snprintf(buf, PAGE_SIZE, "0x%x", tmp);
return len;
}
static struct class_device_attribute aac_model = { static struct class_device_attribute aac_model = {
.attr = { .attr = {
...@@ -845,6 +876,14 @@ static struct class_device_attribute aac_max_id = { ...@@ -845,6 +876,14 @@ static struct class_device_attribute aac_max_id = {
}, },
.show = aac_show_max_id, .show = aac_show_max_id,
}; };
static struct class_device_attribute aac_reset = {
.attr = {
.name = "reset_host",
.mode = S_IWUSR|S_IRUGO,
},
.store = aac_store_reset_adapter,
.show = aac_show_reset_adapter,
};
static struct class_device_attribute *aac_attrs[] = { static struct class_device_attribute *aac_attrs[] = {
&aac_model, &aac_model,
...@@ -855,6 +894,7 @@ static struct class_device_attribute *aac_attrs[] = { ...@@ -855,6 +894,7 @@ static struct class_device_attribute *aac_attrs[] = {
&aac_serial_number, &aac_serial_number,
&aac_max_channel, &aac_max_channel,
&aac_max_id, &aac_max_id,
&aac_reset,
NULL NULL
}; };
...@@ -1118,7 +1158,7 @@ static int __init aac_init(void) ...@@ -1118,7 +1158,7 @@ static int __init aac_init(void)
{ {
int error; int error;
printk(KERN_INFO "Adaptec %s driver (%s)\n", printk(KERN_INFO "Adaptec %s driver %s\n",
AAC_DRIVERNAME, aac_driver_version); AAC_DRIVERNAME, aac_driver_version);
error = pci_register_driver(&aac_pci_driver); error = pci_register_driver(&aac_pci_driver);
......
...@@ -464,6 +464,8 @@ static int aac_rx_restart_adapter(struct aac_dev *dev, int bled) ...@@ -464,6 +464,8 @@ static int aac_rx_restart_adapter(struct aac_dev *dev, int bled)
{ {
u32 var; u32 var;
if (!(dev->supplement_adapter_info.SupportedOptions2 &
le32_to_cpu(AAC_OPTION_MU_RESET)) || (bled >= 0) || (bled == -2)) {
if (bled) if (bled)
printk(KERN_ERR "%s%d: adapter kernel panic'd %x.\n", printk(KERN_ERR "%s%d: adapter kernel panic'd %x.\n",
dev->name, dev->id, bled); dev->name, dev->id, bled);
...@@ -479,6 +481,7 @@ static int aac_rx_restart_adapter(struct aac_dev *dev, int bled) ...@@ -479,6 +481,7 @@ static int aac_rx_restart_adapter(struct aac_dev *dev, int bled)
if (bled && (bled != -ETIMEDOUT)) if (bled && (bled != -ETIMEDOUT))
return -EINVAL; return -EINVAL;
}
if (bled || (var == 0x3803000F)) { /* USE_OTHER_METHOD */ if (bled || (var == 0x3803000F)) { /* USE_OTHER_METHOD */
rx_writel(dev, MUnit.reserved2, 3); rx_writel(dev, MUnit.reserved2, 3);
msleep(5000); /* Delay 5 seconds */ msleep(5000); /* Delay 5 seconds */
...@@ -596,7 +599,7 @@ int _aac_rx_init(struct aac_dev *dev) ...@@ -596,7 +599,7 @@ int _aac_rx_init(struct aac_dev *dev)
} }
msleep(1); msleep(1);
} }
if (restart) if (restart && aac_commit)
aac_commit = 1; aac_commit = 1;
/* /*
* Fill in the common function dispatch table. * Fill in the common function dispatch table.
......
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