Discussion:
[PATCH v5 4/5] AHCI: Make few function names more descriptive
Alexander Gordeev
2014-09-29 16:26:00 UTC
Permalink
Signed-off-by: Alexander Gordeev <***@redhat.com>
Cc: linux-***@vger.kernel.org
---
drivers/ata/libahci.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index 0080551..48175e5 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -1789,7 +1789,7 @@ static void ahci_port_intr(struct ata_port *ap)
ahci_handle_port_interrupt(ap, port_mmio, status);
}

-static irqreturn_t ahci_thread_fn(int irq, void *dev_instance)
+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;
@@ -1810,7 +1810,7 @@ static irqreturn_t ahci_thread_fn(int irq, void *dev_instance)
return IRQ_HANDLED;
}

-static void ahci_hw_port_interrupt(struct ata_port *ap)
+static void ahci_update_intr_status(struct ata_port *ap)
{
void __iomem *port_mmio = ahci_port_base(ap);
struct ahci_port_priv *pp = ap->private_data;
@@ -1822,7 +1822,7 @@ static void ahci_hw_port_interrupt(struct ata_port *ap)
pp->intr_status |= status;
}

-static irqreturn_t ahci_hw_interrupt(int irq, void *dev_instance)
+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;
@@ -1858,7 +1858,7 @@ static irqreturn_t ahci_hw_interrupt(int irq, void *dev_instance)

ap = host->ports[i];
if (ap) {
- ahci_hw_port_interrupt(ap);
+ ahci_update_intr_status(ap);
VPRINTK("port %u\n", i);
} else {
VPRINTK("port %u (no irq)\n", i);
@@ -1877,7 +1877,7 @@ static irqreturn_t ahci_hw_interrupt(int irq, void *dev_instance)
return IRQ_WAKE_THREAD;
}

