Commit 0c79e681 authored by Yang, Bo's avatar Yang, Bo Committed by James Bottomley

[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: default avatarJames Bottomley <James.Bottomley@suse.de>
parent f4c9a131
...@@ -226,7 +226,10 @@ megasas_clear_intr_xscale(struct megasas_register_set __iomem * regs) ...@@ -226,7 +226,10 @@ megasas_clear_intr_xscale(struct megasas_register_set __iomem * regs)
* @regs : MFI register set * @regs : MFI register set
*/ */
static inline void static inline void
megasas_fire_cmd_xscale(dma_addr_t frame_phys_addr,u32 frame_count, struct megasas_register_set __iomem *regs) megasas_fire_cmd_xscale(struct megasas_instance *instance,
dma_addr_t frame_phys_addr,
u32 frame_count,
struct megasas_register_set __iomem *regs)
{ {
writel((frame_phys_addr >> 3)|(frame_count), writel((frame_phys_addr >> 3)|(frame_count),
&(regs)->inbound_queue_port); &(regs)->inbound_queue_port);
...@@ -323,7 +326,10 @@ megasas_clear_intr_ppc(struct megasas_register_set __iomem * regs) ...@@ -323,7 +326,10 @@ megasas_clear_intr_ppc(struct megasas_register_set __iomem * regs)
* @regs : MFI register set * @regs : MFI register set
*/ */
static inline void static inline void
megasas_fire_cmd_ppc(dma_addr_t frame_phys_addr, u32 frame_count, struct megasas_register_set __iomem *regs) megasas_fire_cmd_ppc(struct megasas_instance *instance,
dma_addr_t frame_phys_addr,
u32 frame_count,
struct megasas_register_set __iomem *regs)
{ {
writel((frame_phys_addr | (frame_count<<1))|1, writel((frame_phys_addr | (frame_count<<1))|1,
&(regs)->inbound_queue_port); &(regs)->inbound_queue_port);
...@@ -413,12 +419,17 @@ megasas_clear_intr_skinny(struct megasas_register_set __iomem *regs) ...@@ -413,12 +419,17 @@ megasas_clear_intr_skinny(struct megasas_register_set __iomem *regs)
* @regs : MFI register set * @regs : MFI register set
*/ */
static inline void static inline void
megasas_fire_cmd_skinny(dma_addr_t frame_phys_addr, u32 frame_count, megasas_fire_cmd_skinny(struct megasas_instance *instance,
dma_addr_t frame_phys_addr,
u32 frame_count,
struct megasas_register_set __iomem *regs) struct megasas_register_set __iomem *regs)
{ {
unsigned long flags;
spin_lock_irqsave(&instance->fire_lock, flags);
writel(0, &(regs)->inbound_high_queue_port); writel(0, &(regs)->inbound_high_queue_port);
writel((frame_phys_addr | (frame_count<<1))|1, writel((frame_phys_addr | (frame_count<<1))|1,
&(regs)->inbound_low_queue_port); &(regs)->inbound_low_queue_port);
spin_unlock_irqrestore(&instance->fire_lock, flags);
} }
static struct megasas_instance_template megasas_instance_template_skinny = { static struct megasas_instance_template megasas_instance_template_skinny = {
...@@ -508,7 +519,9 @@ megasas_clear_intr_gen2(struct megasas_register_set __iomem *regs) ...@@ -508,7 +519,9 @@ megasas_clear_intr_gen2(struct megasas_register_set __iomem *regs)
* @regs : MFI register set * @regs : MFI register set
*/ */
static inline void static inline void
megasas_fire_cmd_gen2(dma_addr_t frame_phys_addr, u32 frame_count, megasas_fire_cmd_gen2(struct megasas_instance *instance,
dma_addr_t frame_phys_addr,
u32 frame_count,
struct megasas_register_set __iomem *regs) struct megasas_register_set __iomem *regs)
{ {
writel((frame_phys_addr | (frame_count<<1))|1, writel((frame_phys_addr | (frame_count<<1))|1,
...@@ -550,7 +563,8 @@ megasas_issue_polled(struct megasas_instance *instance, struct megasas_cmd *cmd) ...@@ -550,7 +563,8 @@ megasas_issue_polled(struct megasas_instance *instance, struct megasas_cmd *cmd)
/* /*
* Issue the frame using inbound queue port * Issue the frame using inbound queue port
*/ */
instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set); instance->instancet->fire_cmd(instance,
cmd->frame_phys_addr, 0, instance->reg_set);
/* /*
* Wait for cmd_status to change * Wait for cmd_status to change
...@@ -581,7 +595,8 @@ megasas_issue_blocked_cmd(struct megasas_instance *instance, ...@@ -581,7 +595,8 @@ megasas_issue_blocked_cmd(struct megasas_instance *instance,
{ {
cmd->cmd_status = ENODATA; cmd->cmd_status = ENODATA;
instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set); instance->instancet->fire_cmd(instance,
cmd->frame_phys_addr, 0, instance->reg_set);
wait_event_timeout(instance->int_cmd_wait_q, (cmd->cmd_status != ENODATA), wait_event_timeout(instance->int_cmd_wait_q, (cmd->cmd_status != ENODATA),
MEGASAS_INTERNAL_CMD_WAIT_TIME*HZ); MEGASAS_INTERNAL_CMD_WAIT_TIME*HZ);
...@@ -626,7 +641,8 @@ megasas_issue_blocked_abort_cmd(struct megasas_instance *instance, ...@@ -626,7 +641,8 @@ megasas_issue_blocked_abort_cmd(struct megasas_instance *instance,
cmd->sync_cmd = 1; cmd->sync_cmd = 1;
cmd->cmd_status = 0xFF; cmd->cmd_status = 0xFF;
instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set); instance->instancet->fire_cmd(instance,
cmd->frame_phys_addr, 0, instance->reg_set);
/* /*
* Wait for this cmd to complete * Wait for this cmd to complete
...@@ -1153,7 +1169,8 @@ megasas_queue_command(struct scsi_cmnd *scmd, void (*done) (struct scsi_cmnd *)) ...@@ -1153,7 +1169,8 @@ megasas_queue_command(struct scsi_cmnd *scmd, void (*done) (struct scsi_cmnd *))
*/ */
atomic_inc(&instance->fw_outstanding); atomic_inc(&instance->fw_outstanding);
instance->instancet->fire_cmd(cmd->frame_phys_addr ,cmd->frame_count-1,instance->reg_set); instance->instancet->fire_cmd(instance, cmd->frame_phys_addr,
cmd->frame_count-1, instance->reg_set);
/* /*
* Check if we have pend cmds to be completed * Check if we have pend cmds to be completed
*/ */
...@@ -1346,8 +1363,16 @@ static int megasas_wait_for_outstanding(struct megasas_instance *instance) ...@@ -1346,8 +1363,16 @@ static int megasas_wait_for_outstanding(struct megasas_instance *instance)
* Send signal to FW to stop processing any pending cmds. * Send signal to FW to stop processing any pending cmds.
* The controller will be taken offline by the OS now. * The controller will be taken offline by the OS now.
*/ */
writel(MFI_STOP_ADP, if ((instance->pdev->device ==
PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
(instance->pdev->device ==
PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
writel(MFI_STOP_ADP,
&instance->reg_set->reserved_0[0]);
} else {
writel(MFI_STOP_ADP,
&instance->reg_set->inbound_doorbell); &instance->reg_set->inbound_doorbell);
}
megasas_dump_pending_frames(instance); megasas_dump_pending_frames(instance);
instance->hw_crit_error = 1; instance->hw_crit_error = 1;
return FAILED; return FAILED;
...@@ -1799,7 +1824,7 @@ megasas_transition_to_ready(struct megasas_instance* instance) ...@@ -1799,7 +1824,7 @@ megasas_transition_to_ready(struct megasas_instance* instance)
/* /*
* Set the CLR bit in inbound doorbell * Set the CLR bit in inbound doorbell
*/ */
if ((instance->pdev->device == \ if ((instance->pdev->device ==
PCI_DEVICE_ID_LSI_SAS0073SKINNY) || PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
(instance->pdev->device == (instance->pdev->device ==
PCI_DEVICE_ID_LSI_SAS0071SKINNY)) { PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
...@@ -2799,7 +2824,8 @@ megasas_register_aen(struct megasas_instance *instance, u32 seq_num, ...@@ -2799,7 +2824,8 @@ megasas_register_aen(struct megasas_instance *instance, u32 seq_num,
/* /*
* Issue the aen registration frame * Issue the aen registration frame
*/ */
instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set); instance->instancet->fire_cmd(instance,
cmd->frame_phys_addr, 0, instance->reg_set);
return 0; return 0;
} }
...@@ -2983,6 +3009,7 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -2983,6 +3009,7 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
init_waitqueue_head(&instance->abort_cmd_wait_q); init_waitqueue_head(&instance->abort_cmd_wait_q);
spin_lock_init(&instance->cmd_pool_lock); spin_lock_init(&instance->cmd_pool_lock);
spin_lock_init(&instance->fire_lock);
spin_lock_init(&instance->completion_lock); spin_lock_init(&instance->completion_lock);
spin_lock_init(&poll_aen_lock); spin_lock_init(&poll_aen_lock);
...@@ -3005,7 +3032,7 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -3005,7 +3032,7 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
megasas_dbg_lvl = 0; megasas_dbg_lvl = 0;
instance->flag = 0; instance->flag = 0;
instance->unload = 0; instance->unload = 1;
instance->last_time = 0; instance->last_time = 0;
/* /*
...@@ -3051,6 +3078,7 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -3051,6 +3078,7 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
if (megasas_io_attach(instance)) if (megasas_io_attach(instance))
goto fail_io_attach; goto fail_io_attach;
instance->unload = 0;
return 0; return 0;
fail_start_aen: fail_start_aen:
...@@ -3174,6 +3202,7 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state) ...@@ -3174,6 +3202,7 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state)
instance = pci_get_drvdata(pdev); instance = pci_get_drvdata(pdev);
host = instance->host; host = instance->host;
instance->unload = 1;
if (poll_mode_io) if (poll_mode_io)
del_timer_sync(&instance->io_completion_timer); del_timer_sync(&instance->io_completion_timer);
...@@ -3269,6 +3298,8 @@ megasas_resume(struct pci_dev *pdev) ...@@ -3269,6 +3298,8 @@ megasas_resume(struct pci_dev *pdev)
megasas_start_timer(instance, &instance->io_completion_timer, megasas_start_timer(instance, &instance->io_completion_timer,
megasas_io_completion_timer, megasas_io_completion_timer,
MEGASAS_COMPLETION_TIMER_INTERVAL); MEGASAS_COMPLETION_TIMER_INTERVAL);
instance->unload = 0;
return 0; return 0;
fail_irq: fail_irq:
...@@ -3366,6 +3397,7 @@ static void __devexit megasas_detach_one(struct pci_dev *pdev) ...@@ -3366,6 +3397,7 @@ static void __devexit megasas_detach_one(struct pci_dev *pdev)
static void megasas_shutdown(struct pci_dev *pdev) static void megasas_shutdown(struct pci_dev *pdev)
{ {
struct megasas_instance *instance = pci_get_drvdata(pdev); struct megasas_instance *instance = pci_get_drvdata(pdev);
instance->unload = 1;
megasas_flush_cache(instance); megasas_flush_cache(instance);
megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN); megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN);
} }
...@@ -3615,6 +3647,17 @@ static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg) ...@@ -3615,6 +3647,17 @@ static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg)
goto out_kfree_ioc; goto out_kfree_ioc;
} }
if (instance->hw_crit_error == 1) {
printk(KERN_DEBUG "Controller in Crit ERROR\n");
error = -ENODEV;
goto out_kfree_ioc;
}
if (instance->unload == 1) {
error = -ENODEV;
goto out_kfree_ioc;
}
/* /*
* We will allow only MEGASAS_INT_CMDS number of parallel ioctl cmds * We will allow only MEGASAS_INT_CMDS number of parallel ioctl cmds
*/ */
...@@ -3650,6 +3693,14 @@ static int megasas_mgmt_ioctl_aen(struct file *file, unsigned long arg) ...@@ -3650,6 +3693,14 @@ static int megasas_mgmt_ioctl_aen(struct file *file, unsigned long arg)
if (!instance) if (!instance)
return -ENODEV; return -ENODEV;
if (instance->hw_crit_error == 1) {
error = -ENODEV;
}
if (instance->unload == 1) {
return -ENODEV;
}
mutex_lock(&instance->aen_mutex); mutex_lock(&instance->aen_mutex);
error = megasas_register_aen(instance, aen.seq_num, error = megasas_register_aen(instance, aen.seq_num,
aen.class_locale_word); aen.class_locale_word);
......
...@@ -1157,17 +1157,6 @@ struct megasas_evt_detail { ...@@ -1157,17 +1157,6 @@ struct megasas_evt_detail {
} __attribute__ ((packed)); } __attribute__ ((packed));
struct megasas_instance_template {
void (*fire_cmd)(dma_addr_t ,u32 ,struct megasas_register_set __iomem *);
void (*enable_intr)(struct megasas_register_set __iomem *) ;
void (*disable_intr)(struct megasas_register_set __iomem *);
int (*clear_intr)(struct megasas_register_set __iomem *);
u32 (*read_fw_status_reg)(struct megasas_register_set __iomem *);
};
struct megasas_instance { struct megasas_instance {
u32 *producer; u32 *producer;
...@@ -1193,6 +1182,8 @@ struct megasas_instance { ...@@ -1193,6 +1182,8 @@ struct megasas_instance {
spinlock_t cmd_pool_lock; spinlock_t cmd_pool_lock;
/* used to synch producer, consumer ptrs in dpc */ /* used to synch producer, consumer ptrs in dpc */
spinlock_t completion_lock; spinlock_t completion_lock;
/* used to sync fire the cmd to fw */
spinlock_t fire_lock;
struct dma_pool *frame_dma_pool; struct dma_pool *frame_dma_pool;
struct dma_pool *sense_dma_pool; struct dma_pool *sense_dma_pool;
...@@ -1224,6 +1215,18 @@ struct megasas_instance { ...@@ -1224,6 +1215,18 @@ struct megasas_instance {
struct timer_list io_completion_timer; struct timer_list io_completion_timer;
}; };
struct megasas_instance_template {
void (*fire_cmd)(struct megasas_instance *, dma_addr_t, \
u32, struct megasas_register_set __iomem *);
void (*enable_intr)(struct megasas_register_set __iomem *) ;
void (*disable_intr)(struct megasas_register_set __iomem *);
int (*clear_intr)(struct megasas_register_set __iomem *);
u32 (*read_fw_status_reg)(struct megasas_register_set __iomem *);
};
#define MEGASAS_IS_LOGICAL(scp) \ #define MEGASAS_IS_LOGICAL(scp) \
(scp->device->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1 (scp->device->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1
......
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