From 65ea5f37d350a5f2ee70eebeda15653c52d611cc Mon Sep 17 00:00:00 2001
From: Phong Vo <pvo@apm.com>
Date: Tue, 19 Jul 2016 08:08:32 +0700
Subject: [PATCH] APM updates for ahci, gic-v2m, xgenet, and kernel

This patch adds the following patches to CentOS git tree. All are
backports of patches/fixes that are already available upstream.
Please refer to individual patches for details.

0013-workqueue-Make-flush_workqueue-available-again-to-no.patch
0014-drivers-irqchip-update-irq-gic-v2m.c.patch
0015-ahci_xgene-Implement-the-workaround-to-fix-the-missi.patch
0016-Revert-ext4-remove-block_device_ejected.patch
0017-drivers-net-xgene-upstream-bugfixes.patch

Signed-off-by: Phong Vo <pvo@apm.com>
---
 ...ake-flush_workqueue-available-again-to-no.patch |   40 +
 ...0014-drivers-irqchip-update-irq-gic-v2m.c.patch |  365 +++++++
 ...Implement-the-workaround-to-fix-the-missi.patch | 1032 ++++++++++++++++++++
 ...6-Revert-ext4-remove-block_device_ejected.patch |  104 ++
 .../0017-drivers-net-xgene-upstream-bugfixes.patch |  640 ++++++++++++
 SPECS/kernel-aarch64.spec                          |   11 +
 6 files changed, 2192 insertions(+)
 create mode 100644 SOURCES/0013-workqueue-Make-flush_workqueue-available-again-to-no.patch
 create mode 100644 SOURCES/0014-drivers-irqchip-update-irq-gic-v2m.c.patch
 create mode 100644 SOURCES/0015-ahci_xgene-Implement-the-workaround-to-fix-the-missi.patch
 create mode 100644 SOURCES/0016-Revert-ext4-remove-block_device_ejected.patch
 create mode 100644 SOURCES/0017-drivers-net-xgene-upstream-bugfixes.patch