-static irqreturn_t ahci_interrupt(int irq, void *dev_instance)
+static irqreturn_t ahci_single_irq_intr(int irq, void *dev_instance)
{
struct ata_host *host = dev_instance;
struct ahci_host_priv *hpriv;
@@ -2488,8 +2488,8 @@ static int ahci_host_activate_multi_irqs(struct ata_host *host, int irq,
}

rc = devm_request_threaded_irq(host->dev, irq + i,
- ahci_hw_interrupt,
- ahci_thread_fn, IRQF_SHARED,
+ ahci_multi_irqs_intr,
+ ahci_port_thread_fn, IRQF_SHARED,
pp->irq_desc, host->ports[i]);
if (rc)
goto out_free_irqs;
@@ -2538,7 +2538,7 @@ int ahci_host_activate(struct ata_host *host, int irq,
if (hpriv->flags & AHCI_HFLAG_MULTI_MSI)
rc = ahci_host_activate_multi_irqs(host, irq, sht);
else
- rc = ata_host_activate(host, irq, ahci_interrupt,
+ rc = ata_host_activate(host, irq, ahci_single_irq_intr,
IRQF_SHARED, sht);
return rc;
}
--
1.8.3.1
Alexander Gordeev
2014-09-29 16:25:57 UTC
Permalink
This update is a prerequisite for consolidation of
AHCI host activation code within ahci_host_activate()
function.

Signed-off-by: Alexander Gordeev <***@redhat.com>
Cc: linux-***@vger.kernel.org
---
drivers/ata/ahci.c | 10 +++++-----
drivers/ata/ahci.h | 3 ++-
2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index f68a995..fcda5b6 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -1230,8 +1230,7 @@ intx:
* ahci_host_activate - start AHCI host, request IRQs and register it
* @host: target ATA host
* @irq: base IRQ number to request
- * @irq_handler: irq_handler used when requesting IRQs
- * @irq_flags: irq_flags used when requesting IRQs
+ * @sht: scsi_host_template to use when registering the host
*
* Similar to ata_host_activate, but requests IRQs according to AHCI-1.1
* when multiple MSIs were allocated. That is one MSI per port, starting
@@ -1243,7 +1242,8 @@ intx:
* RETURNS:
* 0 on success, -errno otherwise.
*/
-int ahci_host_activate(struct ata_host *host, int irq)
+int ahci_host_activate(struct ata_host *host, int irq,
+ struct scsi_host_template *sht)
{
int i, rc;

@@ -1271,7 +1271,7 @@ int ahci_host_activate(struct ata_host *host, int irq)
for (i = 0; i < host->n_ports; i++)
ata_port_desc(host->ports[i], "irq %d", irq + i);

- rc = ata_host_register(host, &ahci_sht);
+ rc = ata_host_register(host, sht);
if (rc)
goto out_free_all_irqs;

@@ -1488,7 +1488,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
pci_set_master(pdev);

if (hpriv->flags & AHCI_HFLAG_MULTI_MSI)
- return ahci_host_activate(host, pdev->irq);
+ return ahci_host_activate(host, pdev->irq, &ahci_sht);

return ata_host_activate(host, pdev->irq, ahci_interrupt, IRQF_SHARED,
&ahci_sht);
diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
index a074c73..31b4c44 100644
--- a/drivers/ata/ahci.h
+++ b/drivers/ata/ahci.h
@@ -392,7 +392,8 @@ irqreturn_t ahci_interrupt(int irq, void *dev_instance);
irqreturn_t ahci_hw_interrupt(int irq, void *dev_instance);
irqreturn_t ahci_thread_fn(int irq, void *dev_instance);
void ahci_print_info(struct ata_host *host, const char *scc_s);
-int ahci_host_activate(struct ata_host *host, int irq);
+int ahci_host_activate(struct ata_host *host, int irq,
+ struct scsi_host_template *sht);
void ahci_error_handler(struct ata_port *ap);

static inline void __iomem *__ahci_port_base(struct ata_host *host,
--
1.8.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-ide" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Alexander Gordeev
2014-09-29 16:25:59 UTC
Permalink
Currently host activation done by calling either function
ahci_host_activate() or ata_host_activate(). Consolidate
the code by only calling ahci_host_activate() for all AHCI
devices.

Signed-off-by: Alexander Gordeev <***@redhat.com>
Cc: linux-***@vger.kernel.org
---
drivers/ata/acard-ahci.c | 3 +--
drivers/ata/ahci.c | 6 +----
drivers/ata/ahci.h | 3 ---
drivers/ata/libahci.c | 59 +++++++++++++++++++++++++-----------------
drivers/ata/libahci_platform.c | 3 +--
drivers/ata/sata_highbank.c | 3 +--
6 files changed, 39 insertions(+), 38 deletions(-)

diff --git a/drivers/ata/acard-ahci.c b/drivers/ata/acard-ahci.c
index 25d0ac3..c962886 100644
--- a/drivers/ata/acard-ahci.c
+++ b/drivers/ata/acard-ahci.c
@@ -498,8 +498,7 @@ static int acard_ahci_init_one(struct pci_dev *pdev, const struct pci_device_id
acard_ahci_pci_print_info(host);

pci_set_master(pdev);
- return ata_host_activate(host, pdev->irq, ahci_interrupt, IRQF_SHARED,
- &acard_ahci_sht);
+ return ahci_host_activate(host, pdev->irq, &acard_ahci_sht);
}

module_pci_driver(acard_ahci_pci_driver);
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 0b21604..d0ac38b 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -1427,11 +1427,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)

pci_set_master(pdev);

- if (hpriv->flags & AHCI_HFLAG_MULTI_MSI)
- return ahci_host_activate(host, pdev->irq, &ahci_sht);
-
- return ata_host_activate(host, pdev->irq, ahci_interrupt, IRQF_SHARED,
- &ahci_sht);
+ return ahci_host_activate(host, pdev->irq, &ahci_sht);
}

module_pci_driver(ahci_pci_driver);
diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
index 31b4c44..6a22055 100644
--- a/drivers/ata/ahci.h
+++ b/drivers/ata/ahci.h
@@ -388,9 +388,6 @@ int ahci_port_resume(struct ata_port *ap);
void ahci_set_em_messages(struct ahci_host_priv *hpriv,
struct ata_port_info *pi);
int ahci_reset_em(struct ata_host *host);
-irqreturn_t ahci_interrupt(int irq, void *dev_instance);
-irqreturn_t ahci_hw_interrupt(int irq, void *dev_instance);
-irqreturn_t ahci_thread_fn(int irq, void *dev_instance);
void ahci_print_info(struct ata_host *host, const char *scc_s);
int ahci_host_activate(struct ata_host *host, int irq,
struct scsi_host_template *sht);
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index 21bb427..0080551 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -1789,7 +1789,7 @@ static void ahci_port_intr(struct ata_port *ap)
ahci_handle_port_interrupt(ap, port_mmio, status);
}

-irqreturn_t ahci_thread_fn(int irq, void *dev_instance)
+static irqreturn_t ahci_thread_fn(int irq, void *dev_instance)
{
struct ata_port *ap = dev_instance;
struct ahci_port_priv *pp = ap->private_data;
@@ -1809,7 +1809,6 @@ irqreturn_t ahci_thread_fn(int irq, void *dev_instance)

return IRQ_HANDLED;
}
-EXPORT_SYMBOL_GPL(ahci_thread_fn);

static void ahci_hw_port_interrupt(struct ata_port *ap)
{
@@ -1823,7 +1822,7 @@ static void ahci_hw_port_interrupt(struct ata_port *ap)
pp->intr_status |= status;
}

-irqreturn_t ahci_hw_interrupt(int irq, void *dev_instance)
+static irqreturn_t ahci_hw_interrupt(int irq, void *dev_instance)
{
struct ata_port *ap_this = dev_instance;
struct ahci_port_priv *pp = ap_this->private_data;
@@ -1877,9 +1876,8 @@ irqreturn_t ahci_hw_interrupt(int irq, void *dev_instance)

return IRQ_WAKE_THREAD;
}
-EXPORT_SYMBOL_GPL(ahci_hw_interrupt);

-irqreturn_t ahci_interrupt(int irq, void *dev_instance)
+static irqreturn_t ahci_interrupt(int irq, void *dev_instance)
{
struct ata_host *host = dev_instance;
struct ahci_host_priv *hpriv;
@@ -1938,7 +1936,6 @@ irqreturn_t ahci_interrupt(int irq, void *dev_instance)

return IRQ_RETVAL(handled);
}
-EXPORT_SYMBOL_GPL(ahci_interrupt);

unsigned int ahci_qc_issue(struct ata_queued_cmd *qc)
{
@@ -2472,24 +2469,8 @@ void ahci_set_em_messages(struct ahci_host_priv *hpriv,
}
EXPORT_SYMBOL_GPL(ahci_set_em_messages);

-/**
- * ahci_host_activate - start AHCI host, request IRQs and register it
- * @host: target ATA host
- * @irq: base IRQ number to request
- * @sht: scsi_host_template to use when registering the host
- *
- * Similar to ata_host_activate, but requests IRQs according to AHCI-1.1
- * when multiple MSIs were allocated. That is one MSI per port, starting
- * from @irq.
- *
- * LOCKING:
- * Inherited from calling layer (may sleep).
- *
- * RETURNS:
- * 0 on success, -errno otherwise.
- */
-int ahci_host_activate(struct ata_host *host, int irq,
- struct scsi_host_template *sht)
+static int ahci_host_activate_multi_irqs(struct ata_host *host, int irq,
+ struct scsi_host_template *sht)
{
int i, rc;

@@ -2531,6 +2512,36 @@ out_free_irqs:

return rc;
}
+
+/**
+ * ahci_host_activate - start AHCI host, request IRQs and register it
+ * @host: target ATA host
+ * @irq: base IRQ number to request
+ * @sht: scsi_host_template to use when registering the host
+ *
+ * Similar to ata_host_activate, but requests IRQs according to AHCI-1.1
+ * when multiple MSIs were allocated. That is one MSI per port, starting
+ * from @irq.
+ *
+ * LOCKING:
+ * Inherited from calling layer (may sleep).
+ *
+ * RETURNS:
+ * 0 on success, -errno otherwise.
+ */
+int ahci_host_activate(struct ata_host *host, int irq,
+ struct scsi_host_template *sht)
+{
+ struct ahci_host_priv *hpriv = host->private_data;
+ int rc;
+
+ if (hpriv->flags & AHCI_HFLAG_MULTI_MSI)
+ rc = ahci_host_activate_multi_irqs(host, irq, sht);
+ else
+ rc = ata_host_activate(host, irq, ahci_interrupt,
+ IRQF_SHARED, sht);
+ return rc;
+}
EXPORT_SYMBOL_GPL(ahci_host_activate);

MODULE_AUTHOR("Jeff Garzik");
diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c
index c7f787e..0b03f90 100644
--- a/drivers/ata/libahci_platform.c
+++ b/drivers/ata/libahci_platform.c
@@ -493,8 +493,7 @@ int ahci_platform_init_host(struct platform_device *pdev,
ahci_init_controller(host);
ahci_print_info(host, "platform");

- return ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED,
- &ahci_platform_sht);
+ return ahci_host_activate(host, irq, &ahci_platform_sht);
}
EXPORT_SYMBOL_GPL(ahci_platform_init_host);

diff --git a/drivers/ata/sata_highbank.c b/drivers/ata/sata_highbank.c
index da3bc27..ce2b99a 100644
--- a/drivers/ata/sata_highbank.c
+++ b/drivers/ata/sata_highbank.c
@@ -568,8 +568,7 @@ static int ahci_highbank_probe(struct platform_device *pdev)
ahci_init_controller(host);
ahci_print_info(host, "platform");

- rc = ata_host_activate(host, irq, ahci_interrupt, 0,
- &ahci_highbank_platform_sht);
+ rc = ahci_host_activate(host, irq, &ahci_highbank_platform_sht);
if (rc)
goto err0;
--
1.8.3.1
Alexander Gordeev
2014-09-29 16:25:58 UTC
Permalink
This update is a prerequisite for consolidation of
AHCI host activation code within ahci_host_activate()
function.

Signed-off-by: Alexander Gordeev <***@redhat.com>
Cc: linux-***@vger.kernel.org
---
drivers/ata/ahci.c | 60 --------------------------------------------------
drivers/ata/libahci.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 61 insertions(+), 60 deletions(-)

diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index fcda5b6..0b21604 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -1226,66 +1226,6 @@ intx:
return 0;
}

