diff options
author | Salyzyn, Mark <mark_salyzyn@adaptec.com> | 2007-06-12 09:33:54 -0400 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.il.steeleye.com> | 2007-06-17 16:00:47 -0400 |
commit | 29c976844d0bef07d97babc8db60fa6c46788133 (patch) | |
tree | 9543cef49748d0fe7ac08a5a1780c213e0fc37bd /drivers/scsi/aacraid/commsup.c | |
parent | 1a655040c24ebf3954ad5cf8848391cb420b1ffb (diff) |
[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: Mark Salyzyn <aacraid@adaptec.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/scsi/aacraid/commsup.c')
-rw-r--r-- | drivers/scsi/aacraid/commsup.c | 210 |
1 files changed, 193 insertions, 17 deletions
diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c index 9aca57eda943..d510839c0bb2 100644 --- a/drivers/scsi/aacraid/commsup.c +++ b/drivers/scsi/aacraid/commsup.c | |||
@@ -1021,7 +1021,7 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr) | |||
1021 | 1021 | ||
1022 | } | 1022 | } |
1023 | 1023 | ||
1024 | static int _aac_reset_adapter(struct aac_dev *aac) | 1024 | static int _aac_reset_adapter(struct aac_dev *aac, int forced) |
1025 | { | 1025 | { |
1026 | int index, quirks; | 1026 | int index, quirks; |
1027 | int retval; | 1027 | int retval; |
@@ -1029,25 +1029,32 @@ static int _aac_reset_adapter(struct aac_dev *aac) | |||
1029 | struct scsi_device *dev; | 1029 | struct scsi_device *dev; |
1030 | struct scsi_cmnd *command; | 1030 | struct scsi_cmnd *command; |
1031 | struct scsi_cmnd *command_list; | 1031 | struct scsi_cmnd *command_list; |
1032 | int jafo = 0; | ||
1032 | 1033 | ||
1033 | /* | 1034 | /* |
1034 | * Assumptions: | 1035 | * Assumptions: |
1035 | * - host is locked. | 1036 | * - host is locked, unless called by the aacraid thread. |
1037 | * (a matter of convenience, due to legacy issues surrounding | ||
1038 | * eh_host_adapter_reset). | ||
1036 | * - in_reset is asserted, so no new i/o is getting to the | 1039 | * - in_reset is asserted, so no new i/o is getting to the |
1037 | * card. | 1040 | * card. |
1038 | * - The card is dead. | 1041 | * - The card is dead, or will be very shortly ;-/ so no new |
1042 | * commands are completing in the interrupt service. | ||
1039 | */ | 1043 | */ |
1040 | host = aac->scsi_host_ptr; | 1044 | host = aac->scsi_host_ptr; |
1041 | scsi_block_requests(host); | 1045 | scsi_block_requests(host); |
1042 | aac_adapter_disable_int(aac); | 1046 | aac_adapter_disable_int(aac); |
1043 | spin_unlock_irq(host->host_lock); | 1047 | if (aac->thread->pid != current->pid) { |
1044 | kthread_stop(aac->thread); | 1048 | spin_unlock_irq(host->host_lock); |
1049 | kthread_stop(aac->thread); | ||
1050 | jafo = 1; | ||
1051 | } | ||
1045 | 1052 | ||
1046 | /* | 1053 | /* |
1047 | * If a positive health, means in a known DEAD PANIC | 1054 | * If a positive health, means in a known DEAD PANIC |
1048 | * state and the adapter could be reset to `try again'. | 1055 | * state and the adapter could be reset to `try again'. |
1049 | */ | 1056 | */ |
1050 | retval = aac_adapter_restart(aac, aac_adapter_check_health(aac)); | 1057 | retval = aac_adapter_restart(aac, forced ? 0 : aac_adapter_check_health(aac)); |
1051 | 1058 | ||
1052 | if (retval) | 1059 | if (retval) |
1053 | goto out; | 1060 | goto out; |
@@ -1104,10 +1111,12 @@ static int _aac_reset_adapter(struct aac_dev *aac) | |||
1104 | if (aac_get_driver_ident(index)->quirks & AAC_QUIRK_31BIT) | 1111 | if (aac_get_driver_ident(index)->quirks & AAC_QUIRK_31BIT) |
1105 | if ((retval = pci_set_dma_mask(aac->pdev, DMA_32BIT_MASK))) | 1112 | if ((retval = pci_set_dma_mask(aac->pdev, DMA_32BIT_MASK))) |
1106 | goto out; | 1113 | goto out; |
1107 | aac->thread = kthread_run(aac_command_thread, aac, aac->name); | 1114 | if (jafo) { |
1108 | if (IS_ERR(aac->thread)) { | 1115 | aac->thread = kthread_run(aac_command_thread, aac, aac->name); |
1109 | retval = PTR_ERR(aac->thread); | 1116 | if (IS_ERR(aac->thread)) { |
1110 | goto out; | 1117 | retval = PTR_ERR(aac->thread); |
1118 | goto out; | ||
1119 | } | ||
1111 | } | 1120 | } |
1112 | (void)aac_get_adapter_info(aac); | 1121 | (void)aac_get_adapter_info(aac); |
1113 | quirks = aac_get_driver_ident(index)->quirks; | 1122 | quirks = aac_get_driver_ident(index)->quirks; |
@@ -1150,7 +1159,98 @@ static int _aac_reset_adapter(struct aac_dev *aac) | |||
1150 | out: | 1159 | out: |
1151 | aac->in_reset = 0; | 1160 | aac->in_reset = 0; |
1152 | scsi_unblock_requests(host); | 1161 | scsi_unblock_requests(host); |
1153 | spin_lock_irq(host->host_lock); | 1162 | if (jafo) { |
1163 | spin_lock_irq(host->host_lock); | ||
1164 | } | ||
1165 | return retval; | ||
1166 | } | ||
1167 | |||
1168 | int aac_reset_adapter(struct aac_dev * aac, int forced) | ||
1169 | { | ||
1170 | unsigned long flagv = 0; | ||
1171 | int retval; | ||
1172 | struct Scsi_Host * host; | ||
1173 | |||
1174 | if (spin_trylock_irqsave(&aac->fib_lock, flagv) == 0) | ||
1175 | return -EBUSY; | ||
1176 | |||
1177 | if (aac->in_reset) { | ||
1178 | spin_unlock_irqrestore(&aac->fib_lock, flagv); | ||
1179 | return -EBUSY; | ||
1180 | } | ||
1181 | aac->in_reset = 1; | ||
1182 | spin_unlock_irqrestore(&aac->fib_lock, flagv); | ||
1183 | |||
1184 | /* | ||
1185 | * Wait for all commands to complete to this specific | ||
1186 | * target (block maximum 60 seconds). Although not necessary, | ||
1187 | * it does make us a good storage citizen. | ||
1188 | */ | ||
1189 | host = aac->scsi_host_ptr; | ||
1190 | scsi_block_requests(host); | ||
1191 | if (forced < 2) for (retval = 60; retval; --retval) { | ||
1192 | struct scsi_device * dev; | ||
1193 | struct scsi_cmnd * command; | ||
1194 | int active = 0; | ||
1195 | |||
1196 | __shost_for_each_device(dev, host) { | ||
1197 | spin_lock_irqsave(&dev->list_lock, flagv); | ||
1198 | list_for_each_entry(command, &dev->cmd_list, list) { | ||
1199 | if (command->SCp.phase == AAC_OWNER_FIRMWARE) { | ||
1200 | active++; | ||
1201 | break; | ||
1202 | } | ||
1203 | } | ||
1204 | spin_unlock_irqrestore(&dev->list_lock, flagv); | ||
1205 | if (active) | ||
1206 | break; | ||
1207 | |||
1208 | } | ||
1209 | /* | ||
1210 | * We can exit If all the commands are complete | ||
1211 | */ | ||
1212 | if (active == 0) | ||
1213 | break; | ||
1214 | ssleep(1); | ||
1215 | } | ||
1216 | |||
1217 | /* Quiesce build, flush cache, write through mode */ | ||
1218 | aac_send_shutdown(aac); | ||
1219 | spin_lock_irqsave(host->host_lock, flagv); | ||
1220 | retval = _aac_reset_adapter(aac, forced); | ||
1221 | spin_unlock_irqrestore(host->host_lock, flagv); | ||
1222 | |||
1223 | if (retval == -ENODEV) { | ||
1224 | /* Unwind aac_send_shutdown() IOP_RESET unsupported/disabled */ | ||
1225 | struct fib * fibctx = aac_fib_alloc(aac); | ||
1226 | if (fibctx) { | ||
1227 | struct aac_pause *cmd; | ||
1228 | int status; | ||
1229 | |||
1230 | aac_fib_init(fibctx); | ||
1231 | |||
1232 | cmd = (struct aac_pause *) fib_data(fibctx); | ||
1233 | |||
1234 | cmd->command = cpu_to_le32(VM_ContainerConfig); | ||
1235 | cmd->type = cpu_to_le32(CT_PAUSE_IO); | ||
1236 | cmd->timeout = cpu_to_le32(1); | ||
1237 | cmd->min = cpu_to_le32(1); | ||
1238 | cmd->noRescan = cpu_to_le32(1); | ||
1239 | cmd->count = cpu_to_le32(0); | ||
1240 | |||
1241 | status = aac_fib_send(ContainerCommand, | ||
1242 | fibctx, | ||
1243 | sizeof(struct aac_pause), | ||
1244 | FsaNormal, | ||
1245 | -2 /* Timeout silently */, 1, | ||
1246 | NULL, NULL); | ||
1247 | |||
1248 | if (status >= 0) | ||
1249 | aac_fib_complete(fibctx); | ||
1250 | aac_fib_free(fibctx); | ||
1251 | } | ||
1252 | } | ||
1253 | |||
1154 | return retval; | 1254 | return retval; |
1155 | } | 1255 | } |
1156 | 1256 | ||
@@ -1270,10 +1370,15 @@ int aac_check_health(struct aac_dev * aac) | |||
1270 | 1370 | ||
1271 | printk(KERN_ERR "%s: Host adapter BLINK LED 0x%x\n", aac->name, BlinkLED); | 1371 | printk(KERN_ERR "%s: Host adapter BLINK LED 0x%x\n", aac->name, BlinkLED); |
1272 | 1372 | ||
1373 | if (!check_reset || (aac->supplement_adapter_info.SupportedOptions2 & | ||
1374 | le32_to_cpu(AAC_OPTION_IGNORE_RESET))) | ||
1375 | goto out; | ||
1273 | host = aac->scsi_host_ptr; | 1376 | host = aac->scsi_host_ptr; |
1274 | spin_lock_irqsave(host->host_lock, flagv); | 1377 | if (aac->thread->pid != current->pid) |
1275 | BlinkLED = _aac_reset_adapter(aac); | 1378 | spin_lock_irqsave(host->host_lock, flagv); |
1276 | spin_unlock_irqrestore(host->host_lock, flagv); | 1379 | BlinkLED = _aac_reset_adapter(aac, 0); |
1380 | if (aac->thread->pid != current->pid) | ||
1381 | spin_unlock_irqrestore(host->host_lock, flagv); | ||
1277 | return BlinkLED; | 1382 | return BlinkLED; |
1278 | 1383 | ||
1279 | out: | 1384 | out: |
@@ -1300,6 +1405,9 @@ int aac_command_thread(void *data) | |||
1300 | struct aac_fib_context *fibctx; | 1405 | struct aac_fib_context *fibctx; |
1301 | unsigned long flags; | 1406 | unsigned long flags; |
1302 | DECLARE_WAITQUEUE(wait, current); | 1407 | DECLARE_WAITQUEUE(wait, current); |
1408 | unsigned long next_jiffies = jiffies + HZ; | ||
1409 | unsigned long next_check_jiffies = next_jiffies; | ||
1410 | long difference = HZ; | ||
1303 | 1411 | ||
1304 | /* | 1412 | /* |
1305 | * We can only have one thread per adapter for AIF's. | 1413 | * We can only have one thread per adapter for AIF's. |
@@ -1368,7 +1476,7 @@ int aac_command_thread(void *data) | |||
1368 | cpu_to_le32(AifCmdJobProgress))) { | 1476 | cpu_to_le32(AifCmdJobProgress))) { |
1369 | aac_handle_aif(dev, fib); | 1477 | aac_handle_aif(dev, fib); |
1370 | } | 1478 | } |
1371 | 1479 | ||
1372 | time_now = jiffies/HZ; | 1480 | time_now = jiffies/HZ; |
1373 | 1481 | ||
1374 | /* | 1482 | /* |
@@ -1507,11 +1615,79 @@ int aac_command_thread(void *data) | |||
1507 | * There are no more AIF's | 1615 | * There are no more AIF's |
1508 | */ | 1616 | */ |
1509 | spin_unlock_irqrestore(dev->queues->queue[HostNormCmdQueue].lock, flags); | 1617 | spin_unlock_irqrestore(dev->queues->queue[HostNormCmdQueue].lock, flags); |
1510 | schedule(); | 1618 | |
1619 | /* | ||
1620 | * Background activity | ||
1621 | */ | ||
1622 | if ((time_before(next_check_jiffies,next_jiffies)) | ||
1623 | && ((difference = next_check_jiffies - jiffies) <= 0)) { | ||
1624 | next_check_jiffies = next_jiffies; | ||
1625 | if (aac_check_health(dev) == 0) { | ||
1626 | difference = ((long)(unsigned)check_interval) | ||
1627 | * HZ; | ||
1628 | next_check_jiffies = jiffies + difference; | ||
1629 | } else if (!dev->queues) | ||
1630 | break; | ||
1631 | } | ||
1632 | if (!time_before(next_check_jiffies,next_jiffies) | ||
1633 | && ((difference = next_jiffies - jiffies) <= 0)) { | ||
1634 | struct timeval now; | ||
1635 | int ret; | ||
1636 | |||
1637 | /* Don't even try to talk to adapter if its sick */ | ||
1638 | ret = aac_check_health(dev); | ||
1639 | if (!ret && !dev->queues) | ||
1640 | break; | ||
1641 | next_check_jiffies = jiffies | ||
1642 | + ((long)(unsigned)check_interval) | ||
1643 | * HZ; | ||
1644 | do_gettimeofday(&now); | ||
1645 | |||
1646 | /* Synchronize our watches */ | ||
1647 | if (((1000000 - (1000000 / HZ)) > now.tv_usec) | ||
1648 | && (now.tv_usec > (1000000 / HZ))) | ||
1649 | difference = (((1000000 - now.tv_usec) * HZ) | ||
1650 | + 500000) / 1000000; | ||
1651 | else if (ret == 0) { | ||
1652 | struct fib *fibptr; | ||
1653 | |||
1654 | if ((fibptr = aac_fib_alloc(dev))) { | ||
1655 | u32 * info; | ||
1656 | |||
1657 | aac_fib_init(fibptr); | ||
1658 | |||
1659 | info = (u32 *) fib_data(fibptr); | ||
1660 | if (now.tv_usec > 500000) | ||
1661 | ++now.tv_sec; | ||
1662 | |||
1663 | *info = cpu_to_le32(now.tv_sec); | ||
1664 | |||
1665 | (void)aac_fib_send(SendHostTime, | ||
1666 | fibptr, | ||
1667 | sizeof(*info), | ||
1668 | FsaNormal, | ||
1669 | 1, 1, | ||
1670 | NULL, | ||
1671 | NULL); | ||
1672 | aac_fib_complete(fibptr); | ||
1673 | aac_fib_free(fibptr); | ||
1674 | } | ||
1675 | difference = (long)(unsigned)update_interval*HZ; | ||
1676 | } else { | ||
1677 | /* retry shortly */ | ||
1678 | difference = 10 * HZ; | ||
1679 | } | ||
1680 | next_jiffies = jiffies + difference; | ||
1681 | if (time_before(next_check_jiffies,next_jiffies)) | ||
1682 | difference = next_check_jiffies - jiffies; | ||
1683 | } | ||
1684 | if (difference <= 0) | ||
1685 | difference = 1; | ||
1686 | set_current_state(TASK_INTERRUPTIBLE); | ||
1687 | schedule_timeout(difference); | ||
1511 | 1688 | ||
1512 | if (kthread_should_stop()) | 1689 | if (kthread_should_stop()) |
1513 | break; | 1690 | break; |
1514 | set_current_state(TASK_INTERRUPTIBLE); | ||
1515 | } | 1691 | } |
1516 | if (dev->queues) | 1692 | if (dev->queues) |
1517 | remove_wait_queue(&dev->queues->queue[HostNormCmdQueue].cmdready, &wait); | 1693 | remove_wait_queue(&dev->queues->queue[HostNormCmdQueue].cmdready, &wait); |