diff --git a/SOURCES/0013-workqueue-Make-flush_workqueue-available-again-to-no.patch b/SOURCES/0013-workqueue-Make-flush_workqueue-available-again-to-no.patch
new file mode 100644
index 0000000..c9c7607
--- /dev/null
+++ b/SOURCES/0013-workqueue-Make-flush_workqueue-available-again-to-no.patch
@@ -0,0 +1,40 @@
+From 942ce4e50b62f98404005536d5d8c7b386fa139a Mon Sep 17 00:00:00 2001
+From: Tim Gardner <tim.gardner@canonical.com>
+Date: Tue, 4 Aug 2015 11:26:04 -0600
+Subject: [PATCH 01/21] workqueue: Make flush_workqueue() available again to
+ non GPL modules
+
+Commit 37b1ef31a568fc02e53587620226e5f3c66454c8 ("workqueue: move
+flush_scheduled_work() to workqueue.h") moved the exported non GPL
+flush_scheduled_work() from a function to an inline wrapper.
+Unfortunately, it directly calls flush_workqueue() which is a GPL function.
+This has the effect of changing the licensing requirement for this function
+and makes it unavailable to non GPL modules.
+
+See commit ad7b1f841f8a54c6d61ff181451f55b68175e15a ("workqueue: Make
+schedule_work() available again to non GPL modules") for precedent.
+
+Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+(cherry picked from commit 1dadafa86a779884f14a6e7a3ddde1a57b0a0a65)
+Signed-off-by: Phong Vo <pvo@apm.com>
+---
+ kernel/workqueue.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/kernel/workqueue.c b/kernel/workqueue.c
+index 4c4f061..a413acb 100644
+--- a/kernel/workqueue.c
++++ b/kernel/workqueue.c
+@@ -2614,7 +2614,7 @@ void flush_workqueue(struct workqueue_struct *wq)
+ out_unlock:
+ 	mutex_unlock(&wq->mutex);
+ }
+-EXPORT_SYMBOL_GPL(flush_workqueue);
++EXPORT_SYMBOL(flush_workqueue);
+ 
+ /**
+  * drain_workqueue - drain a workqueue
+-- 
+2.6.0
+
diff --git a/SOURCES/0014-drivers-irqchip-update-irq-gic-v2m.c.patch b/SOURCES/0014-drivers-irqchip-update-irq-gic-v2m.c.patch
new file mode 100644
index 0000000..813ec79
--- /dev/null
+++ b/SOURCES/0014-drivers-irqchip-update-irq-gic-v2m.c.patch
@@ -0,0 +1,365 @@
+From 7ec0bc4aab2291afdb7defbb730eb8d8e6d72d6b Mon Sep 17 00:00:00 2001
+From: Phong Vo <pvo@apm.com>
+Date: Tue, 5 Apr 2016 17:55:17 +0700
+Subject: [PATCH 02/21] drivers:irqchip: update irq-gic-v2m.c
+
+This patch is a backport of drivers/irqchip/irq-gic-v2m.c from upstream
+as appropriate for kernel 4.2. It would resolve the issue of not
+able to register to all PCI MSI frames supported by X-Gene 2.
+
+Signed-off-by: Duc Dang <dhdang@apm.com>
+Signed-off-by: Phong Vo <pvo@apm.com>
+---
+ drivers/irqchip/irq-gic-v2m.c | 238 +++++++++++++++++++++++-------------------
+ 1 file changed, 131 insertions(+), 107 deletions(-)
+
+diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c
+index 9c019d1..827c640 100644
+--- a/drivers/irqchip/irq-gic-v2m.c
++++ b/drivers/irqchip/irq-gic-v2m.c
+@@ -51,8 +51,13 @@
+ /* List of flags for specific v2m implementation */
+ #define GICV2M_NEEDS_SPI_OFFSET		0x00000001
+ 
++static LIST_HEAD(v2m_nodes);
++static DEFINE_SPINLOCK(v2m_lock);
++
+ struct v2m_data {
+-	spinlock_t msi_cnt_lock;
++	struct list_head entry;
++	struct device_node *node;
++	struct msi_controller mchip;
+ 	struct resource res;	/* GICv2m resource */
+ 	void __iomem *base;	/* GICv2m virt address */
+ 	u32 spi_start;		/* The SPI number that MSIs start */
+@@ -105,8 +110,8 @@ static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+ 	struct v2m_data *v2m = irq_data_get_irq_chip_data(data);
+ 	phys_addr_t addr = v2m->res.start + V2M_MSI_SETSPI_NS;
+ 
+-	msg->address_hi = (u32) (addr >> 32);
+-	msg->address_lo = (u32) (addr);
++	msg->address_hi = upper_32_bits(addr);
++	msg->address_lo = lower_32_bits(addr);
+ 	msg->data = data->hwirq;
+ 
+ 	if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET)
+@@ -156,27 +161,30 @@ static void gicv2m_unalloc_msi(struct v2m_data *v2m, unsigned int hwirq)
+ 		return;
+ 	}
+ 
+-	spin_lock(&v2m->msi_cnt_lock);
++	spin_lock(&v2m_lock);
+ 	__clear_bit(pos, v2m->bm);
+-	spin_unlock(&v2m->msi_cnt_lock);
++	spin_unlock(&v2m_lock);
+ }
+ 
+ static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+ 				   unsigned int nr_irqs, void *args)
+ {
+-	struct v2m_data *v2m = domain->host_data;
++	struct v2m_data *v2m = NULL, *tmp;
+ 	int hwirq, offset, err = 0;
+ 
+-	spin_lock(&v2m->msi_cnt_lock);
+-	offset = find_first_zero_bit(v2m->bm, v2m->nr_spis);
+-	if (offset < v2m->nr_spis)
+-		__set_bit(offset, v2m->bm);
+-	else
+-		err = -ENOSPC;
+-	spin_unlock(&v2m->msi_cnt_lock);
++	spin_lock(&v2m_lock);
++	list_for_each_entry(tmp, &v2m_nodes, entry) {
++		offset = find_first_zero_bit(tmp->bm, tmp->nr_spis);
++		if (offset < tmp->nr_spis) {
++			__set_bit(offset, tmp->bm);
++			v2m = tmp;
++			break;
++		}
++	}
++	spin_unlock(&v2m_lock);
+ 
+-	if (err)
+-		return err;
++	if (!v2m)
++		return -ENOSPC;
+ 
+ 	hwirq = v2m->spi_start + offset;
+ 
+@@ -203,7 +211,7 @@ static void gicv2m_irq_domain_free(struct irq_domain *domain,
+ 	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+ }
+ 
+-static struct irq_domain_ops gicv2m_domain_ops = {
++static const struct irq_domain_ops gicv2m_domain_ops = {
+ 	.alloc			= gicv2m_irq_domain_alloc,
+ 	.free			= gicv2m_irq_domain_free,
+ };
+@@ -224,17 +232,55 @@ static bool is_msi_spi_valid(u32 base, u32 num)
+ 	return true;
+ }
+ 
+-char gicv2m_msi_domain_name[] = "V2M-MSI";
+-char gicv2m_domain_name[] = "GICV2M";
+-static int __init gicv2m_init_one(struct irq_domain *parent,
++static void gicv2m_teardown(void)
++{
++	struct v2m_data *v2m, *tmp;
++
++	list_for_each_entry_safe(v2m, tmp, &v2m_nodes, entry) {
++		list_del(&v2m->entry);
++		kfree(v2m->bm);
++		iounmap(v2m->base);
++		of_node_put(v2m->node);
++		kfree(v2m);
++	}
++}
++
++static int gicv2m_allocate_domains(struct irq_domain *parent)
++{
++	struct irq_domain *inner_domain;
++	struct v2m_data *v2m;
++
++	v2m = list_first_entry_or_null(&v2m_nodes, struct v2m_data, entry);
++	if (!v2m)
++		return 0;
++
++	inner_domain = irq_domain_add_tree(NULL, &gicv2m_domain_ops, v2m);
++	if (!inner_domain) {
++		pr_err("Failed to create GICv2m domain\n");
++		return -ENOMEM;
++	}
++
++	inner_domain->parent = parent;
++	v2m->domain = pci_msi_create_default_irq_domain(v2m->node,
++						      &gicv2m_msi_domain_info,
++						      inner_domain);
++	if (!v2m->domain) {
++		pr_err("Failed to create MSI domain\n");
++		if (inner_domain)
++			irq_domain_remove(inner_domain);
++		return -ENOMEM;
++	}
++
++	return 0;
++}
++
++static int __init gicv2m_init_one(struct device_node *node,
+ 				  u32 spi_start, u32 nr_spis,
+-				  struct resource *res,
+-				  struct device_node *node,
+-				  u32 msi_frame_id)
++                                  struct resource *res,
++                                  u32 msi_frame_id)
+ {
+ 	int ret;
+ 	struct v2m_data *v2m;
+-	struct irq_domain *inner_domain;
+ 
+ 	v2m = kzalloc(sizeof(struct v2m_data), GFP_KERNEL);
+ 	if (!v2m) {
+@@ -242,13 +288,17 @@ static int __init gicv2m_init_one(struct irq_domain *parent,
+ 		return -ENOMEM;
+ 	}
+ 
+-	v2m->base = ioremap(res->start, resource_size(res));
++	INIT_LIST_HEAD(&v2m->entry);
++	v2m->node = node;
++
++	memcpy(&v2m->res, res, sizeof(struct resource));
++
++	v2m->base = ioremap(v2m->res.start, resource_size(&v2m->res));
+ 	if (!v2m->base) {
+ 		pr_err("Failed to map GICv2m resource\n");
+ 		ret = -ENOMEM;
+ 		goto err_free_v2m;
+ 	}
+-	memcpy(&v2m->res,res, sizeof(struct resource));
+ 
+ 	if (spi_start && nr_spis) {
+ 		v2m->spi_start = spi_start;
+@@ -283,37 +333,12 @@ static int __init gicv2m_init_one(struct irq_domain *parent,
+ 		goto err_iounmap;
+ 	}
+ 
+-	inner_domain = irq_domain_add_tree(NULL, &gicv2m_domain_ops, v2m);
+-	if (!inner_domain) {
+-		pr_err("Failed to create GICv2m domain\n");
+-		ret = -ENOMEM;
+-		goto err_free_bm;
+-	}
+-
+-	inner_domain->parent = parent;
+-	inner_domain->name = gicv2m_domain_name;
+-	gicv2m_msi_domain_info.acpi_msi_frame_id = msi_frame_id;
+-	v2m->domain = pci_msi_create_default_irq_domain(node, &gicv2m_msi_domain_info,
+-							inner_domain);
+-	if (!v2m->domain) {
+-		pr_err("Failed to create MSI domain\n");
+-		ret = -ENOMEM;
+-		goto err_free_domains;
+-	}
+-
+-	v2m->domain->name = gicv2m_msi_domain_name;
+-
+-	spin_lock_init(&v2m->msi_cnt_lock);
++	list_add_tail(&v2m->entry, &v2m_nodes);
+ 
++	pr_info("range%pR, SPI[%d:%d]\n", res,
++		v2m->spi_start, (v2m->spi_start + v2m->nr_spis - 1));
+ 	return 0;
+ 
+-err_free_domains:
+-	if (v2m->domain)
+-		irq_domain_remove(v2m->domain);
+-	if (inner_domain)
+-		irq_domain_remove(inner_domain);
+-err_free_bm:
+-	kfree(v2m->bm);
+ err_iounmap:
+ 	iounmap(v2m->base);
+ err_free_v2m:
+@@ -345,87 +370,86 @@ int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent)
+ 			break;
+ 		}
+ 
+-		if (!of_property_read_u32(child, "arm,msi-base-spi", &spi_start) &&
++		if (!of_property_read_u32(child, "arm,msi-base-spi",
++					  &spi_start) &&
+ 		    !of_property_read_u32(child, "arm,msi-num-spis", &nr_spis))
+-			pr_info("Overriding V2M MSI_TYPER (base:%u, num:%u)\n",
++			pr_info("DT overriding V2M MSI_TYPER (base:%u, num:%u)\n",
+ 				spi_start, nr_spis);
+ 
+-		ret = gicv2m_init_one(parent, spi_start, nr_spis, &res, child, 0);
++		ret = gicv2m_init_one(child, spi_start, nr_spis, &res, 0);
+ 		if (ret) {
+-			of_node_put(node);
++			of_node_put(child);
+ 			break;
+ 		}
+-
+-		pr_info("Node %s: range[%#lx:%#lx], SPI[%d:%d]\n", child->name,
+-			(unsigned long)res.start, (unsigned long)res.end,
+-			spi_start, (spi_start + nr_spis));
+ 	}
+ 
++	if (!ret)
++		ret = gicv2m_allocate_domains(parent);
++	if (ret)
++		gicv2m_teardown();
++
+ 	return ret;
+ }
+ 
+ #ifdef CONFIG_ACPI
+-static struct acpi_madt_generic_msi_frame *msi_frame;
++static int acpi_num_msi;
+ 
+ static int __init
+-gic_acpi_parse_madt_msi(struct acpi_subtable_header *header,
+-			const unsigned long end)
++acpi_parse_madt_msi(struct acpi_subtable_header *header,
++		    const unsigned long end)
+ {
+-	struct acpi_madt_generic_msi_frame *frame;
++	struct resource res;
++	u32 spi_start = 0, nr_spis = 0;
++	struct acpi_madt_generic_msi_frame *m;
+ 
+-	frame = (struct acpi_madt_generic_msi_frame *)header;
+-	if (BAD_MADT_ENTRY(frame, end))
++	m = (struct acpi_madt_generic_msi_frame *)header;
++	if (BAD_MADT_ENTRY(m, end))
+ 		return -EINVAL;
+ 
+-	if (msi_frame)
+-		pr_warn("Only one GIC MSI FRAME supported.\n");
+-	else
+-		msi_frame = frame;
++	res.start = m->base_address;
++	res.end = m->base_address + 0xfff;
+ 
+-	return 0;
++	if (m->flags & ACPI_MADT_OVERRIDE_SPI_VALUES) {
++		spi_start = m->spi_base;
++		nr_spis = m->spi_count;
++
++		pr_info("ACPI overriding V2M MSI_TYPER (base:%u, num:%u)\n",
++			spi_start, nr_spis);
++	}
++
++	return (gicv2m_init_one(NULL, spi_start, nr_spis, &res, m->msi_frame_id));
+ }
+ 
+ int __init gicv2m_acpi_init(struct acpi_table_header *table,
+-			    struct irq_domain *parent)
++                            struct irq_domain *parent)
+ {
+-	int ret = 0;
+-	int count, i;
+-	static struct acpi_madt_generic_msi_frame *cur;
+-
+-	count = acpi_parse_entries(ACPI_SIG_MADT, sizeof(struct acpi_table_madt),
+-				   gic_acpi_parse_madt_msi, table,
+-				   ACPI_MADT_TYPE_GENERIC_MSI_FRAME, 0);
+-	if ((count <= 0) || !msi_frame) {
+-		pr_debug("No valid ACPI GIC MSI FRAME exist\n");
+-		return 0;
+-	}
++	int ret;
+ 
+-	for (i = 0, cur = msi_frame; i < count; i++, cur++) {
+-		struct resource res;
+-		u32 spi_start = 0, nr_spis = 0;
++	if (acpi_num_msi > 0)
++		return 0;
+ 
+-		res.start = cur->base_address;
+-		res.end = cur->base_address + 0x1000;
++	acpi_num_msi = acpi_parse_entries(ACPI_SIG_MADT,
++					  sizeof(struct acpi_table_madt),
++					  acpi_parse_madt_msi, table,
++					  ACPI_MADT_TYPE_GENERIC_MSI_FRAME, 0);
++	if (acpi_num_msi <= 0) {
++		goto err_out;
++	}
+ 
+-		if (cur->flags & ACPI_MADT_OVERRIDE_SPI_VALUES) {
+-			spi_start = cur->spi_base;
+-			nr_spis = cur->spi_count;
++	ret = gicv2m_allocate_domains(parent);
++	if (ret)
++		goto err_out;
+ 
+-			pr_info("ACPI overriding V2M MSI_TYPER (base:%u, num:%u)\n",
+-				spi_start, nr_spis);
+-		}
+-
+-		ret = gicv2m_init_one(parent, spi_start, nr_spis, &res, NULL,
+-				      cur->msi_frame_id);
+-		if (ret)
+-			break;
++	return 0;
+ 
+-		pr_info("MSI frame ID %u: range[%#lx:%#lx], SPI[%d:%d]\n",
+-			cur->msi_frame_id,
+-			(unsigned long)res.start, (unsigned long)res.end,
+-			spi_start, (spi_start + nr_spis));
+-	}
+-	return ret;
++err_out:
++	gicv2m_teardown();
++	return -EINVAL;
++}
++#else /* CONFIG_ACPI */
++static int __init gicv2m_acpi_init(struct acpi_table_header *table,
++				   struct irq_domain *parent)
++{
++	return -EINVAL;
+ }
+-
+ #endif /* CONFIG_ACPI */
+-- 
+2.6.0
+
diff --git a/SOURCES/0015-ahci_xgene-Implement-the-workaround-to-fix-the-missi.patch b/SOURCES/0015-ahci_xgene-Implement-the-workaround-to-fix-the-missi.patch
new file mode 100644
index 0000000..8c15d0f
--- /dev/null
+++ b/SOURCES/0015-ahci_xgene-Implement-the-workaround-to-fix-the-missi.patch
@@ -0,0 +1,1032 @@
+From a0e65e3d5f1925197dbf81a317e9531640406680 Mon Sep 17 00:00:00 2001
+From: Manuel Lauss <manuel.lauss@gmail.com>
+Date: Wed, 30 Sep 2015 21:10:25 +0200
+Subject: [PATCH 03/21] ata: ahci: find eSATA ports and flag them as removable
+
+If the AHCI ports' HPCP or ESP bits are set, the port
+should be considered external (e.g. eSATA) and is marked
+as removable.  Userspace tools like udisks then treat it
+like an usb drive.
+
+With this patch applied, when I plug a drive into the esata port,
+KDE pops up a window asking what to do with the drives(s), just
+like it does for any random USB stick.
+
+Removability is indicated to the upper layers by way of the
+SCSI RMB bit, as I haven't found another way to signal
+userspace to treat a sata disk like any usb stick.
+
+Signed-off-by: Manuel Lauss <manuel.lauss@gmail.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+(cherry picked from commit 8a3e33cf92c7b7ae25c589eccd1a69ab11cc4353)
+Signed-off-by: Phong Vo <pvo@apm.com>
+---
+ drivers/ata/ahci.h        | 2 ++
+ drivers/ata/libahci.c     | 7 +++++++
+ drivers/ata/libata-scsi.c | 7 +++++--
+ include/linux/libata.h    | 1 +
+ 4 files changed, 15 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
+index 5b8e8a0..45586c1 100644
+--- a/drivers/ata/ahci.h
++++ b/drivers/ata/ahci.h
+@@ -181,6 +181,8 @@ enum {
+ 	PORT_CMD_ALPE		= (1 << 26), /* Aggressive Link PM enable */
+ 	PORT_CMD_ATAPI		= (1 << 24), /* Device is ATAPI */
+ 	PORT_CMD_FBSCP		= (1 << 22), /* FBS Capable Port */
++	PORT_CMD_ESP		= (1 << 21), /* External Sata Port */
++	PORT_CMD_HPCP		= (1 << 18), /* HotPlug Capable Port */
+ 	PORT_CMD_PMP		= (1 << 17), /* PMP attached */
+ 	PORT_CMD_LIST_ON	= (1 << 15), /* cmd list DMA engine running */
+ 	PORT_CMD_FIS_ON		= (1 << 14), /* FIS DMA engine running */
+diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
+index d256a66..2fa551a 100644
+--- a/drivers/ata/libahci.c
++++ b/drivers/ata/libahci.c
+@@ -1117,6 +1117,7 @@ static void ahci_port_init(struct device *dev, struct ata_port *ap,
+ 			   int port_no, void __iomem *mmio,
+ 			   void __iomem *port_mmio)
+ {
++	struct ahci_host_priv *hpriv = ap->host->private_data;
+ 	const char *emsg = NULL;
+ 	int rc;
+ 	u32 tmp;
+@@ -1138,6 +1139,12 @@ static void ahci_port_init(struct device *dev, struct ata_port *ap,
+ 		writel(tmp, port_mmio + PORT_IRQ_STAT);
+ 
+ 	writel(1 << port_no, mmio + HOST_IRQ_STAT);
++
++	/* mark esata ports */
++	tmp = readl(port_mmio + PORT_CMD);
++	if ((tmp & PORT_CMD_HPCP) ||
++	    ((tmp & PORT_CMD_ESP) && (hpriv->cap & HOST_CAP_SXS)))
++		ap->pflags |= ATA_PFLAG_EXTERNAL;
+ }
+ 
+ void ahci_init_controller(struct ata_host *host)
+diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
+index 0d7f0da..183a57b 100644
+--- a/drivers/ata/libata-scsi.c
++++ b/drivers/ata/libata-scsi.c
+@@ -2015,8 +2015,11 @@ static unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf)
+ 
+ 	VPRINTK("ENTER\n");
+ 
+-	/* set scsi removable (RMB) bit per ata bit */
+-	if (ata_id_removable(args->id))
++	/* set scsi removable (RMB) bit per ata bit, or if the
++	 * AHCI port says it's external (Hotplug-capable, eSATA).
++	 */
++	if (ata_id_removable(args->id) ||
++	    (args->dev->link->ap->pflags & ATA_PFLAG_EXTERNAL))
+ 		hdr[1] |= (1 << 7);
+ 
+ 	if (args->dev->class == ATA_DEV_ZAC) {
+diff --git a/include/linux/libata.h b/include/linux/libata.h
+index c9cfbcd..83577f8 100644
+--- a/include/linux/libata.h
++++ b/include/linux/libata.h
+@@ -254,6 +254,7 @@ enum {
+ 
+ 	ATA_PFLAG_PIO32		= (1 << 20),  /* 32bit PIO */
+ 	ATA_PFLAG_PIO32CHANGE	= (1 << 21),  /* 32bit PIO can be turned on/off */
++	ATA_PFLAG_EXTERNAL	= (1 << 22),  /* eSATA/external port */
+ 
+ 	/* struct ata_queued_cmd flags */
+ 	ATA_QCFLAG_ACTIVE	= (1 << 0), /* cmd not yet ack'd to scsi lyer */
+-- 
+2.6.0
+
+From 1fa72a1089f0b69fa6ccecdc5f48cb1ff82e8c70 Mon Sep 17 00:00:00 2001
+From: Dan Williams <dan.j.williams@intel.com>
+Date: Fri, 30 Oct 2015 17:09:30 -0400
+Subject: [PATCH 04/21] ahci: ahci_host_activate: kill IRQF_SHARED
+
+MSI messages are per-device, so there will never be another "shared"
+device in the interrupt chain.
+
+Signed-off-by: Dan Williams <dan.j.williams@intel.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+(cherry picked from commit 899a63c3ebf3fad8723a432c1bba875ec3ef1948)
+Signed-off-by: Phong Vo <pvo@apm.com>
+---
+ drivers/ata/libahci.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
+index 2fa551a..67f97e3 100644
+--- a/drivers/ata/libahci.c
++++ b/drivers/ata/libahci.c
+@@ -2493,7 +2493,7 @@ static int ahci_host_activate_multi_irqs(struct ata_host *host, int irq,
+ 
+ 		rc = devm_request_threaded_irq(host->dev, irq + i,
+ 					       ahci_multi_irqs_intr,
+-					       ahci_port_thread_fn, IRQF_SHARED,
++					       ahci_port_thread_fn, 0,
+ 					       pp->irq_desc, host->ports[i]);
+ 		if (rc)
+ 			goto out_free_irqs;
+-- 
+2.6.0
+
+From c160734389e064b73c74d55371200f4cabb20b0f Mon Sep 17 00:00:00 2001
+From: Dan Williams <dan.j.williams@intel.com>
+Date: Fri, 30 Oct 2015 17:09:35 -0400
+Subject: [PATCH 05/21] ahci: cleanup ahci_host_activate_multi_irqs
+
+With devm there is no need to explicitly free irqs on error.
+
+Signed-off-by: Dan Williams <dan.j.williams@intel.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+(cherry picked from commit 0a142b26921c23ccdb6ef802d8047d02ad4cf0a7)
+Signed-off-by: Phong Vo <pvo@apm.com>
+---
+ drivers/ata/libahci.c | 21 +++------------------
+ 1 file changed, 3 insertions(+), 18 deletions(-)
+
+diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
+index 67f97e3..096064c 100644
+--- a/drivers/ata/libahci.c
++++ b/drivers/ata/libahci.c
+@@ -2496,25 +2496,10 @@ static int ahci_host_activate_multi_irqs(struct ata_host *host, int irq,
+ 					       ahci_port_thread_fn, 0,
+ 					       pp->irq_desc, host->ports[i]);
+ 		if (rc)
+-			goto out_free_irqs;
+-	}
+-
+-	for (i = 0; i < host->n_ports; i++)
++			return rc;
+ 		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;
++	}
++	return ata_host_register(host, sht);
+ }
+ 
+ /**
+-- 
+2.6.0
+
+From 1e259526141bb38e471580fe36d6c705ac53e85b Mon Sep 17 00:00:00 2001
+From: Dan Williams <dan.j.williamps@intel.com>
+Date: Wed, 11 Nov 2015 16:27:33 -0800
+Subject: [PATCH 06/21] ahci: per-port msix support
+
+Some AHCI controllers support per-port MSI-X vectors.  At the same time
+the Linux AHCI driver needs to support one-off architectures that
+implement a single MSI-X vector for all ports.  The heuristic for
+enabling AHCI ports becomes, in order of preference:
+
+1/ per-port multi-MSI-X
+
+2/ per-port multi-MSI
+
+3/ single MSI
+
+4/ single MSI-X
+
+5/ legacy INTX
+
+This all depends on AHCI implementations with potentially broken MSI-X
+requesting less vectors than the number of ports.  If this assumption is
+violated we will need to start explicitly white-listing AHCI-MSIX
+implementations.
+
+Reported-by: Ricardo Neri <ricardo.neri@intel.com>
+[ricardo: fix struct msix_entry handling]
+Reported-by: kernel test robot <ying.huang@linux.intel.com>
+Signed-off-by: Dan Williams <dan.j.williams@intel.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+
+(cherry picked from commit d684a90d38e24dcaf95fdb32c83efe05f80d152a)
+Signed-off-by: Phong Vo <pvo@apm.com>
+---
+ drivers/ata/ahci.c    | 67 ++++++++++++++++++++++++++++++++++-----------------
+ drivers/ata/ahci.h    |  2 ++
+ drivers/ata/libahci.c | 19 +++++++++++----
+ 3 files changed, 61 insertions(+), 27 deletions(-)
+
+diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
+index 7e62751..3f2cdf8 100644
+--- a/drivers/ata/ahci.c
++++ b/drivers/ata/ahci.c
+@@ -1291,15 +1291,13 @@ static inline void ahci_gtf_filter_workaround(struct ata_host *host)
+ #endif
+ 
+ /*
+- * ahci_init_msix() only implements single MSI-X support, not multiple
+- * MSI-X per-port interrupts. This is needed for host controllers that only
+- * have MSI-X support implemented, but no MSI or intx.
++ * ahci_init_msix() - optionally enable per-port MSI-X otherwise defer
++ * to single msi.
+  */
+ static int ahci_init_msix(struct pci_dev *pdev, unsigned int n_ports,
+-			  struct ahci_host_priv *hpriv)
++			  struct ahci_host_priv *hpriv, unsigned long flags)
+ {
+-	int rc, nvec;
+-	struct msix_entry entry = {};
++	int nvec, i, rc;
+ 
+ 	/* Do not init MSI-X if MSI is disabled for the device */
+ 	if (hpriv->flags & AHCI_HFLAG_NO_MSI)
+@@ -1309,22 +1307,39 @@ static int ahci_init_msix(struct pci_dev *pdev, unsigned int n_ports,
+ 	if (nvec < 0)
+ 		return nvec;
+ 
+-	if (!nvec) {
++	/*
++	 * Proper MSI-X implementations will have a vector per-port.
++	 * Barring that, we prefer single-MSI over single-MSIX.  If this
++	 * check fails (not enough MSI-X vectors for all ports) we will
++	 * be called again with the flag clear iff ahci_init_msi()
++	 * fails.
++	 */
++	if (flags & AHCI_HFLAG_MULTI_MSIX) {
++		if (nvec < n_ports)
++			return -ENODEV;
++		nvec = n_ports;
++	} else if (nvec) {
++		nvec = 1;
++	} else {
++		/*
++		 * Emit dev_err() since this was the non-legacy irq
++		 * method of last resort.
++		 */
+ 		rc = -ENODEV;
+ 		goto fail;
+ 	}
+ 
+-	/*
+-	 * There can be more than one vector (e.g. for error detection or
+-	 * hdd hotplug). Only the first vector (entry.entry = 0) is used.
+-	 */
+-	rc = pci_enable_msix_exact(pdev, &entry, 1);
++	for (i = 0; i < nvec; i++)
++		hpriv->msix[i].entry = i;
++	rc = pci_enable_msix_exact(pdev, hpriv->msix, nvec);
+ 	if (rc < 0)
+ 		goto fail;
+ 
+-	hpriv->irq = entry.vector;
++	if (nvec > 1)
++		hpriv->flags |= AHCI_HFLAG_MULTI_MSIX;
++	hpriv->irq = hpriv->msix[0].vector; /* for single msi-x */
+ 
+-	return 1;
++	return nvec;
+ fail:
+ 	dev_err(&pdev->dev,
+ 		"failed to enable MSI-X with error %d, # of vectors: %d\n",
+@@ -1388,20 +1403,25 @@ static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports,
+ {
+ 	int nvec;
+ 
++	/*
++	 * Try to enable per-port MSI-X.  If the host is not capable
++	 * fall back to single MSI before finally attempting single
++	 * MSI-X.
++	 */
++	nvec = ahci_init_msix(pdev, n_ports, hpriv, AHCI_HFLAG_MULTI_MSIX);
++	if (nvec >= 0)
++		return nvec;
++
+ 	nvec = ahci_init_msi(pdev, n_ports, hpriv);
+ 	if (nvec >= 0)
+ 		return nvec;
+ 
+-	/*
+-	 * Currently, MSI-X support only implements single IRQ mode and
+-	 * exists for controllers which can't do other types of IRQ. Only
+-	 * set it up if MSI fails.
+-	 */
+-	nvec = ahci_init_msix(pdev, n_ports, hpriv);
++	/* try single-msix */
++	nvec = ahci_init_msix(pdev, n_ports, hpriv, 0);
+ 	if (nvec >= 0)
+ 		return nvec;
+ 
+-	/* lagacy intx interrupts */
++	/* legacy intx interrupts */
+ 	pci_intx(pdev, 1);
+ 	hpriv->irq = pdev->irq;
+ 
+@@ -1575,7 +1595,10 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+ 	if (!host)
+ 		return -ENOMEM;
+ 	host->private_data = hpriv;
+-
++	hpriv->msix = devm_kzalloc(&pdev->dev,
++			sizeof(struct msix_entry) * n_ports, GFP_KERNEL);
++	if (!hpriv->msix)
++		return -ENOMEM;
+ 	ahci_init_interrupts(pdev, n_ports, hpriv);
+ 
+ 	if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss)
+diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
+index 45586c1..9e60c50 100644
+--- a/drivers/ata/ahci.h
++++ b/drivers/ata/ahci.h
+@@ -242,6 +242,7 @@ enum {
+ 	AHCI_HFLAG_NO_FBS		= (1 << 18), /* no FBS */
+ 	AHCI_HFLAG_EDGE_IRQ		= (1 << 19), /* HOST_IRQ_STAT behaves as
+ 							Edge Triggered */
++	AHCI_HFLAG_MULTI_MSIX		= (1 << 20), /* per-port MSI-X */
+ 
+ 	/* ap->flags bits */
+ 
+@@ -343,6 +344,7 @@ struct ahci_host_priv {
+ 	 * the PHY position in this array.
+ 	 */
+ 	struct phy		**phys;
++	struct msix_entry	*msix;		/* Optional MSI-X support */
+ 	unsigned		nports;		/* Number of ports */
+ 	void			*plat_data;	/* Other platform data */
+ 	unsigned int		irq;		/* interrupt line */
+diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
+index 096064c..0a5645f 100644
+--- a/drivers/ata/libahci.c
++++ b/drivers/ata/libahci.c
+@@ -43,6 +43,7 @@
+ #include <scsi/scsi_host.h>
+ #include <scsi/scsi_cmnd.h>
+ #include <linux/libata.h>
++#include <linux/pci.h>
+ #include "ahci.h"
+ #include "libata.h"
+ 
+@@ -2470,9 +2471,10 @@ void ahci_set_em_messages(struct ahci_host_priv *hpriv,
+ }
+ EXPORT_SYMBOL_GPL(ahci_set_em_messages);
+ 
+-static int ahci_host_activate_multi_irqs(struct ata_host *host, int irq,
++static int ahci_host_activate_multi_irqs(struct ata_host *host,
+ 					 struct scsi_host_template *sht)
+ {
++	struct ahci_host_priv *hpriv = host->private_data;
+ 	int i, rc;
+ 
+ 	rc = ata_host_start(host);
+@@ -2484,6 +2486,12 @@ static int ahci_host_activate_multi_irqs(struct ata_host *host, int irq,
+ 	 */
+ 	for (i = 0; i < host->n_ports; i++) {
+ 		struct ahci_port_priv *pp = host->ports[i]->private_data;
++		int irq;
++
++		if (hpriv->flags & AHCI_HFLAG_MULTI_MSIX)
++			irq = hpriv->msix[i].vector;
++		else
++			irq = hpriv->irq + i;
+ 
+ 		/* Do not receive interrupts sent by dummy ports */
+ 		if (!pp) {
+@@ -2491,14 +2499,15 @@ static int ahci_host_activate_multi_irqs(struct ata_host *host, int irq,
+ 			continue;
+ 		}
+ 
+-		rc = devm_request_threaded_irq(host->dev, irq + i,
++		rc = devm_request_threaded_irq(host->dev, irq,
+ 					       ahci_multi_irqs_intr,
+ 					       ahci_port_thread_fn, 0,
+ 					       pp->irq_desc, host->ports[i]);
+ 		if (rc)
+ 			return rc;
+-		ata_port_desc(host->ports[i], "irq %d", irq + i);
++		ata_port_desc(host->ports[i], "irq %d", irq);
+ 	}
++
+ 	return ata_host_register(host, sht);
+ }
+ 
+@@ -2519,8 +2528,8 @@ int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht)
+ 	int irq = hpriv->irq;
+ 	int rc;
+ 
+-	if (hpriv->flags & AHCI_HFLAG_MULTI_MSI)
+-		rc = ahci_host_activate_multi_irqs(host, irq, sht);
++	if (hpriv->flags & (AHCI_HFLAG_MULTI_MSI | AHCI_HFLAG_MULTI_MSIX))
++		rc = ahci_host_activate_multi_irqs(host, sht);
+ 	else if (hpriv->flags & AHCI_HFLAG_EDGE_IRQ)
+ 		rc = ata_host_activate(host, irq, ahci_single_edge_irq_intr,
+ 				       IRQF_SHARED, sht);
+-- 
+2.6.0
+
+From bdd2e8d9ea72935b4ec52fd1b7b65e499987b268 Mon Sep 17 00:00:00 2001
+From: Dan Williams <dan.j.williams@intel.com>
+Date: Wed, 11 Nov 2015 16:27:38 -0800
+Subject: [PATCH 07/21] ahci: switch from 'threaded' to 'hardirq' interrupt
+ handling
+
+For high frequency I/O the overhead of threaded interrupts impacts
+performance.  A quick out-of-the-box test (i.e. no affinity tuning)
+shows ~10% random read performance at ~20% less cpu.  The cpu wins
+appear to be from reduced lock contention.
+
+Signed-off-by: Dan Williams <dan.j.williams@intel.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+(cherry picked from commit a6b7fb764ed2a6b7bb1ac96d93c06787aa589092)
+Signed-off-by: Phong Vo <pvo@apm.com>
+---
+ drivers/ata/libahci.c | 34 ++++++++--------------------------
+ 1 file changed, 8 insertions(+), 26 deletions(-)
+
+diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
+index 0a5645f..1b6c7cc 100644
+--- a/drivers/ata/libahci.c
++++ b/drivers/ata/libahci.c
+@@ -1796,41 +1796,24 @@ static void ahci_port_intr(struct ata_port *ap)
+ 	ahci_handle_port_interrupt(ap, port_mmio, status);
+ }
+ 
+-static irqreturn_t ahci_port_thread_fn(int irq, void *dev_instance)
++static irqreturn_t ahci_multi_irqs_intr_hard(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);
+ 	u32 status;
+ 
+-	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);
+-	spin_unlock_bh(ap->lock);
+-
+-	return IRQ_HANDLED;
+-}
+-
+-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;
+-
+ 	VPRINTK("ENTER\n");
+ 
+ 	status = readl(port_mmio + PORT_IRQ_STAT);
+ 	writel(status, port_mmio + PORT_IRQ_STAT);
+ 
+-	atomic_or(status, &pp->intr_status);
++	spin_lock(ap->lock);
++	ahci_handle_port_interrupt(ap, port_mmio, status);
++	spin_unlock(ap->lock);
+ 
+ 	VPRINTK("EXIT\n");
+ 
+-	return IRQ_WAKE_THREAD;
++	return IRQ_HANDLED;
+ }
+ 
+ static u32 ahci_handle_port_intr(struct ata_host *host, u32 irq_masked)
+@@ -2499,10 +2482,9 @@ static int ahci_host_activate_multi_irqs(struct ata_host *host,
+ 			continue;
+ 		}
+ 
+-		rc = devm_request_threaded_irq(host->dev, irq,
+-					       ahci_multi_irqs_intr,
+-					       ahci_port_thread_fn, 0,
+-					       pp->irq_desc, host->ports[i]);
++		rc = devm_request_irq(host->dev, irq, ahci_multi_irqs_intr_hard,
++				0, pp->irq_desc, host->ports[i]);
++
+ 		if (rc)
+ 			return rc;
+ 		ata_port_desc(host->ports[i], "irq %d", irq);
+-- 
+2.6.0
+
+From cf638a7790a2f270a14e8e6ebb0ccd57fb285e30 Mon Sep 17 00:00:00 2001
+From: Dan Williams <dan.j.williams@intel.com>
+Date: Sat, 5 Dec 2015 16:18:44 -0800
+Subject: [PATCH 08/21] ahci: compile out msi/msix infrastructure
+
+Quoting Arnd:
+    The AHCI driver is used for some on-chip devices that do not use PCI
+    for probing, and it can be built even when CONFIG_PCI is disabled, but
+    that now results in a build failure:
+
+    ata/libahci.c: In function 'ahci_host_activate_multi_irqs':
+    ata/libahci.c:2475:4: error: invalid use of undefined type 'struct msix_entry'
+    ata/libahci.c:2475:21: error: dereferencing pointer to incomplete type 'struct msix_entry'
+
+Add ifdef CONFIG_PCI_MSI infrastructure to compile out the multi-msi and
+multi-msix code.
+
+Reported-by: Arnd Bergmann <arnd@arndb.de>
+Tested--by: Arnd Bergmann <arnd@arndb.de>
+[arnd: fix up pci enabled case]
+Reported-by: Paul Gortmaker <paul.gortmaker@windriver.com>
+Fixes: d684a90d38e2 ("ahci: per-port msix support")
+Signed-off-by: Dan Williams <dan.j.williams@intel.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+
+(cherry picked from commit f893180b79f6ada44068e4fe764eb2de70ee6bea)
+Signed-off-by: Phong Vo <pvo@apm.com>
+---
+ drivers/ata/ahci.h    | 26 ++++++++++++++++++++++++--
+ drivers/ata/libahci.c |  7 +------
+ 2 files changed, 25 insertions(+), 8 deletions(-)
+
+diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
+index 9e60c50..60e7f66 100644
+--- a/drivers/ata/ahci.h
++++ b/drivers/ata/ahci.h
+@@ -35,6 +35,7 @@
+ #ifndef _AHCI_H
+ #define _AHCI_H
+ 
++#include <linux/pci.h>
+ #include <linux/clk.h>
+ #include <linux/libata.h>
+ #include <linux/phy/phy.h>
+@@ -237,12 +238,18 @@ enum {
+ 	AHCI_HFLAG_DELAY_ENGINE		= (1 << 15), /* do not start engine on
+ 						        port start (wait until
+ 						        error-handling stage) */
+-	AHCI_HFLAG_MULTI_MSI		= (1 << 16), /* multiple PCI MSIs */
+ 	AHCI_HFLAG_NO_DEVSLP		= (1 << 17), /* no device sleep */
+ 	AHCI_HFLAG_NO_FBS		= (1 << 18), /* no FBS */
+ 	AHCI_HFLAG_EDGE_IRQ		= (1 << 19), /* HOST_IRQ_STAT behaves as
+ 							Edge Triggered */
+-	AHCI_HFLAG_MULTI_MSIX		= (1 << 20), /* per-port MSI-X */
++#ifdef CONFIG_PCI_MSI
++	AHCI_HFLAG_MULTI_MSI		= (1 << 20), /* multiple PCI MSIs */
++	AHCI_HFLAG_MULTI_MSIX		= (1 << 21), /* per-port MSI-X */
++#else
++	/* compile out MSI infrastructure */
++	AHCI_HFLAG_MULTI_MSI		= 0,
++	AHCI_HFLAG_MULTI_MSIX		= 0,
++#endif
+ 
+ 	/* ap->flags bits */
+ 
+@@ -356,6 +363,21 @@ struct ahci_host_priv {
+ 	void			(*start_engine)(struct ata_port *ap);
+ };
+ 
++#ifdef CONFIG_PCI_MSI
++static inline int ahci_irq_vector(struct ahci_host_priv *hpriv, int port)
++{
++	if (hpriv->flags & AHCI_HFLAG_MULTI_MSIX)
++		return hpriv->msix[port].vector;
++	else
++		return hpriv->irq + port;
++}
++#else
++static inline int ahci_irq_vector(struct ahci_host_priv *hpriv, int port)
++{
++	return hpriv->irq;
++}
++#endif
++
+ extern int ahci_ignore_sss;
+ 
+ extern struct device_attribute *ahci_shost_attrs[];
+diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
+index 1b6c7cc..eda3cf2 100644
+--- a/drivers/ata/libahci.c
++++ b/drivers/ata/libahci.c
+@@ -2469,12 +2469,7 @@ static int ahci_host_activate_multi_irqs(struct ata_host *host,
+ 	 */
+ 	for (i = 0; i < host->n_ports; i++) {
+ 		struct ahci_port_priv *pp = host->ports[i]->private_data;
+-		int irq;
+-
+-		if (hpriv->flags & AHCI_HFLAG_MULTI_MSIX)
+-			irq = hpriv->msix[i].vector;
+-		else
+-			irq = hpriv->irq + i;
++		int irq = ahci_irq_vector(hpriv, i);
+ 
+ 		/* Do not receive interrupts sent by dummy ports */
+ 		if (!pp) {
+-- 
+2.6.0
+
+From d14e257f2521887eb2da0bdb0c2b6b776aa06f61 Mon Sep 17 00:00:00 2001
+From: Tejun Heo <tj@kernel.org>
+Date: Fri, 15 Jan 2016 15:13:05 -0500
+Subject: [PATCH 09/21] libata: disable forced PORTS_IMPL for >= AHCI 1.3
+
+Some early controllers incorrectly reported zero ports in PORTS_IMPL
+register and the ahci driver fabricates PORTS_IMPL from the number of
+ports in those cases.  This hasn't mattered but with the new nvme
+controllers there are cases where zero PORTS_IMPL is valid and should
+be honored.
+
+Disable the workaround for >= AHCI 1.3.
+
+Signed-off-by: Tejun Heo <tj@kernel.org>
+Reported-by: Andy Lutomirski <luto@amacapital.net>
+Link: http://lkml.kernel.org/g/CALCETrU7yMvXEDhjAUShoHEhDwifJGapdw--BKxsP0jmjKGmRw@mail.gmail.com
+Cc: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
+Cc: stable@vger.kernel.org
+(cherry picked from commit 566d1827df2ef0cbe921d3d6946ac3007b1a6938)
+Signed-off-by: Phong Vo <pvo@apm.com>
+---
+ drivers/ata/libahci.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
+index eda3cf2..c198280 100644
+--- a/drivers/ata/libahci.c
++++ b/drivers/ata/libahci.c
+@@ -496,8 +496,8 @@ void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv)
+ 		}
+ 	}
+ 
+-	/* fabricate port_map from cap.nr_ports */
+-	if (!port_map) {
++	/* fabricate port_map from cap.nr_ports for < AHCI 1.3 */
++	if (!port_map && vers < 0x10300) {
+ 		port_map = (1 << ahci_nr_ports(cap)) - 1;
+ 		dev_warn(dev, "forcing PORTS_IMPL to 0x%x\n", port_map);
+ 
+-- 
+2.6.0
+
+From 3c8cc8a0edd22abd21ac35138650820a0537ac76 Mon Sep 17 00:00:00 2001
+From: Suman Tripathi <stripathi@apm.com>
+Date: Sat, 6 Feb 2016 11:25:22 +0530
+Subject: [PATCH 10/21] libahci: Implement the capability to override the
+ generic ahci interrupt handler.
+
+This patch implements the capability to override the generic AHCI
+interrupt handler so that specific ahci drivers can implement their
+own custom interrupt handler routines.  It also exports
+ahci_handle_port_intr so that custom irq_handler implementations can
+use it.
+
+tj: s/ahci_irq_handler/irq_handler/ and updated description.
+
+Signed-off-by: Suman Tripathi <stripathi@apm.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+(cherry picked from commit f070d6715509dafc0af223577c896fe3d204ca88)
+Signed-off-by: Phong Vo <pvo@apm.com>
+---
+ drivers/ata/ahci.h    |  2 ++
+ drivers/ata/libahci.c | 27 ++++++++++++++++++++-------
+ 2 files changed, 22 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
+index 60e7f66..42d4e57 100644
+--- a/drivers/ata/ahci.h
++++ b/drivers/ata/ahci.h
+@@ -361,6 +361,7 @@ struct ahci_host_priv {
+ 	 * be overridden anytime before the host is activated.
+ 	 */
+ 	void			(*start_engine)(struct ata_port *ap);
++	irqreturn_t 		(*irq_handler)(int irq, void *dev_instance);
+ };
+ 
+ #ifdef CONFIG_PCI_MSI
+@@ -424,6 +425,7 @@ int ahci_reset_em(struct ata_host *host);
+ void ahci_print_info(struct ata_host *host, const char *scc_s);
+ int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht);
+ void ahci_error_handler(struct ata_port *ap);
++u32 ahci_handle_port_intr(struct ata_host *host, u32 irq_masked);
+ 
+ static inline void __iomem *__ahci_port_base(struct ata_host *host,
+ 					     unsigned int port_no)
+diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
+index c198280..87a2acb 100644
+--- a/drivers/ata/libahci.c
++++ b/drivers/ata/libahci.c
+@@ -113,6 +113,9 @@ static ssize_t ahci_store_em_buffer(struct device *dev,
+ 				    const char *buf, size_t size);
+ static ssize_t ahci_show_em_supported(struct device *dev,
+ 				      struct device_attribute *attr, char *buf);
++static irqreturn_t ahci_single_edge_irq_intr(int irq, void *dev_instance);
++
++static irqreturn_t ahci_single_level_irq_intr(int irq, void *dev_instance);
+ 
+ static DEVICE_ATTR(ahci_host_caps, S_IRUGO, ahci_show_host_caps, NULL);
+ static DEVICE_ATTR(ahci_host_cap2, S_IRUGO, ahci_show_host_cap2, NULL);
+@@ -512,6 +515,11 @@ void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv)
+ 
+ 	if (!hpriv->start_engine)
+ 		hpriv->start_engine = ahci_start_engine;
++
++	if (!hpriv->irq_handler)
++		hpriv->irq_handler = (hpriv->flags & AHCI_HFLAG_EDGE_IRQ) ?
++				     ahci_single_edge_irq_intr :
++				     ahci_single_level_irq_intr;
+ }
+ EXPORT_SYMBOL_GPL(ahci_save_initial_config);
+ 
+@@ -1816,7 +1824,7 @@ static irqreturn_t ahci_multi_irqs_intr_hard(int irq, void *dev_instance)
+ 	return IRQ_HANDLED;
+ }
+ 
+-static u32 ahci_handle_port_intr(struct ata_host *host, u32 irq_masked)
++u32 ahci_handle_port_intr(struct ata_host *host, u32 irq_masked)
+ {
+ 	unsigned int i, handled = 0;
+ 
+@@ -1842,6 +1850,7 @@ static u32 ahci_handle_port_intr(struct ata_host *host, u32 irq_masked)
+ 
+ 	return handled;
+ }
++EXPORT_SYMBOL_GPL(ahci_handle_port_intr);
+ 
+ static irqreturn_t ahci_single_edge_irq_intr(int irq, void *dev_instance)
+ {
+@@ -2505,14 +2514,18 @@ int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht)
+ 	int irq = hpriv->irq;
+ 	int rc;
+ 
+-	if (hpriv->flags & (AHCI_HFLAG_MULTI_MSI | AHCI_HFLAG_MULTI_MSIX))
++	if (hpriv->flags & (AHCI_HFLAG_MULTI_MSI | AHCI_HFLAG_MULTI_MSIX)) {
++		if (hpriv->irq_handler)
++			dev_warn(host->dev, "both AHCI_HFLAG_MULTI_MSI flag set \
++				 and custom irq handler implemented\n");
++
+ 		rc = ahci_host_activate_multi_irqs(host, sht);
+-	else if (hpriv->flags & AHCI_HFLAG_EDGE_IRQ)
+-		rc = ata_host_activate(host, irq, ahci_single_edge_irq_intr,
+-				       IRQF_SHARED, sht);
+-	else
+-		rc = ata_host_activate(host, irq, ahci_single_level_irq_intr,
++	} else {
++		rc = ata_host_activate(host, irq, hpriv->irq_handler,
+ 				       IRQF_SHARED, sht);
++	}
++
++
+ 	return rc;
+ }
+ EXPORT_SYMBOL_GPL(ahci_host_activate);
+-- 
+2.6.0
+
+From 516afb40a8bfc63c6b0a23d13ab618954128b52b Mon Sep 17 00:00:00 2001
+From: Suman Tripathi <stripathi@apm.com>
+Date: Sat, 6 Feb 2016 11:25:23 +0530
+Subject: [PATCH 11/21] ata: Remove the AHCI_HFLAG_EDGE_IRQ support from
+ libahci.
+
+The flexibility to override the irq handles in the LLD's are already
+present, so controllers implementing a edge trigger latch can
+implement their own interrupt handler inside the driver.  This patch
+removes the AHCI_HFLAG_EDGE_IRQ support from libahci and moves edge
+irq handling to ahci_xgene.
+
+tj: Minor update to description.
+
+Signed-off-by: Suman Tripathi <stripathi@apm.com>
+Signed-off-by: Tejun Heo <tj@kenrel.org>
+(cherry picked from commit d867b95f965457b9e85fb061ef8e3fdc029116ed)
+Signed-off-by: Phong Vo <pvo@apm.com>
+---
+ drivers/ata/ahci.h       |  3 +--
+ drivers/ata/ahci_xgene.c | 40 +++++++++++++++++++++++++++++++++++++++-
+ drivers/ata/libahci.c    | 43 +------------------------------------------
+ 3 files changed, 41 insertions(+), 45 deletions(-)
+
+diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
+index 42d4e57..4fe2b8d 100644
+--- a/drivers/ata/ahci.h
++++ b/drivers/ata/ahci.h
+@@ -240,8 +240,7 @@ enum {
+ 						        error-handling stage) */
+ 	AHCI_HFLAG_NO_DEVSLP		= (1 << 17), /* no device sleep */
+ 	AHCI_HFLAG_NO_FBS		= (1 << 18), /* no FBS */
+-	AHCI_HFLAG_EDGE_IRQ		= (1 << 19), /* HOST_IRQ_STAT behaves as
+-							Edge Triggered */
++
+ #ifdef CONFIG_PCI_MSI
+ 	AHCI_HFLAG_MULTI_MSI		= (1 << 20), /* multiple PCI MSIs */
+ 	AHCI_HFLAG_MULTI_MSIX		= (1 << 21), /* per-port MSI-X */
+diff --git a/drivers/ata/ahci_xgene.c b/drivers/ata/ahci_xgene.c
+index cc91850..5d47365 100644
+--- a/drivers/ata/ahci_xgene.c
++++ b/drivers/ata/ahci_xgene.c
+@@ -561,6 +561,43 @@ softreset_retry:
+ 	return rc;
+ }
+ 
++static irqreturn_t xgene_ahci_irq_intr(int irq, void *dev_instance)
++{
++	struct ata_host *host = dev_instance;
++	struct ahci_host_priv *hpriv;
++	unsigned int rc = 0;
++	void __iomem *mmio;
++	u32 irq_stat, irq_masked;
++
++	VPRINTK("ENTER\n");
++
++	hpriv = host->private_data;
++	mmio = hpriv->mmio;
++
++	/* sigh.  0xffffffff is a valid return from h/w */
++	irq_stat = readl(mmio + HOST_IRQ_STAT);
++	if (!irq_stat)
++		return IRQ_NONE;
++
++	irq_masked = irq_stat & hpriv->port_map;
++
++	spin_lock(&host->lock);
++
++	/*
++	 * HOST_IRQ_STAT behaves as edge triggered latch meaning that
++	 * it should be cleared before all the port events are cleared.
++	 */
++	writel(irq_stat, mmio + HOST_IRQ_STAT);
++
++	rc = ahci_handle_port_intr(host, irq_masked);
++
++	spin_unlock(&host->lock);
++
++	VPRINTK("EXIT\n");
++
++	return IRQ_RETVAL(rc);
++}
++
+ static struct ata_port_operations xgene_ahci_v1_ops = {
+ 	.inherits = &ahci_ops,
+ 	.dev_config = xgene_ahci_dev_config,
+@@ -795,7 +832,8 @@ skip_clk_phy:
+ 		hpriv->flags = AHCI_HFLAG_NO_NCQ;
+ 		break;
+ 	case XGENE_AHCI_V2:
+-		hpriv->flags |= AHCI_HFLAG_YES_FBS | AHCI_HFLAG_EDGE_IRQ;
++		hpriv->flags |= AHCI_HFLAG_YES_FBS;
++		hpriv->irq_handler = xgene_ahci_irq_intr;
+ 		break;
+ 	default:
+ 		break;
+diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
+index 87a2acb..6dcd628 100644
+--- a/drivers/ata/libahci.c
++++ b/drivers/ata/libahci.c
+@@ -113,8 +113,6 @@ static ssize_t ahci_store_em_buffer(struct device *dev,
+ 				    const char *buf, size_t size);
+ static ssize_t ahci_show_em_supported(struct device *dev,
+ 				      struct device_attribute *attr, char *buf);
+-static irqreturn_t ahci_single_edge_irq_intr(int irq, void *dev_instance);
+-
+ static irqreturn_t ahci_single_level_irq_intr(int irq, void *dev_instance);
+ 
+ static DEVICE_ATTR(ahci_host_caps, S_IRUGO, ahci_show_host_caps, NULL);
+@@ -517,9 +515,7 @@ void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv)
+ 		hpriv->start_engine = ahci_start_engine;
+ 
+ 	if (!hpriv->irq_handler)
+-		hpriv->irq_handler = (hpriv->flags & AHCI_HFLAG_EDGE_IRQ) ?
+-				     ahci_single_edge_irq_intr :
+-				     ahci_single_level_irq_intr;
++		hpriv->irq_handler = ahci_single_level_irq_intr;
+ }
+ EXPORT_SYMBOL_GPL(ahci_save_initial_config);
+ 
+@@ -1852,43 +1848,6 @@ u32 ahci_handle_port_intr(struct ata_host *host, u32 irq_masked)
+ }
+ EXPORT_SYMBOL_GPL(ahci_handle_port_intr);
+ 
+-static irqreturn_t ahci_single_edge_irq_intr(int irq, void *dev_instance)
+-{
+-	struct ata_host *host = dev_instance;
+-	struct ahci_host_priv *hpriv;
+-	unsigned int rc = 0;
+-	void __iomem *mmio;
+-	u32 irq_stat, irq_masked;
+-
+-	VPRINTK("ENTER\n");
+-
+-	hpriv = host->private_data;
+-	mmio = hpriv->mmio;
+-
+-	/* sigh.  0xffffffff is a valid return from h/w */
+-	irq_stat = readl(mmio + HOST_IRQ_STAT);
+-	if (!irq_stat)
+-		return IRQ_NONE;
+-
+-	irq_masked = irq_stat & hpriv->port_map;
+-
+-	spin_lock(&host->lock);
+-
+-	/*
+-	 * HOST_IRQ_STAT behaves as edge triggered latch meaning that
+-	 * it should be cleared before all the port events are cleared.
+-	 */
+-	writel(irq_stat, mmio + HOST_IRQ_STAT);
+-
+-	rc = ahci_handle_port_intr(host, irq_masked);
+-
+-	spin_unlock(&host->lock);
+-
+-	VPRINTK("EXIT\n");
+-
+-	return IRQ_RETVAL(rc);
+-}
+-
+ static irqreturn_t ahci_single_level_irq_intr(int irq, void *dev_instance)
+ {
+ 	struct ata_host *host = dev_instance;
+-- 
+2.6.0
+
+From 44d965b4274aeefd93d3f249bc1485dcf05fe26b Mon Sep 17 00:00:00 2001
+From: Suman Tripathi <stripathi@apm.com>
+Date: Sat, 6 Feb 2016 11:25:24 +0530
+Subject: [PATCH 12/21] ahci_xgene: Implement the workaround to fix the missing
+ of the edge interrupt for the HOST_IRQ_STAT.
+
+Due to H/W errata, the HOST_IRQ_STAT register misses the edge interrupt
+when clearing the HOST_IRQ_STAT register and hardware reporting the
+PORT_IRQ_STAT register happens to be at the same clock cycle.
+
+Signed-off-by: Suman Tripathi <stripathi@apm.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+(cherry picked from commit 32aea2680de01f539d928112150279fdeeabca00)
+Signed-off-by: Phong Vo <pvo@apm.com>
+---
+ drivers/ata/ahci_xgene.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 46 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/ata/ahci_xgene.c b/drivers/ata/ahci_xgene.c
+index 5d47365..410ee28 100644
+--- a/drivers/ata/ahci_xgene.c
++++ b/drivers/ata/ahci_xgene.c
+@@ -561,6 +561,51 @@ softreset_retry:
+ 	return rc;
+ }
+ 
++/**
++ * xgene_ahci_handle_broken_edge_irq - Handle the broken irq.
++ * @ata_host: Host that recieved the irq
++ * @irq_masked: HOST_IRQ_STAT value
++ *
++ * For hardware with broken edge trigger latch
++ * the HOST_IRQ_STAT register misses the edge interrupt
++ * when clearing of HOST_IRQ_STAT register and hardware
++ * reporting the PORT_IRQ_STAT register at the
++ * same clock cycle.
++ * As such, the algorithm below outlines the workaround.
++ *
++ * 1. Read HOST_IRQ_STAT register and save the state.
++ * 2. Clear the HOST_IRQ_STAT register.
++ * 3. Read back the HOST_IRQ_STAT register.
++ * 4. If HOST_IRQ_STAT register equals to zero, then
++ *    traverse the rest of port's PORT_IRQ_STAT register
++ *    to check if an interrupt is triggered at that point else
++ *    go to step 6.
++ * 5. If PORT_IRQ_STAT register of rest ports is not equal to zero
++ *    then update the state of HOST_IRQ_STAT saved in step 1.
++ * 6. Handle port interrupts.
++ * 7. Exit
++ */
++static int xgene_ahci_handle_broken_edge_irq(struct ata_host *host,
++					     u32 irq_masked)
++{
++	struct ahci_host_priv *hpriv = host->private_data;
++	void __iomem *port_mmio;
++	int i;
++
++	if (!readl(hpriv->mmio + HOST_IRQ_STAT)) {
++		for (i = 0; i < host->n_ports; i++) {
++			if (irq_masked & (1 << i))
++				continue;
++
++			port_mmio = ahci_port_base(host->ports[i]);
++			if (readl(port_mmio + PORT_IRQ_STAT))
++				irq_masked |= (1 << i);
++		}
++	}
++
++	return ahci_handle_port_intr(host, irq_masked);
++}
++
+ static irqreturn_t xgene_ahci_irq_intr(int irq, void *dev_instance)
+ {
+ 	struct ata_host *host = dev_instance;
+@@ -589,7 +634,7 @@ static irqreturn_t xgene_ahci_irq_intr(int irq, void *dev_instance)
+ 	 */
+ 	writel(irq_stat, mmio + HOST_IRQ_STAT);
+ 
+-	rc = ahci_handle_port_intr(host, irq_masked);
++	rc = xgene_ahci_handle_broken_edge_irq(host, irq_masked);
+ 
+ 	spin_unlock(&host->lock);
+ 
+-- 
+2.6.0
+
diff --git a/SOURCES/0016-Revert-ext4-remove-block_device_ejected.patch b/SOURCES/0016-Revert-ext4-remove-block_device_ejected.patch
new file mode 100644
index 0000000..9cb0da2
--- /dev/null
+++ b/SOURCES/0016-Revert-ext4-remove-block_device_ejected.patch
@@ -0,0 +1,104 @@
+From 4816e0008609de1afdd98ae287f0a2ab985ac4ef Mon Sep 17 00:00:00 2001
+From: Theodore Ts'o <tytso@mit.edu>
+Date: Sun, 16 Aug 2015 10:03:57 -0400
+Subject: [PATCH 13/21] Revert "ext4: remove block_device_ejected"
+
+This reverts commit 08439fec266c3cc5702953b4f54bdf5649357de0.
+
+Unfortunately we still need to test for bdi->dev to avoid a crash when a
+USB stick is yanked out while a file system is mounted:
+
+   usb 2-2: USB disconnect, device number 2
+   Buffer I/O error on dev sdb1, logical block 15237120, lost sync page write
+   JBD2: Error -5 detected when updating journal superblock for sdb1-8.
+   BUG: unable to handle kernel paging request at 34beb000
+   IP: [<c136ce88>] __percpu_counter_add+0x18/0xc0
+   *pdpt = 0000000023db9001 *pde = 0000000000000000
+   Oops: 0000 [#1] SMP
+   CPU: 0 PID: 4083 Comm: umount Tainted: G     U     OE   4.1.1-040101-generic #201507011435
+   Hardware name: LENOVO 7675CTO/7675CTO, BIOS 7NETC2WW (2.22 ) 03/22/2011
+   task: ebf06b50 ti: ebebc000 task.ti: ebebc000
+   EIP: 0060:[<c136ce88>] EFLAGS: 00010082 CPU: 0
+   EIP is at __percpu_counter_add+0x18/0xc0
+   EAX: f21c8e88 EBX: f21c8e88 ECX: 00000000 EDX: 00000001
+   ESI: 00000001 EDI: 00000000 EBP: ebebde60 ESP: ebebde40
+    DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068
+   CR0: 8005003b CR2: 34beb000 CR3: 33354200 CR4: 000007f0
+   Stack:
+    c1abe100 edcb0098 edcb00ec ffffffff f21c8e68 ffffffff f21c8e68 f286d160
+    ebebde84 c1160454 00000010 00000282 f72a77f8 00000984 f72a77f8 f286d160
+    f286d170 ebebdea0 c11e613f 00000000 00000282 f72a77f8 edd7f4d0 00000000
+   Call Trace:
+    [<c1160454>] account_page_dirtied+0x74/0x110
+    [<c11e613f>] __set_page_dirty+0x3f/0xb0
+    [<c11e6203>] mark_buffer_dirty+0x53/0xc0
+    [<c124a0cb>] ext4_commit_super+0x17b/0x250
+    [<c124ac71>] ext4_put_super+0xc1/0x320
+    [<c11f04ba>] ? fsnotify_unmount_inodes+0x1aa/0x1c0
+    [<c11cfeda>] ? evict_inodes+0xca/0xe0
+    [<c11b925a>] generic_shutdown_super+0x6a/0xe0
+    [<c10a1df0>] ? prepare_to_wait_event+0xd0/0xd0
+    [<c1165a50>] ? unregister_shrinker+0x40/0x50
+    [<c11b92f6>] kill_block_super+0x26/0x70
+    [<c11b94f5>] deactivate_locked_super+0x45/0x80
+    [<c11ba007>] deactivate_super+0x47/0x60
+    [<c11d2b39>] cleanup_mnt+0x39/0x80
+    [<c11d2bc0>] __cleanup_mnt+0x10/0x20
+    [<c1080b51>] task_work_run+0x91/0xd0
+    [<c1011e3c>] do_notify_resume+0x7c/0x90
+    [<c1720da5>] work_notify
+   Code: 8b 55 e8 e9 f4 fe ff ff 90 90 90 90 90 90 90 90 90 90 90 55 89 e5 83 ec 20 89 5d f4 89 c3 89 75 f8 89 d6 89 7d fc 89 cf 8b 48 14 <64> 8b 01 89 45 ec 89 c2 8b 45 08 c1 fa 1f 01 75 ec 89 55 f0 89
+   EIP: [<c136ce88>] __percpu_counter_add+0x18/0xc0 SS:ESP 0068:ebebde40
+   CR2: 0000000034beb000
+   ---[ end trace dd564a7bea834ecd ]---
+
+Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=101011
+
+Signed-off-by: Theodore Ts'o <tytso@mit.edu>
+Cc: stable@vger.kernel.org
+
+(cherry picked from commit bdfe0cbd746aa9b2509c2f6d6be17193cf7facd7)
+Signed-off-by: Phong Vo <pvo@apm.com>
+---
+ fs/ext4/super.c | 18 +++++++++++++++++-
+ 1 file changed, 17 insertions(+), 1 deletion(-)
+
+diff --git a/fs/ext4/super.c b/fs/ext4/super.c
+index 58987b5..4a97b15 100644
+--- a/fs/ext4/super.c
++++ b/fs/ext4/super.c
+@@ -325,6 +325,22 @@ static void save_error_info(struct super_block *sb, const char *func,
+ 	ext4_commit_super(sb, 1);
+ }
+ 
++/*
++ * The del_gendisk() function uninitializes the disk-specific data
++ * structures, including the bdi structure, without telling anyone
++ * else.  Once this happens, any attempt to call mark_buffer_dirty()
++ * (for example, by ext4_commit_super), will cause a kernel OOPS.
++ * This is a kludge to prevent these oops until we can put in a proper
++ * hook in del_gendisk() to inform the VFS and file system layers.
++ */
++static int block_device_ejected(struct super_block *sb)
++{
++	struct inode *bd_inode = sb->s_bdev->bd_inode;
++	struct backing_dev_info *bdi = inode_to_bdi(bd_inode);
++
++	return bdi->dev == NULL;
++}
++
+ static void ext4_journal_commit_callback(journal_t *journal, transaction_t *txn)
+ {
+ 	struct super_block		*sb = journal->j_private;
+@@ -4617,7 +4633,7 @@ static int ext4_commit_super(struct super_block *sb, int sync)
+ 	struct buffer_head *sbh = EXT4_SB(sb)->s_sbh;
+ 	int error = 0;
+ 
+-	if (!sbh)
++	if (!sbh || block_device_ejected(sb))
+ 		return error;
+ 	if (buffer_write_io_error(sbh)) {
+ 		/*
+-- 
+2.6.0
+
diff --git a/SOURCES/0017-drivers-net-xgene-upstream-bugfixes.patch b/SOURCES/0017-drivers-net-xgene-upstream-bugfixes.patch
new file mode 100644
index 0000000..691275c
--- /dev/null
+++ b/SOURCES/0017-drivers-net-xgene-upstream-bugfixes.patch
@@ -0,0 +1,640 @@
+From 8381b8952ebbb7bd0e4e6be08995612896e2ee96 Mon Sep 17 00:00:00 2001
+From: Iyappan Subramanian <isubramanian@apm.com>
+Date: Fri, 29 Apr 2016 11:10:13 -0700
+Subject: [PATCH 14/21] drivers: net: xgene: Get channel number from device
+ binding
+
+This patch gets ethernet to CPU channel (prefetch buffer number) from
+the newly added 'channel' property, thus decoupling Linux driver from
+resource management.
+
+Signed-off-by: Iyappan Subramanian <isubramanian@apm.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+(cherry picked from commit 2a37daa634416a617bb8e9032626ab491004e7da)
+Signed-off-by: Phong Vo <pvo@apm.com>
+---
+ drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 15 ++++++++++++++-
+ 1 file changed, 14 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
+index eec69aa..2ceb03b 100644
+--- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
+@@ -962,6 +962,17 @@ static enum xgene_ring_owner xgene_derive_ring_owner(struct xgene_enet_pdata *p)
+ 	return owner;
+ }
+ 
++static u8 xgene_start_cpu_bufnum(struct xgene_enet_pdata *pdata)
++{
++	struct device *dev = &pdata->pdev->dev;
++	u32 cpu_bufnum;
++	int ret;
++
++	ret = device_property_read_u32(dev, "channel", &cpu_bufnum);
++
++	return (!ret) ? cpu_bufnum : pdata->cpu_bufnum;
++}
++
+ static int xgene_enet_create_desc_rings(struct net_device *ndev)
+ {
+ 	struct xgene_enet_pdata *pdata = netdev_priv(ndev);
+@@ -970,13 +981,15 @@ static int xgene_enet_create_desc_rings(struct net_device *ndev)
+ 	struct xgene_enet_desc_ring *buf_pool = NULL;
+ 	enum xgene_ring_owner owner;
+ 	dma_addr_t dma_exp_bufs;
+-	u8 cpu_bufnum = pdata->cpu_bufnum;
++	u8 cpu_bufnum;
+ 	u8 eth_bufnum = pdata->eth_bufnum;
+ 	u8 bp_bufnum = pdata->bp_bufnum;
+ 	u16 ring_num = pdata->ring_num;
+ 	u16 ring_id;
+ 	int i, ret, size;
+ 
++	cpu_bufnum = xgene_start_cpu_bufnum(pdata);
++
+ 	for (i = 0; i < pdata->rxq_cnt; i++) {
+ 		/* allocate rx descriptor ring */
+ 		owner = xgene_derive_ring_owner(pdata);
+-- 
+2.6.0
+
+From 9640d3c94348caa9560d3f3c1bfcb02508c6160d Mon Sep 17 00:00:00 2001
+From: Julia Lawall <julia.lawall@lip6.fr>
+Date: Sun, 1 May 2016 14:36:28 +0200
+Subject: [PATCH 15/21] drivers: net: xgene: constify xgene_cle_ops structure
+
+The xgene_cle_ops structure is never modified, so declare it as const.
+
+Done with the help of Coccinelle.
+
+Signed-off-by: Julia Lawall <Julia.Lawall@lip6.fr>
+Acked-by: Iyappan Subramanian <isubramanian@apm.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+(cherry picked from commit b555a3d1e91cfa75eb37680c0c86a32d18215729)
+Signed-off-by: Phong Vo <pvo@apm.com>
+---
+ drivers/net/ethernet/apm/xgene/xgene_enet_cle.c  | 2 +-
+ drivers/net/ethernet/apm/xgene/xgene_enet_cle.h  | 2 +-
+ drivers/net/ethernet/apm/xgene/xgene_enet_main.h | 2 +-
+ 3 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c b/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c
+index b6c13d6..8d3f66a 100644
+--- a/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c
++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c
+@@ -729,6 +729,6 @@ static int xgene_enet_cle_init(struct xgene_enet_pdata *pdata)
+ 	return xgene_cle_setup_ptree(pdata, enet_cle);
+ }
+ 
+-struct xgene_cle_ops xgene_cle3in_ops = {
++const struct xgene_cle_ops xgene_cle3in_ops = {
+ 	.cle_init = xgene_enet_cle_init,
+ };
+diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_cle.h b/drivers/net/ethernet/apm/xgene/xgene_enet_cle.h
+index 572bdc5..049dbe8 100644
+--- a/drivers/net/ethernet/apm/xgene/xgene_enet_cle.h
++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_cle.h
+@@ -290,6 +290,6 @@ struct xgene_enet_cle {
+ 	u32 jump_bytes;
+ };
+ 
+-extern struct xgene_cle_ops xgene_cle3in_ops;
++extern const struct xgene_cle_ops xgene_cle3in_ops;
+ 
+ #endif /* __XGENE_ENET_CLE_H__ */
+diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h
+index ebdfddd..2535167 100644
+--- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h
++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h
+@@ -191,7 +191,7 @@ struct xgene_enet_pdata {
+ 	struct xgene_mac_ops *mac_ops;
+ 	struct xgene_port_ops *port_ops;
+ 	struct xgene_ring_ops *ring_ops;
+-	struct xgene_cle_ops *cle_ops;
++	const struct xgene_cle_ops *cle_ops;
+ 	struct delayed_work link_work;
+ 	u32 port_id;
+ 	u8 cpu_bufnum;
+-- 
+2.6.0
+
+From a5c8c2ed962c146edf75cb9d1553d289855f6659 Mon Sep 17 00:00:00 2001
+From: Matthias Brugger <mbrugger@suse.com>
+Date: Tue, 3 May 2016 16:05:07 +0200
+Subject: [PATCH 16/21] drivers: net: xgene: Fix error handling
+
+When probe bails out with an error, we try to unregister the
+netdev before we have even registered it. Fix the goto statements
+for that.
+
+Signed-off-by: Matthias Brugger <mbrugger@suse.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+(cherry picked from commit 20decb7e486d7eefff3931f58d092d2d7c024a1c)
+Signed-off-by: Phong Vo <pvo@apm.com>
+---
+ drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
+index 2ceb03b..9986db8 100644
+--- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
+@@ -1640,20 +1640,22 @@ static int xgene_enet_probe(struct platform_device *pdev)
+ 
+ 	ret = xgene_enet_init_hw(pdata);
+ 	if (ret)
+-		goto err;
++		goto err_netdev;
+ 
+ 	mac_ops = pdata->mac_ops;
+ 	if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII) {
+ 		ret = xgene_enet_mdio_config(pdata);
+ 		if (ret)
+-			goto err;
+-	} else 
++			goto err_netdev;
++	} else {
+ 		INIT_DELAYED_WORK(&pdata->link_work, mac_ops->link_state);
++	}
+ 
+ 	xgene_enet_napi_add(pdata);
+ 	return 0;
+-err:
++err_netdev:
+ 	unregister_netdev(ndev);
++err:
+ 	free_netdev(ndev);
+ 	return ret;
+ }
+-- 
+2.6.0
+
+From 3d405c219962fd16b2f850f978b05a895b46881c Mon Sep 17 00:00:00 2001
+From: Iyappan Subramanian <isubramanian@apm.com>
+Date: Fri, 13 May 2016 16:52:57 -0700
+Subject: [PATCH 17/21] drivers: net: xgene: fix IPv4 forward crash
+
+This patch fixes the crash observed during IPv4 forward test by
+setting the drop field in the dbptr.
+
+Signed-off-by: Iyappan Subramanian <isubramanian@apm.com>
+Tested-by: Toan Le <toanle@apm.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+(cherry picked from commit b30cfd241fbc6818c7de0cbf89ae83c70a1dac9a)
+Signed-off-by: Phong Vo <pvo@apm.com>
+---
+ drivers/net/ethernet/apm/xgene/xgene_enet_cle.c | 11 ++++++-----
+ drivers/net/ethernet/apm/xgene/xgene_enet_cle.h |  2 ++
+ 2 files changed, 8 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c b/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c
+index 8d3f66a..289cd99 100644
+--- a/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c
++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c
+@@ -43,6 +43,7 @@ static void xgene_cle_idt_to_hw(u32 dstqid, u32 fpsel,
+ static void xgene_cle_dbptr_to_hw(struct xgene_enet_pdata *pdata,
+ 				  struct xgene_cle_dbptr *dbptr, u32 *buf)
+ {
++	buf[0] = SET_VAL(CLE_DROP, dbptr->drop);
+ 	buf[4] = SET_VAL(CLE_FPSEL, dbptr->fpsel) |
+ 		 SET_VAL(CLE_DSTQIDL, dbptr->dstqid);
+ 
+@@ -413,7 +414,7 @@ static int xgene_enet_cle_init(struct xgene_enet_pdata *pdata)
+ 			.branch = {
+ 				{
+ 					/* IPV4 */
+-					.valid = 0,
++					.valid = 1,
+ 					.next_packet_pointer = 22,
+ 					.jump_bw = JMP_FW,
+ 					.jump_rel = JMP_ABS,
+@@ -421,7 +422,7 @@ static int xgene_enet_cle_init(struct xgene_enet_pdata *pdata)
+ 					.next_node = PKT_PROT_NODE,
+ 					.next_branch = 0,
+ 					.data = 0x8,
+-					.mask = 0xffff
++					.mask = 0x0
+ 				},
+ 				{
+ 					.valid = 0,
+@@ -457,7 +458,7 @@ static int xgene_enet_cle_init(struct xgene_enet_pdata *pdata)
+ 					.next_node = RSS_IPV4_TCP_NODE,
+ 					.next_branch = 0,
+ 					.data = 0x0600,
+-					.mask = 0xffff
++					.mask = 0x00ff
+ 				},
+ 				{
+ 					/* UDP */
+@@ -469,7 +470,7 @@ static int xgene_enet_cle_init(struct xgene_enet_pdata *pdata)
+ 					.next_node = RSS_IPV4_UDP_NODE,
+ 					.next_branch = 0,
+ 					.data = 0x1100,
+-					.mask = 0xffff
++					.mask = 0x00ff
+ 				},
+ 				{
+ 					.valid = 0,
+@@ -643,7 +644,7 @@ static int xgene_enet_cle_init(struct xgene_enet_pdata *pdata)
+ 				{
+ 					/* TCP DST Port */
+ 					.valid = 0,
+-					.next_packet_pointer = 256,
++					.next_packet_pointer = 258,
+ 					.jump_bw = JMP_FW,
+ 					.jump_rel = JMP_ABS,
+ 					.operation = EQT,
+diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_cle.h b/drivers/net/ethernet/apm/xgene/xgene_enet_cle.h
+index 049dbe8..6dee1a3 100644
+--- a/drivers/net/ethernet/apm/xgene/xgene_enet_cle.h
++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_cle.h
+@@ -83,6 +83,8 @@
+ #define CLE_TYPE_POS		0
+ #define CLE_TYPE_LEN		2
+ 
++#define CLE_DROP_POS		28
++#define CLE_DROP_LEN		1
+ #define CLE_DSTQIDL_POS		25
+ #define CLE_DSTQIDL_LEN		7
+ #define CLE_DSTQIDH_POS		0
+-- 
+2.6.0
+
+From 42903c9b5bf23b1b760b29da2700a602dbb31ef7 Mon Sep 17 00:00:00 2001
+From: Iyappan Subramanian <isubramanian@apm.com>
+Date: Fri, 13 May 2016 16:52:58 -0700
+Subject: [PATCH 18/21] drivers: net: xgene: fix sharing of irqs
+
+Since hardware doesn't allow sharing of interrupts,
+this patch fixes the same by removing IRQF_SHARED flag.
+
+Signed-off-by: Iyappan Subramanian <isubramanian@apm.com>
+Tested-by: Toan Le <toanle@apm.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+(cherry picked from commit 46a22d29a51e40704c2260168c124e260b7d660e)
+Signed-off-by: Phong Vo <pvo@apm.com>
+---
+ drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
+index 9986db8..25fc87d 100644
+--- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
+@@ -619,7 +619,7 @@ static int xgene_enet_register_irq(struct net_device *ndev)
+ 		ring = pdata->rx_ring[i];
+ 		irq_set_status_flags(ring->irq, IRQ_DISABLE_UNLAZY);
+ 		ret = devm_request_irq(dev, ring->irq, xgene_enet_rx_irq,
+-				       IRQF_SHARED, ring->irq_name, ring);
++				       0, ring->irq_name, ring);
+ 		if (ret) {
+ 			netdev_err(ndev, "Failed to request irq %s\n",
+ 				   ring->irq_name);
+@@ -630,7 +630,7 @@ static int xgene_enet_register_irq(struct net_device *ndev)
+ 		ring = pdata->tx_ring[i]->cp_ring;
+ 		irq_set_status_flags(ring->irq, IRQ_DISABLE_UNLAZY);
+ 		ret = devm_request_irq(dev, ring->irq, xgene_enet_rx_irq,
+-				       IRQF_SHARED, ring->irq_name, ring);
++				       0, ring->irq_name, ring);
+ 		if (ret) {
+ 			netdev_err(ndev, "Failed to request irq %s\n",
+ 				   ring->irq_name);
+-- 
+2.6.0
+
+From a5c0532a665cc10d3f7dd89fdc488a3fdcdc383d Mon Sep 17 00:00:00 2001
+From: Iyappan Subramanian <isubramanian@apm.com>
+Date: Fri, 13 May 2016 16:52:59 -0700
+Subject: [PATCH 19/21] drivers: net: xgene: fix ununiform latency across
+ queues
+
+This patch addresses ununiform latency across queues by adding
+more queues to match with, upto number of CPU cores.
+
+Also, number of interrupts are increased and the channel numbers
+are reordered.
+
+Signed-off-by: Iyappan Subramanian <isubramanian@apm.com>
+Tested-by: Toan Le <toanle@apm.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+(cherry picked from commit 1b090a4839f6c74af2dc42872c9e9dc9d7c60a99)
+Signed-off-by: Phong Vo <pvo@apm.com>
+---
+ drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 30 ++++++++++++++++++------
+ drivers/net/ethernet/apm/xgene/xgene_enet_main.h |  8 +++----
+ 2 files changed, 27 insertions(+), 11 deletions(-)
+
+diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
+index 25fc87d..9dd1328 100644
+--- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
+@@ -1276,6 +1276,13 @@ static int xgene_enet_get_irqs(struct xgene_enet_pdata *pdata)
+ 	for (i = 0; i < max_irqs; i++) {
+ 		ret = platform_get_irq(pdev, i);
+ 		if (ret <= 0) {
++			if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII) {
++				max_irqs = i;
++				pdata->rxq_cnt = max_irqs / 2;
++				pdata->txq_cnt = max_irqs / 2;
++				pdata->cq_cnt = max_irqs / 2;
++				break;
++			}
+ 			dev_err(dev, "Unable to get ENET IRQ\n");
+ 			ret = ret ? : -ENXIO;
+ 			return ret;
+@@ -1482,19 +1489,28 @@ static void xgene_enet_setup_ops(struct xgene_enet_pdata *pdata)
+ 		pdata->port_ops = &xgene_xgport_ops;
+ 		pdata->cle_ops = &xgene_cle3in_ops;
+ 		pdata->rm = RM0;
+-		pdata->rxq_cnt = XGENE_NUM_RX_RING;
+-		pdata->txq_cnt = XGENE_NUM_TX_RING;
+-		pdata->cq_cnt = XGENE_NUM_TXC_RING;
++		if (!pdata->rxq_cnt) {
++			pdata->rxq_cnt = XGENE_NUM_RX_RING;
++			pdata->txq_cnt = XGENE_NUM_TX_RING;
++			pdata->cq_cnt = XGENE_NUM_TXC_RING;
++		}
+ 		break;
+ 	}
+ 
+ 	if (pdata->enet_id == XGENE_ENET1) {
+ 		switch (pdata->port_id) {
+ 		case 0:
+-			pdata->cpu_bufnum = START_CPU_BUFNUM_0;
+-			pdata->eth_bufnum = START_ETH_BUFNUM_0;
+-			pdata->bp_bufnum = START_BP_BUFNUM_0;
+-			pdata->ring_num = START_RING_NUM_0;
++			if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII) {
++				pdata->cpu_bufnum = X2_START_CPU_BUFNUM_0;
++				pdata->eth_bufnum = X2_START_ETH_BUFNUM_0;
++				pdata->bp_bufnum = X2_START_BP_BUFNUM_0;
++				pdata->ring_num = START_RING_NUM_0;
++			} else {
++				pdata->cpu_bufnum = START_CPU_BUFNUM_0;
++				pdata->eth_bufnum = START_ETH_BUFNUM_0;
++				pdata->bp_bufnum = START_BP_BUFNUM_0;
++				pdata->ring_num = START_RING_NUM_0;
++			}
+ 			break;
+ 		case 1:
+ 			if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII) {
+diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h
+index 2535167..09f8344 100644
+--- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h
++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h
+@@ -49,10 +49,10 @@
+ #define XGENE_ENET_MSS	1448
+ #define XGENE_MIN_ENET_FRAME_SIZE	60
+ 
+-#define XGENE_MAX_ENET_IRQ	8
+-#define XGENE_NUM_RX_RING	4
+-#define XGENE_NUM_TX_RING	4
+-#define XGENE_NUM_TXC_RING	4
++#define XGENE_MAX_ENET_IRQ	16
++#define XGENE_NUM_RX_RING	8
++#define XGENE_NUM_TX_RING	8
++#define XGENE_NUM_TXC_RING	8
+ 
+ #define START_CPU_BUFNUM_0	0
+ #define START_ETH_BUFNUM_0	2
+-- 
+2.6.0
+
+From f8ce9a085ed243a2585c043483b54d6b92f1e9de Mon Sep 17 00:00:00 2001
+From: Iyappan Subramanian <isubramanian@apm.com>
+Date: Fri, 13 May 2016 16:53:00 -0700
+Subject: [PATCH 20/21] drivers: net: xgene: fix statistics counters race
+ condition
+
+This patch fixes the race condition on updating the statistics
+counters by moving the counters to the ring structure.
+
+Signed-off-by: Iyappan Subramanian <isubramanian@apm.com>
+Tested-by: Toan Le <toanle@apm.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+(cherry picked from commit 3bb502f83080ad28abdd7404f29aa2fc743b28b5)
+Signed-off-by: Phong Vo <pvo@apm.com>
+---
+ drivers/net/ethernet/apm/xgene/xgene_enet_hw.c   | 19 ++++++-----
+ drivers/net/ethernet/apm/xgene/xgene_enet_hw.h   |  2 ++
+ drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 41 +++++++++++++++++-------
+ drivers/net/ethernet/apm/xgene/xgene_enet_main.h | 10 ++++++
+ 4 files changed, 53 insertions(+), 19 deletions(-)
+
+diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
+index ede0c75..8a8ae6c 100644
+--- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
+@@ -219,27 +219,30 @@ void xgene_enet_parse_error(struct xgene_enet_desc_ring *ring,
+ 			    struct xgene_enet_pdata *pdata,
+ 			    enum xgene_enet_err_code status)
+ {
+-	struct rtnl_link_stats64 *stats = &pdata->stats;
+-
+ 	switch (status) {
+ 	case INGRESS_CRC:
+-		stats->rx_crc_errors++;
++		ring->rx_crc_errors++;
++		ring->rx_dropped++;
+ 		break;
+ 	case INGRESS_CHECKSUM:
+ 	case INGRESS_CHECKSUM_COMPUTE:
+-		stats->rx_errors++;
++		ring->rx_errors++;
++		ring->rx_dropped++;
+ 		break;
+ 	case INGRESS_TRUNC_FRAME:
+-		stats->rx_frame_errors++;
++		ring->rx_frame_errors++;
++		ring->rx_dropped++;
+ 		break;
+ 	case INGRESS_PKT_LEN:
+-		stats->rx_length_errors++;
++		ring->rx_length_errors++;
++		ring->rx_dropped++;
+ 		break;
+ 	case INGRESS_PKT_UNDER:
+-		stats->rx_frame_errors++;
++		ring->rx_frame_errors++;
++		ring->rx_dropped++;
+ 		break;
+ 	case INGRESS_FIFO_OVERRUN:
+-		stats->rx_fifo_errors++;
++		ring->rx_fifo_errors++;
+ 		break;
+ 	default:
+ 		break;
+diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h
+index 40fcf90..f6c994e 100644
+--- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h
++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h
+@@ -208,6 +208,8 @@ enum xgene_enet_rm {
+ #define USERINFO_LEN			32
+ #define FPQNUM_POS			32
+ #define FPQNUM_LEN			12
++#define ELERR_POS                       46
++#define ELERR_LEN                       2
+ #define NV_POS				50
+ #define NV_LEN				1
+ #define LL_POS				51
+diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
+index 9dd1328..5e965a3 100644
+--- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
+@@ -431,8 +431,8 @@ static netdev_tx_t xgene_enet_start_xmit(struct sk_buff *skb,
+ 
+ 	skb_tx_timestamp(skb);
+ 
+-	pdata->stats.tx_packets++;
+-	pdata->stats.tx_bytes += skb->len;
++	tx_ring->tx_packets++;
++	tx_ring->tx_bytes += skb->len;
+ 
+ 	pdata->ring_ops->wr_cmd(tx_ring, count);
+ 	return NETDEV_TX_OK;
+@@ -472,12 +472,12 @@ static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring,
+ 	buf_pool->rx_skb[skb_index] = NULL;
+ 
+ 	/* checking for error */
+-	status = GET_VAL(LERR, le64_to_cpu(raw_desc->m0));
++	status = (GET_VAL(ELERR, le64_to_cpu(raw_desc->m0)) << LERR_LEN) ||
++		  GET_VAL(LERR, le64_to_cpu(raw_desc->m0));
+ 	if (unlikely(status > 2)) {
+ 		dev_kfree_skb_any(skb);
+ 		xgene_enet_parse_error(rx_ring, netdev_priv(rx_ring->ndev),
+ 				       status);
+-		pdata->stats.rx_dropped++;
+ 		ret = -EIO;
+ 		goto out;
+ 	}
+@@ -495,8 +495,8 @@ static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring,
+ 		xgene_enet_skip_csum(skb);
+ 	}
+ 
+-	pdata->stats.rx_packets++;
+-	pdata->stats.rx_bytes += datalen;
++	rx_ring->rx_packets++;
++	rx_ring->rx_bytes += datalen;
+ 	napi_gro_receive(&rx_ring->napi, skb);
+ out:
+ 	if (--rx_ring->nbufpool == 0) {
+@@ -1116,12 +1116,31 @@ static struct rtnl_link_stats64 *xgene_enet_get_stats64(
+ {
+ 	struct xgene_enet_pdata *pdata = netdev_priv(ndev);
+ 	struct rtnl_link_stats64 *stats = &pdata->stats;
++	struct xgene_enet_desc_ring *ring;
++	int i;
++
++	memset(stats, 0, sizeof(struct rtnl_link_stats64));
++	for (i = 0; i < pdata->txq_cnt; i++) {
++		ring = pdata->tx_ring[i];
++		if (ring) {
++			stats->tx_packets += ring->tx_packets;
++			stats->tx_bytes += ring->tx_bytes;
++		}
++	}
+ 
+-	stats->rx_errors += stats->rx_length_errors +
+-			    stats->rx_crc_errors +
+-			    stats->rx_frame_errors +
+-			    stats->rx_fifo_errors;
+-	memcpy(storage, &pdata->stats, sizeof(struct rtnl_link_stats64));
++	for (i = 0; i < pdata->rxq_cnt; i++) {
++		ring = pdata->rx_ring[i];
++		if (ring) {
++			stats->rx_packets += ring->rx_packets;
++			stats->rx_bytes += ring->rx_bytes;
++			stats->rx_errors += ring->rx_length_errors +
++				ring->rx_crc_errors +
++				ring->rx_frame_errors +
++				ring->rx_fifo_errors;
++			stats->rx_dropped += ring->rx_dropped;
++		}
++	}
++	memcpy(storage, stats, sizeof(struct rtnl_link_stats64));
+ 
+ 	return storage;
+ }
+diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h
+index 09f8344..44420a1 100644
+--- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h
++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h
+@@ -121,6 +121,16 @@ struct xgene_enet_desc_ring {
+ 		struct xgene_enet_raw_desc16 *raw_desc16;
+ 	};
+ 	__le64 *exp_bufs;
++	u64 tx_packets;
++	u64 tx_bytes;
++	u64 rx_packets;
++	u64 rx_bytes;
++	u64 rx_dropped;
++	u64 rx_errors;
++	u64 rx_length_errors;
++	u64 rx_crc_errors;
++	u64 rx_frame_errors;
++	u64 rx_fifo_errors;
+ };
+ 
+ struct xgene_mac_ops {
+-- 
+2.6.0
+
+From adca53f272e102704b313f0fae78d3e38fbc1dbf Mon Sep 17 00:00:00 2001
+From: Iyappan Subramanian <isubramanian@apm.com>
+Date: Fri, 13 May 2016 16:53:01 -0700
+Subject: [PATCH 21/21] drivers: net: xgene: fix register offset
+
+This patch fixes SG_RX_DV_GATE_REG_0_ADDR register offset
+and ring state field lengths.
+
+Signed-off-by: Iyappan Subramanian <isubramanian@apm.com>
+Tested-by: Toan Le <toanle@apm.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+(cherry picked from commit e2f2d9a72605666ec772c865dbe89edbab896d27)
+Signed-off-by: Phong Vo <pvo@apm.com>
+---
+ drivers/net/ethernet/apm/xgene/xgene_enet_hw.h    | 6 +++---
+ drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.h | 2 +-
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h
+index f6c994e..d9a2bf7 100644
+--- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h
++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h
+@@ -86,7 +86,7 @@ enum xgene_enet_rm {
+ #define RINGADDRL_POS		5
+ #define RINGADDRL_LEN		27
+ #define RINGADDRH_POS		0
+-#define RINGADDRH_LEN		6
++#define RINGADDRH_LEN		7
+ #define RINGSIZE_POS		23
+ #define RINGSIZE_LEN		3
+ #define RINGTYPE_POS		19
+@@ -94,9 +94,9 @@ enum xgene_enet_rm {
+ #define RINGMODE_POS		20
+ #define RINGMODE_LEN		3
+ #define RECOMTIMEOUTL_POS	28
+-#define RECOMTIMEOUTL_LEN	3
++#define RECOMTIMEOUTL_LEN	4
+ #define RECOMTIMEOUTH_POS	0
+-#define RECOMTIMEOUTH_LEN	2
++#define RECOMTIMEOUTH_LEN	3
+ #define NUMMSGSINQ_POS		1
+ #define NUMMSGSINQ_LEN		16
+ #define ACCEPTLERR		BIT(19)
+diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.h b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.h
+index de43246..eab54f6 100644
+--- a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.h
++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.h
+@@ -33,7 +33,7 @@
+ #define LINK_STATUS			BIT(2)
+ #define LINK_UP				BIT(15)
+ #define MPA_IDLE_WITH_QMI_EMPTY		BIT(12)
+-#define SG_RX_DV_GATE_REG_0_ADDR	0x0dfc
++#define SG_RX_DV_GATE_REG_0_ADDR	0x05fc
+ 
+ extern struct xgene_mac_ops xgene_sgmac_ops;
+ extern struct xgene_port_ops xgene_sgport_ops;
+-- 
+2.6.0
+
diff --git a/SPECS/kernel-aarch64.spec b/SPECS/kernel-aarch64.spec
index 5ccdded..391c0ce 100644
--- a/SPECS/kernel-aarch64.spec
+++ b/SPECS/kernel-aarch64.spec
@@ -337,6 +337,12 @@ Patch1009: 0009-Add-X-Gene-ACPI-I2C-device-support.patch
 Patch1010: 0010-drivers-rtc-add-ACPI-support-for-ds1307-RTC-driver.patch
 Patch1011: 0011-drivers-net-xgene-fix-tx-flow-and-extra-irq.patch
 Patch1012: 0012-drivers-net-xgene-classifier-rss-support.patch
+Patch1013: 0013-workqueue-Make-flush_workqueue-available-again-to-no.patch
+Patch1014: 0014-drivers-irqchip-update-irq-gic-v2m.c.patch
+Patch1015: 0015-ahci_xgene-Implement-the-workaround-to-fix-the-missi.patch
+Patch1016: 0016-Revert-ext4-remove-block_device_ejected.patch
+Patch1017: 0017-drivers-net-xgene-upstream-bugfixes.patch
+
 
 # Security patches outside redhat
 Patch2000: KEYS-Fix-keyring-ref-leak-in-join_session_keyring.patch
@@ -696,6 +702,11 @@ git am %{PATCH1009}
 git am %{PATCH1010}
 git am %{PATCH1011}
 git am %{PATCH1012}
+git am %{PATCH1013}
+git am %{PATCH1014}
+git am %{PATCH1015}
+git am %{PATCH1016}
+git am %{PATCH1017}
 
 # apply security patch for cve-2016-0728
 
-- 
2.6.0