-/**
- * ahci_host_activate - start AHCI host, request IRQs and register it
- * @host: target ATA host
- * @irq: base IRQ number to request
- * @sht: scsi_host_template to use when registering the host
- *
- * Similar to ata_host_activate, but requests IRQs according to AHCI-1.1
- * when multiple MSIs were allocated. That is one MSI per port, starting
- * from @irq.
- *
- * LOCKING:
- * Inherited from calling layer (may sleep).
- *
- * RETURNS:
- * 0 on success, -errno otherwise.
- */
-int ahci_host_activate(struct ata_host *host, int irq,
- struct scsi_host_template *sht)
-{
- int i, rc;
-
- rc = ata_host_start(host);
- if (rc)
- return rc;
-
- for (i = 0; i < host->n_ports; i++) {
- struct ahci_port_priv *pp = host->ports[i]->private_data;
-
- /* Do not receive interrupts sent by dummy ports */
- if (!pp) {
- disable_irq(irq + i);
- continue;
- }
-
- rc = devm_request_threaded_irq(host->dev, irq + i,
- ahci_hw_interrupt,
- ahci_thread_fn, IRQF_SHARED,
- pp->irq_desc, host->ports[i]);
- if (rc)
- goto out_free_irqs;
- }
-
- for (i = 0; i < host->n_ports; i++)
- ata_port_desc(host->ports[i], "irq %d", irq + i);
-
- rc = ata_host_register(host, sht);
- if (rc)
- goto out_free_all_irqs;
-
- return 0;
-
-out_free_all_irqs:
- i = host->n_ports;
-out_free_irqs:
- for (i--; i >= 0; i--)
- devm_free_irq(host->dev, irq + i, host->ports[i]);
-
- return rc;
-}
-
static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
unsigned int board_id = ent->driver_data;
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index b784e9d..21bb427 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -2472,6 +2472,67 @@ void ahci_set_em_messages(struct ahci_host_priv *hpriv,
}
EXPORT_SYMBOL_GPL(ahci_set_em_messages);

