diff options
author | Yang, Bo <Bo.Yang@lsi.com> | 2009-10-06 16:47:35 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@suse.de> | 2009-10-29 13:03:21 -0400 |
commit | 0c79e681eef10810a5ed41a2eb1dce244ab1c37d (patch) | |
tree | c6b389d9acafd043ddbc5b8ea159f1ee1d4fc899 /drivers/scsi/megaraid | |
parent | f4c9a1317d32bb0af7546ef0c1dcc3be52dc8d0a (diff) |
[SCSI] megaraid_sas: Fix the fix for fw hang caused by megaraid sas application
Add a lock to the skinny firmware initialisation sequence to prevent
the two stage write being non atomic if multiple instances use it.
Add a flag to the driver shutdown sequence to prevent aen ioctls being
called after shutdown begins.
Signed-off-by Bo Yang<bo.yang@lsi.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
Diffstat (limited to 'drivers/scsi/megaraid')
-rw-r--r-- | drivers/scsi/megaraid/megaraid_sas.c | 75 | ||||
-rw-r--r-- | drivers/scsi/megaraid/megaraid_sas.h | 25 |
2 files changed, 77 insertions, 23 deletions
diff --git a/drivers/scsi/megaraid/megaraid_sas.c b/drivers/scsi/megaraid/megaraid_sas.c index 4c04a68bad6c..6d998e050338 100644 --- a/drivers/scsi/megaraid/megaraid_sas.c +++ b/drivers/scsi/megaraid/megaraid_sas.c | |||
@@ -226,7 +226,10 @@ megasas_clear_intr_xscale(struct megasas_register_set __iomem * regs) | |||
226 | * @regs : MFI register set | 226 | * @regs : MFI register set |
227 | */ | 227 | */ |
228 | static inline void | 228 | static inline void |
229 | megasas_fire_cmd_xscale(dma_addr_t frame_phys_addr,u32 frame_count, struct megasas_register_set __iomem *regs) | 229 | megasas_fire_cmd_xscale(struct megasas_instance *instance, |
230 | dma_addr_t frame_phys_addr, | ||
231 | u32 frame_count, | ||
232 | struct megasas_register_set __iomem *regs) | ||
230 | { | 233 | { |
231 | writel((frame_phys_addr >> 3)|(frame_count), | 234 | writel((frame_phys_addr >> 3)|(frame_count), |
232 | &(regs)->inbound_queue_port); | 235 | &(regs)->inbound_queue_port); |
@@ -323,7 +326,10 @@ megasas_clear_intr_ppc(struct megasas_register_set __iomem * regs) | |||
323 | * @regs : MFI register set | 326 | * @regs : MFI register set |
324 | */ | 327 | */ |
325 | static inline void | 328 | static inline void |
326 | megasas_fire_cmd_ppc(dma_addr_t frame_phys_addr, u32 frame_count, struct megasas_register_set __iomem *regs) | 329 | megasas_fire_cmd_ppc(struct megasas_instance *instance, |
330 | dma_addr_t frame_phys_addr, | ||
331 | u32 frame_count, | ||
332 | struct megasas_register_set __iomem *regs) | ||
327 | { | 333 | { |
328 | writel((frame_phys_addr | (frame_count<<1))|1, | 334 | writel((frame_phys_addr | (frame_count<<1))|1, |
329 | &(regs)->inbound_queue_port); | 335 | &(regs)->inbound_queue_port); |
@@ -413,12 +419,17 @@ megasas_clear_intr_skinny(struct megasas_register_set __iomem *regs) | |||
413 | * @regs : MFI register set | 419 | * @regs : MFI register set |
414 | */ | 420 | */ |
415 | static inline void | 421 | static inline void |
416 | megasas_fire_cmd_skinny(dma_addr_t frame_phys_addr, u32 frame_count, | 422 | megasas_fire_cmd_skinny(struct megasas_instance *instance, |
423 | dma_addr_t frame_phys_addr, | ||
424 | u32 frame_count, | ||
417 | struct megasas_register_set __iomem *regs) | 425 | struct megasas_register_set __iomem *regs) |
418 | { | 426 | { |
427 | unsigned long flags; | ||
428 | spin_lock_irqsave(&instance->fire_lock, flags); | ||
419 | writel(0, &(regs)->inbound_high_queue_port); | 429 | writel(0, &(regs)->inbound_high_queue_port); |
420 | writel((frame_phys_addr | (frame_count<<1))|1, | 430 | writel((frame_phys_addr | (frame_count<<1))|1, |
421 | &(regs)->inbound_low_queue_port); | 431 | &(regs)->inbound_low_queue_port); |
432 | spin_unlock_irqrestore(&instance->fire_lock, flags); | ||
422 | } | 433 | } |
423 | 434 | ||
424 | static struct megasas_instance_template megasas_instance_template_skinny = { | 435 | static struct megasas_instance_template megasas_instance_template_skinny = { |
@@ -508,7 +519,9 @@ megasas_clear_intr_gen2(struct megasas_register_set __iomem *regs) | |||
508 | * @regs : MFI register set | 519 | * @regs : MFI register set |
509 | */ | 520 | */ |
510 | static inline void | 521 | static inline void |
511 | megasas_fire_cmd_gen2(dma_addr_t frame_phys_addr, u32 frame_count, | 522 | megasas_fire_cmd_gen2(struct megasas_instance *instance, |
523 | dma_addr_t frame_phys_addr, | ||
524 | u32 frame_count, | ||
512 | struct megasas_register_set __iomem *regs) | 525 | struct megasas_register_set __iomem *regs) |
513 | { | 526 | { |
514 | writel((frame_phys_addr | (frame_count<<1))|1, | 527 | writel((frame_phys_addr | (frame_count<<1))|1, |
@@ -550,7 +563,8 @@ megasas_issue_polled(struct megasas_instance *instance, struct megasas_cmd *cmd) | |||
550 | /* | 563 | /* |
551 | * Issue the frame using inbound queue port | 564 | * Issue the frame using inbound queue port |
552 | */ | 565 | */ |
553 | instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set); | 566 | instance->instancet->fire_cmd(instance, |
567 | cmd->frame_phys_addr, 0, instance->reg_set); | ||
554 | 568 | ||
555 | /* | 569 | /* |
556 | * Wait for cmd_status to change | 570 | * Wait for cmd_status to change |
@@ -581,7 +595,8 @@ megasas_issue_blocked_cmd(struct megasas_instance *instance, | |||
581 | { | 595 | { |
582 | cmd->cmd_status = ENODATA; | 596 | cmd->cmd_status = ENODATA; |
583 | 597 | ||
584 | instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set); | 598 | instance->instancet->fire_cmd(instance, |
599 | cmd->frame_phys_addr, 0, instance->reg_set); | ||
585 | 600 | ||
586 | wait_event_timeout(instance->int_cmd_wait_q, (cmd->cmd_status != ENODATA), | 601 | wait_event_timeout(instance->int_cmd_wait_q, (cmd->cmd_status != ENODATA), |
587 | MEGASAS_INTERNAL_CMD_WAIT_TIME*HZ); | 602 | MEGASAS_INTERNAL_CMD_WAIT_TIME*HZ); |
@@ -626,7 +641,8 @@ megasas_issue_blocked_abort_cmd(struct megasas_instance *instance, | |||
626 | cmd->sync_cmd = 1; | 641 | cmd->sync_cmd = 1; |
627 | cmd->cmd_status = 0xFF; | 642 | cmd->cmd_status = 0xFF; |
628 | 643 | ||
629 | instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set); | 644 | instance->instancet->fire_cmd(instance, |
645 | cmd->frame_phys_addr, 0, instance->reg_set); | ||
630 | 646 | ||
631 | /* | 647 | /* |
632 | * Wait for this cmd to complete | 648 | * Wait for this cmd to complete |
@@ -1153,7 +1169,8 @@ megasas_queue_command(struct scsi_cmnd *scmd, void (*done) (struct scsi_cmnd *)) | |||
1153 | */ | 1169 | */ |
1154 | atomic_inc(&instance->fw_outstanding); | 1170 | atomic_inc(&instance->fw_outstanding); |
1155 | 1171 | ||
1156 | instance->instancet->fire_cmd(cmd->frame_phys_addr ,cmd->frame_count-1,instance->reg_set); | 1172 | instance->instancet->fire_cmd(instance, cmd->frame_phys_addr, |
1173 | cmd->frame_count-1, instance->reg_set); | ||
1157 | /* | 1174 | /* |
1158 | * Check if we have pend cmds to be completed | 1175 | * Check if we have pend cmds to be completed |
1159 | */ | 1176 | */ |
@@ -1346,8 +1363,16 @@ static int megasas_wait_for_outstanding(struct megasas_instance *instance) | |||
1346 | * Send signal to FW to stop processing any pending cmds. | 1363 | * Send signal to FW to stop processing any pending cmds. |
1347 | * The controller will be taken offline by the OS now. | 1364 | * The controller will be taken offline by the OS now. |
1348 | */ | 1365 | */ |
1349 | writel(MFI_STOP_ADP, | 1366 | if ((instance->pdev->device == |
1367 | PCI_DEVICE_ID_LSI_SAS0073SKINNY) || | ||
1368 | (instance->pdev->device == | ||
1369 | PCI_DEVICE_ID_LSI_SAS0071SKINNY)) { | ||
1370 | writel(MFI_STOP_ADP, | ||
1371 | &instance->reg_set->reserved_0[0]); | ||
1372 | } else { | ||
1373 | writel(MFI_STOP_ADP, | ||
1350 | &instance->reg_set->inbound_doorbell); | 1374 | &instance->reg_set->inbound_doorbell); |
1375 | } | ||
1351 | megasas_dump_pending_frames(instance); | 1376 | megasas_dump_pending_frames(instance); |
1352 | instance->hw_crit_error = 1; | 1377 | instance->hw_crit_error = 1; |
1353 | return FAILED; | 1378 | return FAILED; |
@@ -1799,7 +1824,7 @@ megasas_transition_to_ready(struct megasas_instance* instance) | |||
1799 | /* | 1824 | /* |
1800 | * Set the CLR bit in inbound doorbell | 1825 | * Set the CLR bit in inbound doorbell |
1801 | */ | 1826 | */ |
1802 | if ((instance->pdev->device == \ | 1827 | if ((instance->pdev->device == |
1803 | PCI_DEVICE_ID_LSI_SAS0073SKINNY) || | 1828 | PCI_DEVICE_ID_LSI_SAS0073SKINNY) || |
1804 | (instance->pdev->device == | 1829 | (instance->pdev->device == |
1805 | PCI_DEVICE_ID_LSI_SAS0071SKINNY)) { | 1830 | PCI_DEVICE_ID_LSI_SAS0071SKINNY)) { |
@@ -2799,7 +2824,8 @@ megasas_register_aen(struct megasas_instance *instance, u32 seq_num, | |||
2799 | /* | 2824 | /* |
2800 | * Issue the aen registration frame | 2825 | * Issue the aen registration frame |
2801 | */ | 2826 | */ |
2802 | instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set); | 2827 | instance->instancet->fire_cmd(instance, |
2828 | cmd->frame_phys_addr, 0, instance->reg_set); | ||
2803 | 2829 | ||
2804 | return 0; | 2830 | return 0; |
2805 | } | 2831 | } |
@@ -2983,6 +3009,7 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) | |||
2983 | init_waitqueue_head(&instance->abort_cmd_wait_q); | 3009 | init_waitqueue_head(&instance->abort_cmd_wait_q); |
2984 | 3010 | ||
2985 | spin_lock_init(&instance->cmd_pool_lock); | 3011 | spin_lock_init(&instance->cmd_pool_lock); |
3012 | spin_lock_init(&instance->fire_lock); | ||
2986 | spin_lock_init(&instance->completion_lock); | 3013 | spin_lock_init(&instance->completion_lock); |
2987 | spin_lock_init(&poll_aen_lock); | 3014 | spin_lock_init(&poll_aen_lock); |
2988 | 3015 | ||
@@ -3005,7 +3032,7 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) | |||
3005 | 3032 | ||
3006 | megasas_dbg_lvl = 0; | 3033 | megasas_dbg_lvl = 0; |
3007 | instance->flag = 0; | 3034 | instance->flag = 0; |
3008 | instance->unload = 0; | 3035 | instance->unload = 1; |
3009 | instance->last_time = 0; | 3036 | instance->last_time = 0; |
3010 | 3037 | ||
3011 | /* | 3038 | /* |
@@ -3051,6 +3078,7 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) | |||
3051 | if (megasas_io_attach(instance)) | 3078 | if (megasas_io_attach(instance)) |
3052 | goto fail_io_attach; | 3079 | goto fail_io_attach; |
3053 | 3080 | ||
3081 | instance->unload = 0; | ||
3054 | return 0; | 3082 | return 0; |
3055 | 3083 | ||
3056 | fail_start_aen: | 3084 | fail_start_aen: |
@@ -3174,6 +3202,7 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state) | |||
3174 | 3202 | ||
3175 | instance = pci_get_drvdata(pdev); | 3203 | instance = pci_get_drvdata(pdev); |
3176 | host = instance->host; | 3204 | host = instance->host; |
3205 | instance->unload = 1; | ||
3177 | 3206 | ||
3178 | if (poll_mode_io) | 3207 | if (poll_mode_io) |
3179 | del_timer_sync(&instance->io_completion_timer); | 3208 | del_timer_sync(&instance->io_completion_timer); |
@@ -3269,6 +3298,8 @@ megasas_resume(struct pci_dev *pdev) | |||
3269 | megasas_start_timer(instance, &instance->io_completion_timer, | 3298 | megasas_start_timer(instance, &instance->io_completion_timer, |
3270 | megasas_io_completion_timer, | 3299 | megasas_io_completion_timer, |
3271 | MEGASAS_COMPLETION_TIMER_INTERVAL); | 3300 | MEGASAS_COMPLETION_TIMER_INTERVAL); |
3301 | instance->unload = 0; | ||
3302 | |||
3272 | return 0; | 3303 | return 0; |
3273 | 3304 | ||
3274 | fail_irq: | 3305 | fail_irq: |
@@ -3366,6 +3397,7 @@ static void __devexit megasas_detach_one(struct pci_dev *pdev) | |||
3366 | static void megasas_shutdown(struct pci_dev *pdev) | 3397 | static void megasas_shutdown(struct pci_dev *pdev) |
3367 | { | 3398 | { |
3368 | struct megasas_instance *instance = pci_get_drvdata(pdev); | 3399 | struct megasas_instance *instance = pci_get_drvdata(pdev); |
3400 | instance->unload = 1; | ||
3369 | megasas_flush_cache(instance); | 3401 | megasas_flush_cache(instance); |
3370 | megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN); | 3402 | megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN); |
3371 | } | 3403 | } |
@@ -3615,6 +3647,17 @@ static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg) | |||
3615 | goto out_kfree_ioc; | 3647 | goto out_kfree_ioc; |
3616 | } | 3648 | } |
3617 | 3649 | ||
3650 | if (instance->hw_crit_error == 1) { | ||
3651 | printk(KERN_DEBUG "Controller in Crit ERROR\n"); | ||
3652 | error = -ENODEV; | ||
3653 | goto out_kfree_ioc; | ||
3654 | } | ||
3655 | |||
3656 | if (instance->unload == 1) { | ||
3657 | error = -ENODEV; | ||
3658 | goto out_kfree_ioc; | ||
3659 | } | ||
3660 | |||
3618 | /* | 3661 | /* |
3619 | * We will allow only MEGASAS_INT_CMDS number of parallel ioctl cmds | 3662 | * We will allow only MEGASAS_INT_CMDS number of parallel ioctl cmds |
3620 | */ | 3663 | */ |
@@ -3650,6 +3693,14 @@ static int megasas_mgmt_ioctl_aen(struct file *file, unsigned long arg) | |||
3650 | if (!instance) | 3693 | if (!instance) |
3651 | return -ENODEV; | 3694 | return -ENODEV; |
3652 | 3695 | ||
3696 | if (instance->hw_crit_error == 1) { | ||
3697 | error = -ENODEV; | ||
3698 | } | ||
3699 | |||
3700 | if (instance->unload == 1) { | ||
3701 | return -ENODEV; | ||
3702 | } | ||
3703 | |||
3653 | mutex_lock(&instance->aen_mutex); | 3704 | mutex_lock(&instance->aen_mutex); |
3654 | error = megasas_register_aen(instance, aen.seq_num, | 3705 | error = megasas_register_aen(instance, aen.seq_num, |
3655 | aen.class_locale_word); | 3706 | aen.class_locale_word); |
diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index a1fd44bc1817..13ac37e80075 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h | |||
@@ -1157,17 +1157,6 @@ struct megasas_evt_detail { | |||
1157 | 1157 | ||
1158 | } __attribute__ ((packed)); | 1158 | } __attribute__ ((packed)); |
1159 | 1159 | ||
1160 | struct megasas_instance_template { | ||
1161 | void (*fire_cmd)(dma_addr_t ,u32 ,struct megasas_register_set __iomem *); | ||
1162 | |||
1163 | void (*enable_intr)(struct megasas_register_set __iomem *) ; | ||
1164 | void (*disable_intr)(struct megasas_register_set __iomem *); | ||
1165 | |||
1166 | int (*clear_intr)(struct megasas_register_set __iomem *); | ||
1167 | |||
1168 | u32 (*read_fw_status_reg)(struct megasas_register_set __iomem *); | ||
1169 | }; | ||
1170 | |||
1171 | struct megasas_instance { | 1160 | struct megasas_instance { |
1172 | 1161 | ||
1173 | u32 *producer; | 1162 | u32 *producer; |
@@ -1193,6 +1182,8 @@ struct megasas_instance { | |||
1193 | spinlock_t cmd_pool_lock; | 1182 | spinlock_t cmd_pool_lock; |
1194 | /* used to synch producer, consumer ptrs in dpc */ | 1183 | /* used to synch producer, consumer ptrs in dpc */ |
1195 | spinlock_t completion_lock; | 1184 | spinlock_t completion_lock; |
1185 | /* used to sync fire the cmd to fw */ | ||
1186 | spinlock_t fire_lock; | ||
1196 | struct dma_pool *frame_dma_pool; | 1187 | struct dma_pool *frame_dma_pool; |
1197 | struct dma_pool *sense_dma_pool; | 1188 | struct dma_pool *sense_dma_pool; |
1198 | 1189 | ||
@@ -1224,6 +1215,18 @@ struct megasas_instance { | |||
1224 | struct timer_list io_completion_timer; | 1215 | struct timer_list io_completion_timer; |
1225 | }; | 1216 | }; |
1226 | 1217 | ||
1218 | struct megasas_instance_template { | ||
1219 | void (*fire_cmd)(struct megasas_instance *, dma_addr_t, \ | ||
1220 | u32, struct megasas_register_set __iomem *); | ||
1221 | |||
1222 | void (*enable_intr)(struct megasas_register_set __iomem *) ; | ||
1223 | void (*disable_intr)(struct megasas_register_set __iomem *); | ||
1224 | |||
1225 | int (*clear_intr)(struct megasas_register_set __iomem *); | ||
1226 | |||
1227 | u32 (*read_fw_status_reg)(struct megasas_register_set __iomem *); | ||
1228 | }; | ||
1229 | |||
1227 | #define MEGASAS_IS_LOGICAL(scp) \ | 1230 | #define MEGASAS_IS_LOGICAL(scp) \ |
1228 | (scp->device->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1 | 1231 | (scp->device->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1 |
1229 | 1232 | ||