Skip to content

Commit 95fecd9

Browse files
Wayne BoyerJames Bottomley
authored andcommitted
ipr: add test for MSI interrupt support
The return value from pci_enable_msi() can not always be trusted. This patch adds code to generate an interrupt after MSI has been enabled and tests whether or not we can receive and process it. If the tests fails, then fall back to LSI. Signed-off-by: Wayne Boyer <[email protected]> Acked-by: Brian King <[email protected]> Signed-off-by: James Bottomley <[email protected]>
1 parent a9e0edb commit 95fecd9

File tree

2 files changed

+105
-9
lines changed

2 files changed

+105
-9
lines changed

drivers/scsi/ipr.c

Lines changed: 101 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7367,6 +7367,7 @@ static void __devinit ipr_init_ioa_cfg(struct ipr_ioa_cfg *ioa_cfg,
73677367
INIT_LIST_HEAD(&ioa_cfg->used_res_q);
73687368
INIT_WORK(&ioa_cfg->work_q, ipr_worker_thread);
73697369
init_waitqueue_head(&ioa_cfg->reset_wait_q);
7370+
init_waitqueue_head(&ioa_cfg->msi_wait_q);
73707371
ioa_cfg->sdt_state = INACTIVE;
73717372
if (ipr_enable_cache)
73727373
ioa_cfg->cache_state = CACHE_ENABLED;
@@ -7416,6 +7417,89 @@ ipr_get_chip_cfg(const struct pci_device_id *dev_id)
74167417
return NULL;
74177418
}
74187419

7420+
/**
7421+
* ipr_test_intr - Handle the interrupt generated in ipr_test_msi().
7422+
* @pdev: PCI device struct
7423+
*
7424+
* Description: Simply set the msi_received flag to 1 indicating that
7425+
* Message Signaled Interrupts are supported.
7426+
*
7427+
* Return value:
7428+
* 0 on success / non-zero on failure
7429+
**/
7430+
static irqreturn_t __devinit ipr_test_intr(int irq, void *devp)
7431+
{
7432+
struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)devp;
7433+
unsigned long lock_flags = 0;
7434+
irqreturn_t rc = IRQ_HANDLED;
7435+
7436+
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
7437+
7438+
ioa_cfg->msi_received = 1;
7439+
wake_up(&ioa_cfg->msi_wait_q);
7440+
7441+
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
7442+
return rc;
7443+
}
7444+
7445+
/**
7446+
* ipr_test_msi - Test for Message Signaled Interrupt (MSI) support.
7447+
* @pdev: PCI device struct
7448+
*
7449+
* Description: The return value from pci_enable_msi() can not always be
7450+
* trusted. This routine sets up and initiates a test interrupt to determine
7451+
* if the interrupt is received via the ipr_test_intr() service routine.
7452+
* If the tests fails, the driver will fall back to LSI.
7453+
*
7454+
* Return value:
7455+
* 0 on success / non-zero on failure
7456+
**/
7457+
static int __devinit ipr_test_msi(struct ipr_ioa_cfg *ioa_cfg,
7458+
struct pci_dev *pdev)
7459+
{
7460+
int rc;
7461+
volatile u32 int_reg;
7462+
unsigned long lock_flags = 0;
7463+
7464+
ENTER;
7465+
7466+
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
7467+
init_waitqueue_head(&ioa_cfg->msi_wait_q);
7468+
ioa_cfg->msi_received = 0;
7469+
ipr_mask_and_clear_interrupts(ioa_cfg, ~IPR_PCII_IOA_TRANS_TO_OPER);
7470+
writel(IPR_PCII_IO_DEBUG_ACKNOWLEDGE, ioa_cfg->regs.clr_interrupt_mask_reg);
7471+
int_reg = readl(ioa_cfg->regs.sense_interrupt_mask_reg);
7472+
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
7473+
7474+
rc = request_irq(pdev->irq, ipr_test_intr, 0, IPR_NAME, ioa_cfg);
7475+
if (rc) {
7476+
dev_err(&pdev->dev, "Can not assign irq %d\n", pdev->irq);
7477+
return rc;
7478+
} else if (ipr_debug)
7479+
dev_info(&pdev->dev, "IRQ assigned: %d\n", pdev->irq);
7480+
7481+
writel(IPR_PCII_IO_DEBUG_ACKNOWLEDGE, ioa_cfg->regs.sense_interrupt_reg);
7482+
int_reg = readl(ioa_cfg->regs.sense_interrupt_reg);
7483+
wait_event_timeout(ioa_cfg->msi_wait_q, ioa_cfg->msi_received, HZ);
7484+
ipr_mask_and_clear_interrupts(ioa_cfg, ~IPR_PCII_IOA_TRANS_TO_OPER);
7485+
7486+
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
7487+
if (!ioa_cfg->msi_received) {
7488+
/* MSI test failed */
7489+
dev_info(&pdev->dev, "MSI test failed. Falling back to LSI.\n");
7490+
rc = -EOPNOTSUPP;
7491+
} else if (ipr_debug)
7492+
dev_info(&pdev->dev, "MSI test succeeded.\n");
7493+
7494+
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
7495+
7496+
free_irq(pdev->irq, ioa_cfg);
7497+
7498+
LEAVE;
7499+
7500+
return rc;
7501+
}
7502+
74197503
/**
74207504
* ipr_probe_ioa - Allocates memory and does first stage of initialization
74217505
* @pdev: PCI device struct
@@ -7441,11 +7525,6 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev,
74417525
goto out;
74427526
}
74437527

7444-
if (!(rc = pci_enable_msi(pdev)))
7445-
dev_info(&pdev->dev, "MSI enabled\n");
7446-
else if (ipr_debug)
7447-
dev_info(&pdev->dev, "Cannot enable MSI\n");
7448-
74497528
dev_info(&pdev->dev, "Found IOA with IRQ: %d\n", pdev->irq);
74507529

74517530
host = scsi_host_alloc(&driver_template, sizeof(*ioa_cfg));
@@ -7519,6 +7598,18 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev,
75197598
goto cleanup_nomem;
75207599
}
75217600

7601+
/* Enable MSI style interrupts if they are supported. */
7602+
if (!(rc = pci_enable_msi(pdev))) {
7603+
rc = ipr_test_msi(ioa_cfg, pdev);
7604+
if (rc == -EOPNOTSUPP)
7605+
pci_disable_msi(pdev);
7606+
else if (rc)
7607+
goto out_msi_disable;
7608+
else
7609+
dev_info(&pdev->dev, "MSI enabled with IRQ: %d\n", pdev->irq);
7610+
} else if (ipr_debug)
7611+
dev_info(&pdev->dev, "Cannot enable MSI.\n");
7612+
75227613
/* Save away PCI config space for use following IOA reset */
75237614
rc = pci_save_state(pdev);
75247615