+/**
+ * ahci_host_activate - start AHCI host, request IRQs and register it
+ * @host: target ATA host
+ * @irq: base IRQ number to request
+ * @sht: scsi_host_template to use when registering the host
+ *
+ * Similar to ata_host_activate, but requests IRQs according to AHCI-1.1
+ * when multiple MSIs were allocated. That is one MSI per port, starting
+ * from @irq.
+ *
+ * LOCKING:
+ * Inherited from calling layer (may sleep).
+ *
+ * RETURNS:
+ * 0 on success, -errno otherwise.
+ */
+int ahci_host_activate(struct ata_host *host, int irq,
+ struct scsi_host_template *sht)
+{
+ int i, rc;
+
+ rc = ata_host_start(host);
+ if (rc)
+ return rc;
+
+ for (i = 0; i < host->n_ports; i++) {
+ struct ahci_port_priv *pp = host->ports[i]->private_data;
+
+ /* Do not receive interrupts sent by dummy ports */
+ if (!pp) {
+ disable_irq(irq + i);
+ continue;
+ }
+
+ rc = devm_request_threaded_irq(host->dev, irq + i,
+ ahci_hw_interrupt,
+ ahci_thread_fn, IRQF_SHARED,
+ pp->irq_desc, host->ports[i]);
+ if (rc)
+ goto out_free_irqs;
+ }
+
+ for (i = 0; i < host->n_ports; i++)
+ ata_port_desc(host->ports[i], "irq %d", irq + i);
+
+ rc = ata_host_register(host, sht);
+ if (rc)
+ goto out_free_all_irqs;
+
+ return 0;
+
+out_free_all_irqs:
+ i = host->n_ports;
+out_free_irqs:
+ for (i--; i >= 0; i--)
+ devm_free_irq(host->dev, irq + i, host->ports[i]);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(ahci_host_activate);
+
MODULE_AUTHOR("Jeff Garzik");
MODULE_DESCRIPTION("Common AHCI SATA low-level routines");
MODULE_LICENSE("GPL");
--
1.8.3.1
Alexander Gordeev
2014-09-29 16:26:01 UTC
Permalink
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 <***@redhat.com>
Suggested-by: "Jiang, Dave" <***@intel.com>
Cc: "Jiang, Dave" <***@intel.com>
Cc: linux-***@vger.kernel.org
---
drivers/ata/ahci.h | 2 +-
drivers/ata/libahci.c | 67 ++++++---------------------------------------------
2 files changed, 9 insertions(+), 60 deletions(-)

diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
index 6a22055..40f0e34 100644
--- a/drivers/ata/ahci.h
+++ b/drivers/ata/ahci.h
@@ -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 */
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index 48175e5..97683e4 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -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);
@@ -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");
--
1.8.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-ide" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Alexander Gordeev
2014-10-06 15:24:45 UTC
Permalink
Split interrupt service routine into hardware context handler
and threaded context handler. That allows to protect ports with
individual locks rather than with a single host-wide lock and
move port interrupts handling out of the hardware interrupt
context.

