From 99990c92d47d2a9485906a9bef7125269344a2b9 Mon Sep 17 00:00:00 2001
From: Xiang Chen <chenxiang66@hisilicon.com>
Date: Wed, 6 Feb 2019 18:52:55 +0800
Subject: [PATCH 004/108] scsi: hisi_sas: Use pci_irq_get_affinity() for v3 hw
 as experimental

mainline inclusion
from mainline-v5.1-rc1
commit 4fefe5bbf599d6c6bee6b2ee376be789b33ca571
category: feature
bugzilla: https://gitee.com/src-openeuler/kernel/issues/I8EKNE

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4fefe5bbf599d6c6bee6b2ee376be789b33ca571

----------------------------------------------------------------------

For auto-control irq affinity mode, choose the dq to deliver IO according
to the current CPU.

Then it decreases the performance regression that fio and CQ interrupts are
processed on different node.

For user control irq affinity mode, keep it as before.

To realize it, also need to distinguish the usage of dq lock and sas_dev
lock.

We mark as experimental due to ongoing discussion on managed MSI IRQ
during hotplug:
https://marc.info/?l=linux-scsi&m=154876335707751&w=2

We're almost at the point where we can expose multiple queues to the upper
layer for SCSI MQ, but we need to sort out the per-HBA tags performance
issue.

Signed-off-by: Xiang Chen <chenxiang66@hisilicon.com>
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: YunYi Yang <yangyunyi2@huawei.com>

 Conflicts:
	drivers/scsi/hisi_sas/hisi_sas.h
	drivers/scsi/hisi_sas/hisi_sas_main.c
	drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
---
 drivers/scsi/hisi_sas/hisi_sas.h       |  4 +-
 drivers/scsi/hisi_sas/hisi_sas_main.c  | 20 ++++---
 drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 74 +++++++++++++++++++-------
 3 files changed, 68 insertions(+), 30 deletions(-)

diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h
index daa64e5aff3e..06a8bf9f0f73 100644
--- a/drivers/scsi/hisi_sas/hisi_sas.h
+++ b/drivers/scsi/hisi_sas/hisi_sas.h
@@ -192,6 +192,7 @@ struct hisi_sas_port {
 
 struct hisi_sas_cq {
 	struct hisi_hba *hisi_hba;
+	const struct cpumask *pci_irq_mask;
 	struct tasklet_struct tasklet;
 	int	rd_point;
 	int	id;
@@ -214,8 +215,8 @@ struct hisi_sas_device {
 	enum sas_device_type	dev_type;
 	unsigned int device_id;
 	int sata_idx;
-	spinlock_t lock;
 	enum dev_status dev_status;
+	spinlock_t lock; /* For protecting slots */
 };
 
 struct hisi_sas_tmf_task {
@@ -420,6 +421,7 @@ struct hisi_hba {
 	int bist_loopback_enable;
 
 	int enable_dix_dif;
+	unsigned int *reply_map;
 
 	/* debugfs memories */
 	u32 *debugfs_global_reg;
diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c
index 852ece4af0f4..7ccdaaba311c 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_main.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_main.c
@@ -456,9 +456,6 @@ static int hisi_sas_task_prep(struct sas_task *task,
 	unsigned long flags;
 	int wr_q_index;
 	unsigned int curr_node_id = numa_node_id();
-	unsigned int dq_index =
-		(hisi_hba->dq_idx[curr_node_id] % hisi_hba->dq_num_per_node) +
-		(hisi_hba->dq_num_per_node * curr_node_id);
 
 	if (DEV_IS_GONE(sas_dev)) {
 		if (sas_dev)
@@ -471,10 +468,14 @@ static int hisi_sas_task_prep(struct sas_task *task,
 		return -ECOMM;
 	}
 
-	if (hisi_hba->user_ctl_irq)
-		*dq_pointer = dq = sas_dev->dq;
-	else
+	if (hisi_hba->reply_map) {
+		int cpu = raw_smp_processor_id();
+		unsigned int dq_index = hisi_hba->reply_map[cpu];
+
 		*dq_pointer = dq = &hisi_hba->dq[dq_index];
+	} else {
+		*dq_pointer = dq = sas_dev->dq;
+	}
 
 	port = to_hisi_sas_port(sas_port);
 	if (port && !port->port_attached) {
@@ -2207,11 +2208,8 @@ hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba,
 						     abort_flag, tag, dq);
 	case HISI_SAS_INT_ABT_DEV:
 		for (i = 0; i < hisi_hba->nvecs; i++) {
-			const struct cpumask *mask = NULL;
-
-			if (hisi_hba->hw->get_managed_irq_aff)
-				mask = hisi_hba->hw->get_managed_irq_aff(
-						hisi_hba, i);
+			struct hisi_sas_cq *cq = &hisi_hba->cq[i];
+			const struct cpumask *mask = cq->pci_irq_mask;
 			/*
 			 * The kernel will not permit unmanaged (MSI are
 			 * managed) IRQ affinity to offline CPUs, so
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
index cfa9756fb995..4635c3b161db 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
@@ -556,6 +556,11 @@ module_param(user_ctl_irq, bool, 0444);
 MODULE_PARM_DESC(user_ctl_irq, "Enable user control irq affinity:\n"
 			       "default is auto-control irq affinity");
 
+static bool auto_affine_msi_experimental;
+module_param(auto_affine_msi_experimental, bool, 0444);
+MODULE_PARM_DESC(auto_affine_msi_experimental, "Enable auto-affinity of MSI IRQs as experimental:\n"
+		 "default is off");
+
 static u32 hisi_sas_read32(struct hisi_hba *hisi_hba, u32 off)
 {
 	void __iomem *regs = hisi_hba->regs + off;
@@ -2677,33 +2682,66 @@ static irqreturn_t cq_interrupt_v3_hw(int irq_no, void *p)
 	return IRQ_HANDLED;
 }
 
+static void setup_reply_map_v3_hw(struct hisi_hba *hisi_hba, int nvecs)
+{
+	const struct cpumask *mask;
+	int queue, cpu;
+
+	for (queue = 0; queue < nvecs; queue++) {
+		struct hisi_sas_cq *cq = &hisi_hba->cq[queue];
+
+		mask = pci_irq_get_affinity(hisi_hba->pci_dev, queue +
+					    HISI_SAS_CQ_INT_BASE_VECTORS_V3_HW);
+		if (!mask)
+			goto fallback;
+		cq->pci_irq_mask = mask;
+		for_each_cpu(cpu, mask)
+			hisi_hba->reply_map[cpu] = queue;
+	}
+	return;
+
+fallback:
+	for_each_possible_cpu(cpu)
+		hisi_hba->reply_map[cpu] = cpu % hisi_hba->queue_count;
+	/* Don't clean all CQ masks */
+}
+
 static int interrupt_init_v3_hw(struct hisi_hba *hisi_hba)
 {
 	struct device *dev = hisi_hba->dev;
 	struct pci_dev *pdev = hisi_hba->pci_dev;
 	int vectors, rc;
 	int i, k;
-	int max_msi = HISI_SAS_MSI_COUNT_V3_HW;
 	int max_dq_num, online_numa_num;
-	struct irq_affinity desc = {
-		.pre_vectors = HISI_SAS_CQ_INT_BASE_VECTORS_V3_HW,
-	};
+	int max_msi = HISI_SAS_MSI_COUNT_V3_HW, min_msi;
 
-	if (user_ctl_irq) {
-		vectors = pci_alloc_irq_vectors(hisi_hba->pci_dev, 1,
-						max_msi, PCI_IRQ_MSI);
-	} else {
-		vectors = pci_alloc_irq_vectors_affinity(hisi_hba->pci_dev,
-				HISI_SAS_MIN_VECTORS_V3_HW,
-				max_msi,
-				PCI_IRQ_MSI |
-				PCI_IRQ_AFFINITY,
-				&desc);
-	}
+	if (auto_affine_msi_experimental) {
+		struct irq_affinity desc = {
+			.pre_vectors = HISI_SAS_CQ_INT_BASE_VECTORS_V3_HW,
+		};
 
-	if (vectors < HISI_SAS_MIN_VECTORS_V3_HW) {
-		dev_err(dev, "allocate msi (%d) not enough\n", vectors);
-		return -ENOENT;
+		min_msi = HISI_SAS_MIN_VECTORS_V3_HW;
+
+		hisi_hba->reply_map = devm_kcalloc(dev, nr_cpu_ids,
+						   sizeof(unsigned int),
+						   GFP_KERNEL);
+		if (!hisi_hba->reply_map)
+			return -ENOMEM;
+		vectors = pci_alloc_irq_vectors_affinity(hisi_hba->pci_dev,
+							 min_msi, max_msi,
+							 PCI_IRQ_MSI |
+							 PCI_IRQ_AFFINITY,
+							 &desc);
+		if (vectors < 0)
+			return -ENOENT;
+		setup_reply_map_v3_hw(hisi_hba,
+				      vectors - HISI_SAS_CQ_INT_BASE_VECTORS_V3_HW);
+	} else {
+		min_msi = max_msi;
+		vectors = pci_alloc_irq_vectors(hisi_hba->pci_dev, min_msi,
+						max_msi, PCI_IRQ_MSI);
+		if (vectors < 0)
+			return vectors;
 	}
 
 	hisi_hba->nvecs = vectors - HISI_SAS_CQ_INT_BASE_VECTORS_V3_HW;
-- 
2.27.0