@@ -7556,7 +7647,9 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev,
75567647
ioa_cfg->ioa_unit_checked = 1;
75577648

75587649
ipr_mask_and_clear_interrupts(ioa_cfg, ~IPR_PCII_IOA_TRANS_TO_OPER);
7559-
rc = request_irq(pdev->irq, ipr_isr, IRQF_SHARED, IPR_NAME, ioa_cfg);
7650+
rc = request_irq(pdev->irq, ipr_isr,
7651+
ioa_cfg->msi_received ? 0 : IRQF_SHARED,
7652+
IPR_NAME, ioa_cfg);
75607653

75617654
if (rc) {
75627655
dev_err(&pdev->dev, "Couldn't register IRQ %d! rc=%d\n",
@@ -7583,12 +7676,13 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev,
75837676
ipr_free_mem(ioa_cfg);
75847677
cleanup_nomem:
75857678
iounmap(ipr_regs);
7679+
out_msi_disable:
7680+
pci_disable_msi(pdev);
75867681
out_release_regions:
75877682
pci_release_regions(pdev);
75887683
out_scsi_host_put:
75897684
scsi_host_put(host);
75907685
out_disable:
7591-
pci_disable_msi(pdev);
75927686
pci_disable_device(pdev);
75937687
goto out;
75947688
}

drivers/scsi/ipr.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@
3737
/*
3838
* Literals
3939
*/
40-
#define IPR_DRIVER_VERSION "2.4.2"
41-
#define IPR_DRIVER_DATE "(January 21, 2009)"
40+
#define IPR_DRIVER_VERSION "2.4.3"
41+
#define IPR_DRIVER_DATE "(June 10, 2009)"
4242

4343
/*
4444
* IPR_MAX_CMD_PER_LUN: This defines the maximum number of outstanding
@@ -1094,6 +1094,7 @@ struct ipr_ioa_cfg {
10941094
u8 needs_hard_reset:1;
10951095
u8 dual_raid:1;
10961096
u8 needs_warm_reset:1;
1097+
u8 msi_received:1;
10971098

10981099
u8 revid;
10991100

@@ -1179,6 +1180,7 @@ struct ipr_ioa_cfg {
11791180
struct work_struct work_q;
11801181

11811182
wait_queue_head_t reset_wait_q;
1183+
wait_queue_head_t msi_wait_q;
11821184

11831185
struct ipr_dump *dump;
11841186
enum ipr_sdt_state sdt_state;

0 commit comments

Comments
 (0)