Testing was done by transferring 8GB on two hard drives in
parallel using command 'dd if=/dev/sd{a,b} of=/dev/null'. With
lock_stat statistics I measured access times to ata_host::lock
spinlock (since interrupt handler code is fully embraced with
this lock). The average lock's holdtime decreased eight times
while average waittime decreased two times.

Both before and after the change the transfer time is the same,
while 'perf record -e cycles:k ...' shows 1%-4% CPU time spent
in ahci_single_irq_intr() routine before the update and not even
sampled/shown ahci_single_irq_intr() after the update.

Signed-off-by: Alexander Gordeev <***@redhat.com>
Cc: linux-***@vger.kernel.org
---
drivers/ata/libahci.c | 74 ++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 61 insertions(+), 13 deletions(-)

diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index 97683e4..3ce3d23 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -1778,15 +1778,16 @@ static void ahci_handle_port_interrupt(struct ata_port *ap,
}
}

-static void ahci_port_intr(struct ata_port *ap)
+static void ahci_update_intr_status(struct ata_port *ap)
{
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);

- ahci_handle_port_interrupt(ap, port_mmio, status);
+ atomic_or(status, &pp->intr_status);
}

static irqreturn_t ahci_port_thread_fn(int irq, void *dev_instance)
@@ -1807,6 +1808,34 @@ static irqreturn_t ahci_port_thread_fn(int irq, void *dev_instance)
return IRQ_HANDLED;
}

