Skip to content

Commit

Permalink
AHCI: Do not read HOST_IRQ_STAT reg in multi-MSI mode
Browse files Browse the repository at this point in the history
As described in AHCI v1.0 specification chapter 10.6.2.2
"Multiple MSI Based Messages" generation of interrupts
is not controlled through the HOST_IRQ_STAT register.

Considering MMIO access is expensive remove unnecessary
reading and writing of HOST_IRQ_STAT register.

Further, serializing access to the host data is no longer
needed and the interrupt service routine can avoid competing
on the host lock.

Signed-off-by: Alexander Gordeev <agordeev@redhat.com>
Suggested-by: "Jiang, Dave" <dave.jiang@intel.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: "Jiang, Dave" <dave.jiang@intel.com>
Cc: linux-ide@vger.kernel.org
  • Loading branch information
Alexander Gordeev authored and Tejun Heo committed Oct 6, 2014
1 parent 5ee1cfd commit 227dfb4
Show file tree
Hide file tree
Showing 2 changed files with 9 additions and 60 deletions.
2 changes: 1 addition & 1 deletion drivers/ata/ahci.h
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ struct ahci_port_priv {
unsigned int ncq_saw_d2h:1;
unsigned int ncq_saw_dmas:1;
unsigned int ncq_saw_sdb:1;
u32 intr_status; /* interrupts to handle */
atomic_t intr_status; /* interrupts to handle */
spinlock_t lock; /* protects parent ata_port */
u32 intr_mask; /* interrupts to enable */
bool fbs_supported; /* set iff FBS is supported */
Expand Down
67 changes: 8 additions & 59 deletions drivers/ata/libahci.c
Original file line number Diff line number Diff line change
Expand Up @@ -1794,14 +1794,11 @@ static irqreturn_t ahci_port_thread_fn(int irq, void *dev_instance)
struct ata_port *ap = dev_instance;
struct ahci_port_priv *pp = ap->private_data;
void __iomem *port_mmio = ahci_port_base(ap);
unsigned long flags;
u32 status;

spin_lock_irqsave(&ap->host->lock, flags);
status = pp->intr_status;
if (status)
pp->intr_status = 0;
spin_unlock_irqrestore(&ap->host->lock, flags);
status = atomic_xchg(&pp->intr_status, 0);
if (!status)
return IRQ_NONE;

spin_lock_bh(ap->lock);
ahci_handle_port_interrupt(ap, port_mmio, status);
Expand All @@ -1810,67 +1807,19 @@ static irqreturn_t ahci_port_thread_fn(int irq, void *dev_instance)
return IRQ_HANDLED;
}

static void ahci_update_intr_status(struct ata_port *ap)
static irqreturn_t ahci_multi_irqs_intr(int irq, void *dev_instance)
{
struct ata_port *ap = dev_instance;
void __iomem *port_mmio = ahci_port_base(ap);
struct ahci_port_priv *pp = ap->private_data;
u32 status;

status = readl(port_mmio + PORT_IRQ_STAT);
writel(status, port_mmio + PORT_IRQ_STAT);

pp->intr_status |= status;
}

static irqreturn_t ahci_multi_irqs_intr(int irq, void *dev_instance)
{
struct ata_port *ap_this = dev_instance;
struct ahci_port_priv *pp = ap_this->private_data;
struct ata_host *host = ap_this->host;
struct ahci_host_priv *hpriv = host->private_data;
void __iomem *mmio = hpriv->mmio;
unsigned int i;
u32 irq_stat, irq_masked;

VPRINTK("ENTER\n");

spin_lock(&host->lock);

irq_stat = readl(mmio + HOST_IRQ_STAT);

if (!irq_stat) {
u32 status = pp->intr_status;

spin_unlock(&host->lock);

VPRINTK("EXIT\n");

return status ? IRQ_WAKE_THREAD : IRQ_NONE;
}

irq_masked = irq_stat & hpriv->port_map;

for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap;

if (!(irq_masked & (1 << i)))
continue;

ap = host->ports[i];
if (ap) {
ahci_update_intr_status(ap);
VPRINTK("port %u\n", i);
} else {
VPRINTK("port %u (no irq)\n", i);
if (ata_ratelimit())
dev_warn(host->dev,
"interrupt on disabled port %u\n", i);
}
}

writel(irq_stat, mmio + HOST_IRQ_STAT);
status = readl(port_mmio + PORT_IRQ_STAT);
writel(status, port_mmio + PORT_IRQ_STAT);

spin_unlock(&host->lock);
atomic_or(status, &pp->intr_status);

VPRINTK("EXIT\n");

Expand Down

0 comments on commit 227dfb4

Please sign in to comment.