+irqreturn_t ahci_thread_fn(int irq, void *dev_instance)
+{
+ struct ata_host *host = dev_instance;
+ struct ahci_host_priv *hpriv = host->private_data;
+ u32 irq_masked = hpriv->port_map;
+ unsigned int i;
+
+ 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_port_thread_fn(irq, 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);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
static irqreturn_t ahci_multi_irqs_intr(int irq, void *dev_instance)
{
struct ata_port *ap = dev_instance;
@@ -1856,7 +1885,7 @@ static irqreturn_t ahci_single_irq_intr(int irq, void *dev_instance)

ap = host->ports[i];
if (ap) {
- ahci_port_intr(ap);
+ ahci_update_intr_status(ap);
VPRINTK("port %u\n", i);
} else {
VPRINTK("port %u (no irq)\n", i);
@@ -1883,7 +1912,7 @@ static irqreturn_t ahci_single_irq_intr(int irq, void *dev_instance)

VPRINTK("EXIT\n");

- return IRQ_RETVAL(handled);
+ return handled ? IRQ_WAKE_THREAD : IRQ_NONE;
}

unsigned int ahci_qc_issue(struct ata_queued_cmd *qc)
@@ -2295,13 +2324,8 @@ static int ahci_port_start(struct ata_port *ap)
*/
pp->intr_mask = DEF_PORT_IRQ;

- /*
- * Switch to per-port locking in case each port has its own MSI vector.
- */
- if ((hpriv->flags & AHCI_HFLAG_MULTI_MSI)) {
- spin_lock_init(&pp->lock);
- ap->lock = &pp->lock;
- }
+ spin_lock_init(&pp->lock);
+ ap->lock = &pp->lock;

ap->private_data = pp;

@@ -2462,6 +2486,31 @@ out_free_irqs:
return rc;
}

+static int ahci_host_activate_single_irq(struct ata_host *host, int irq,
+ struct scsi_host_template *sht)
+{
+ int i, rc;
+
+ rc = ata_host_start(host);
+ if (rc)
+ return rc;
+
+ rc = devm_request_threaded_irq(host->dev, irq, ahci_single_irq_intr,
+ ahci_thread_fn, IRQF_SHARED,
+ dev_driver_string(host->dev), host);
+ if (rc)
+ return rc;
+
+ for (i = 0; i < host->n_ports; i++)
+ ata_port_desc(host->ports[i], "irq %d", irq);
+
+ rc = ata_host_register(host, sht);
+ if (rc)
+ devm_free_irq(host->dev, irq, host);
+
+ return rc;
+}
+
/**
* ahci_host_activate - start AHCI host, request IRQs and register it
* @host: target ATA host
@@ -2487,8 +2536,7 @@ int ahci_host_activate(struct ata_host *host, int irq,
if (hpriv->flags & AHCI_HFLAG_MULTI_MSI)
rc = ahci_host_activate_multi_irqs(host, irq, sht);
else
- rc = ata_host_activate(host, irq, ahci_single_irq_intr,
- IRQF_SHARED, sht);
+ rc = ahci_host_activate_single_irq(host, irq, sht);
return rc;
}
EXPORT_SYMBOL_GPL(ahci_host_activate);
--
1.8.3.1
--
Regards,
Alexander Gordeev
***@redhat.com
--
To unsubscribe from this list: send the line "unsubscribe linux-ide" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Alexander Gordeev
2014-10-06 15:26:35 UTC
Permalink
There is no need to acquire ata_host::lock spinlock from
hardware context single IRQ interrupt handler since the
handler does not access host data that could be altered
by concurrent processors.

Signed-off-by: Alexander Gordeev <***@redhat.com>
Cc: linux-***@vger.kernel.org
---
drivers/ata/libahci.c | 4 ----
1 file changed, 4 deletions(-)

diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index 3ce3d23..5eb61c9 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -1875,8 +1875,6 @@ static irqreturn_t ahci_single_irq_intr(int irq, void *dev_instance)

irq_masked = irq_stat & hpriv->port_map;

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

@@ -1908,8 +1906,6 @@ static irqreturn_t ahci_single_irq_intr(int irq, void *dev_instance)
*/
writel(irq_stat, mmio + HOST_IRQ_STAT);

- spin_unlock(&host->lock);
-
VPRINTK("EXIT\n");

return handled ? IRQ_WAKE_THREAD : IRQ_NONE;
--
1.8.3.1
--
Regards,
Alexander Gordeev
***@redhat.com
--
To unsubscribe from this list: send the line "unsubscribe linux-ide" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Tejun Heo
2014-10-06 15:45:25 UTC
Permalink
Applied 1-7 to libata/for-3.18.

Thanks.
--
tejun
--
To unsubscribe from this list: send the line "unsubscribe linux-ide" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Loading...