Commit 99e0cd4d authored by Martin K. Petersen's avatar Martin K. Petersen
Browse files

Merge patch series "Add poll support for hisi_sas v3 hw"



chenxiang <chenxiang66@hisilicon.com> says:

To support IO_URING IOPOLL support for hisi_sas, we need to:

 - Add and fill mq_poll interface to poll queue;

 - Ensure internal I/Os (including internal abort I/Os) are delivered and
   completed through non-iopoll queue (queue 0);

Sending internal abort commands to non-poll queue actually requires to
sending the abort command to every queue. This carries a a risk. Make iopoll
support module parameter "experimental".

I have tested performance on v3 hw with different modes as follows.  4K
READs and 4K WRITEs both see an improvement when enabling poll mode:

			4K READ	    4K RANDREAD	    4K WRITE	4K RANDWRITE
interrupt + libaio	1770k	    1316k	    1197k	831k
interrupt + io_uring	1848k	    1390k	    1238k	857k
iopoll + io_uring	2117k	    1364k	    1874k	849k

Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parents 7466a7a3 0c2fb170
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -207,6 +207,7 @@ struct hisi_sas_cq {
	int	rd_point;
	int	id;
	int	irq_no;
	spinlock_t poll_lock;
};

struct hisi_sas_dq {
@@ -484,6 +485,8 @@ struct hisi_hba {
	struct dentry *debugfs_dump_dentry;
	struct dentry *debugfs_bist_dentry;
	struct dentry *debugfs_fifo_dentry;

	int iopoll_q_cnt;
};

/* Generic HW DMA host memory structures */
@@ -657,12 +660,13 @@ extern void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba,
extern void hisi_sas_init_mem(struct hisi_hba *hisi_hba);
extern void hisi_sas_rst_work_handler(struct work_struct *work);
extern void hisi_sas_sync_rst_work_handler(struct work_struct *work);
extern void hisi_sas_sync_irqs(struct hisi_hba *hisi_hba);
extern void hisi_sas_phy_oob_ready(struct hisi_hba *hisi_hba, int phy_no);
extern bool hisi_sas_notify_phy_event(struct hisi_sas_phy *phy,
				enum hisi_sas_phy_event event);
extern void hisi_sas_release_tasks(struct hisi_hba *hisi_hba);
extern u8 hisi_sas_get_prog_phy_linkrate_mask(enum sas_linkrate max);
extern void hisi_sas_sync_cqs(struct hisi_hba *hisi_hba);
extern void hisi_sas_sync_poll_cqs(struct hisi_hba *hisi_hba);
extern void hisi_sas_controller_reset_prepare(struct hisi_hba *hisi_hba);
extern void hisi_sas_controller_reset_done(struct hisi_hba *hisi_hba);
#endif
+72 −23
Original line number Diff line number Diff line
@@ -528,11 +528,22 @@ static int hisi_sas_queue_command(struct sas_task *task, gfp_t gfp_flags)
			blk_tag = blk_mq_unique_tag(rq);
			dq_index = blk_mq_unique_tag_to_hwq(blk_tag);
			dq = &hisi_hba->dq[dq_index];
		} else {
			int queue;

			if (hisi_hba->iopoll_q_cnt) {
				/*
				 * Use interrupt queue (queue 0) to deliver and complete
				 * internal IOs of libsas or libata when there is at least
				 * one iopoll queue
				 */
				queue = 0;
			} else {
				struct Scsi_Host *shost = hisi_hba->shost;
				struct blk_mq_queue_map *qmap = &shost->tag_set.map[HCTX_TYPE_DEFAULT];
			int queue = qmap->mq_map[raw_smp_processor_id()];

				queue = qmap->mq_map[raw_smp_processor_id()];
			}
			dq = &hisi_hba->dq[queue];
		}
		break;
@@ -672,6 +683,55 @@ static struct hisi_sas_device *hisi_sas_alloc_dev(struct domain_device *device)
	return sas_dev;
}

static void hisi_sas_sync_poll_cq(struct hisi_sas_cq *cq)
{
	/* make sure CQ entries being processed are processed to completion */
	spin_lock(&cq->poll_lock);
	spin_unlock(&cq->poll_lock);
}

static bool hisi_sas_queue_is_poll(struct hisi_sas_cq *cq)
{
	struct hisi_hba *hisi_hba = cq->hisi_hba;

	if (cq->id < hisi_hba->queue_count - hisi_hba->iopoll_q_cnt)
		return false;
	return true;
}

static void hisi_sas_sync_cq(struct hisi_sas_cq *cq)
{
	if (hisi_sas_queue_is_poll(cq))
		hisi_sas_sync_poll_cq(cq);
	else
		synchronize_irq(cq->irq_no);
}

void hisi_sas_sync_poll_cqs(struct hisi_hba *hisi_hba)
{
	int i;

	for (i = 0; i < hisi_hba->queue_count; i++) {
		struct hisi_sas_cq *cq = &hisi_hba->cq[i];

		if (hisi_sas_queue_is_poll(cq))
			hisi_sas_sync_poll_cq(cq);
	}
}
EXPORT_SYMBOL_GPL(hisi_sas_sync_poll_cqs);

void hisi_sas_sync_cqs(struct hisi_hba *hisi_hba)
{
	int i;

	for (i = 0; i < hisi_hba->queue_count; i++) {
		struct hisi_sas_cq *cq = &hisi_hba->cq[i];

		hisi_sas_sync_cq(cq);
	}
}
EXPORT_SYMBOL_GPL(hisi_sas_sync_cqs);

static void hisi_sas_tmf_aborted(struct sas_task *task)
{
	struct hisi_sas_slot *slot = task->lldd_task;
@@ -683,10 +743,10 @@ static void hisi_sas_tmf_aborted(struct sas_task *task)
		struct hisi_sas_cq *cq =
			   &hisi_hba->cq[slot->dlvry_queue];
		/*
		 * sync irq to avoid free'ing task
		 * sync irq or poll queue to avoid free'ing task
		 * before using task in IO completion
		 */
		synchronize_irq(cq->irq_no);
		hisi_sas_sync_cq(cq);
		slot->task = NULL;
	}
}
@@ -1540,11 +1600,11 @@ static int hisi_sas_abort_task(struct sas_task *task)

		if (slot) {
			/*
			 * sync irq to avoid free'ing task
			 * sync irq or poll queue to avoid free'ing task
			 * before using task in IO completion
			 */
			cq = &hisi_hba->cq[slot->dlvry_queue];
			synchronize_irq(cq->irq_no);
			hisi_sas_sync_cq(cq);
		}
		spin_unlock_irqrestore(&task->task_state_lock, flags);
		rc = TMF_RESP_FUNC_COMPLETE;
@@ -1611,10 +1671,10 @@ static int hisi_sas_abort_task(struct sas_task *task)
		if (((rc < 0) || (rc == TMF_RESP_FUNC_FAILED)) &&
					task->lldd_task) {
			/*
			 * sync irq to avoid free'ing task
			 * sync irq or poll queue to avoid free'ing task
			 * before using task in IO completion
			 */
			synchronize_irq(cq->irq_no);
			hisi_sas_sync_cq(cq);
			slot->task = NULL;
		}
	}
@@ -1885,10 +1945,10 @@ static bool hisi_sas_internal_abort_timeout(struct sas_task *task,
			struct hisi_sas_cq *cq =
				&hisi_hba->cq[slot->dlvry_queue];
			/*
			 * sync irq to avoid free'ing task
			 * sync irq or poll queue to avoid free'ing task
			 * before using task in IO completion
			 */
			synchronize_irq(cq->irq_no);
			hisi_sas_sync_cq(cq);
			slot->task = NULL;
		}

@@ -1992,18 +2052,6 @@ void hisi_sas_phy_bcast(struct hisi_sas_phy *phy)
}
EXPORT_SYMBOL_GPL(hisi_sas_phy_bcast);

void hisi_sas_sync_irqs(struct hisi_hba *hisi_hba)
{
	int i;

	for (i = 0; i < hisi_hba->cq_nvecs; i++) {
		struct hisi_sas_cq *cq = &hisi_hba->cq[i];

		synchronize_irq(cq->irq_no);
	}
}
EXPORT_SYMBOL_GPL(hisi_sas_sync_irqs);

int hisi_sas_host_reset(struct Scsi_Host *shost, int reset_type)
{
	struct hisi_hba *hisi_hba = shost_priv(shost);
@@ -2101,6 +2149,7 @@ int hisi_sas_alloc(struct hisi_hba *hisi_hba)
		/* Completion queue structure */
		cq->id = i;
		cq->hisi_hba = hisi_hba;
		spin_lock_init(&cq->poll_lock);

		/* Delivery queue structure */
		spin_lock_init(&dq->lock);
+90 −12
Original line number Diff line number Diff line
@@ -552,6 +552,11 @@ static int prot_mask;
module_param(prot_mask, int, 0444);
MODULE_PARM_DESC(prot_mask, " host protection capabilities mask, def=0x0 ");

/* the index of iopoll queues are bigger than interrupt queues' */
static int experimental_iopoll_q_cnt;
module_param(experimental_iopoll_q_cnt, int, 0444);
MODULE_PARM_DESC(experimental_iopoll_q_cnt, "number of queues to be used as poll mode, def=0");

static void debugfs_work_handler_v3_hw(struct work_struct *work);
static void debugfs_snapshot_regs_v3_hw(struct hisi_hba *hisi_hba);

@@ -2391,23 +2396,25 @@ static void slot_complete_v3_hw(struct hisi_hba *hisi_hba,
		task->task_done(task);
}

static irqreturn_t  cq_thread_v3_hw(int irq_no, void *p)
static int complete_v3_hw(struct hisi_sas_cq *cq)
{
	struct hisi_sas_cq *cq = p;
	struct hisi_hba *hisi_hba = cq->hisi_hba;
	struct hisi_sas_slot *slot;
	struct hisi_sas_complete_v3_hdr *complete_queue;
	u32 rd_point = cq->rd_point, wr_point;
	struct hisi_hba *hisi_hba = cq->hisi_hba;
	u32 rd_point, wr_point;
	int queue = cq->id;
	int completed;

	rd_point = cq->rd_point;
	complete_queue = hisi_hba->complete_hdr[queue];

	wr_point = hisi_sas_read32(hisi_hba, COMPL_Q_0_WR_PTR +
				   (0x14 * queue));
	completed = (wr_point + HISI_SAS_QUEUE_SLOTS - rd_point) % HISI_SAS_QUEUE_SLOTS;

	while (rd_point != wr_point) {
		struct hisi_sas_complete_v3_hdr *complete_hdr;
		struct device *dev = hisi_hba->dev;
		struct hisi_sas_slot *slot;
		u32 dw0, dw1, dw3;
		int iptt;

@@ -2451,6 +2458,28 @@ static irqreturn_t cq_thread_v3_hw(int irq_no, void *p)
	cq->rd_point = rd_point;
	hisi_sas_write32(hisi_hba, COMPL_Q_0_RD_PTR + (0x14 * queue), rd_point);

	return completed;
}

static int queue_complete_v3_hw(struct Scsi_Host *shost, unsigned int queue)
{
	struct hisi_hba *hisi_hba = shost_priv(shost);
	struct hisi_sas_cq *cq = &hisi_hba->cq[queue];
	int completed;

	spin_lock(&cq->poll_lock);
	completed = complete_v3_hw(cq);
	spin_unlock(&cq->poll_lock);

	return completed;
}

static irqreturn_t cq_thread_v3_hw(int irq_no, void *p)
{
	struct hisi_sas_cq *cq = p;

	complete_v3_hw(cq);

	return IRQ_HANDLED;
}

@@ -2474,8 +2503,9 @@ static void hisi_sas_v3_free_vectors(void *data)

static int interrupt_preinit_v3_hw(struct hisi_hba *hisi_hba)
{
	int vectors;
	int max_msi = HISI_SAS_MSI_COUNT_V3_HW, min_msi;
	/* Allocate all MSI vectors to avoid re-insertion issue */
	int max_msi = HISI_SAS_MSI_COUNT_V3_HW;
	int vectors, min_msi;
	struct Scsi_Host *shost = hisi_hba->shost;
	struct pci_dev *pdev = hisi_hba->pci_dev;
	struct irq_affinity desc = {
@@ -2492,8 +2522,8 @@ static int interrupt_preinit_v3_hw(struct hisi_hba *hisi_hba)
		return -ENOENT;


	hisi_hba->cq_nvecs = vectors - BASE_VECTORS_V3_HW;
	shost->nr_hw_queues = hisi_hba->cq_nvecs;
	hisi_hba->cq_nvecs = vectors - BASE_VECTORS_V3_HW - hisi_hba->iopoll_q_cnt;
	shost->nr_hw_queues = hisi_hba->cq_nvecs + hisi_hba->iopoll_q_cnt;

	devm_add_action(&pdev->dev, hisi_sas_v3_free_vectors, pdev);
	return 0;
@@ -2627,6 +2657,7 @@ static int disable_host_v3_hw(struct hisi_hba *hisi_hba)
	int rc;

	interrupt_disable_v3_hw(hisi_hba);
	hisi_sas_sync_poll_cqs(hisi_hba);
	hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE, 0x0);

	hisi_sas_stop_phys(hisi_hba);
@@ -2824,6 +2855,18 @@ static ssize_t intr_coal_count_v3_hw_store(struct device *dev,
}
static DEVICE_ATTR_RW(intr_coal_count_v3_hw);

static ssize_t iopoll_q_cnt_v3_hw_show(struct device *dev,
					  struct device_attribute
					  *attr, char *buf)
{
	struct Scsi_Host *shost = class_to_shost(dev);
	struct hisi_hba *hisi_hba = shost_priv(shost);

	return scnprintf(buf, PAGE_SIZE, "%u\n",
			 hisi_hba->iopoll_q_cnt);
}
static DEVICE_ATTR_RO(iopoll_q_cnt_v3_hw);

static int slave_configure_v3_hw(struct scsi_device *sdev)
{
	struct Scsi_Host *shost = dev_to_shost(&sdev->sdev_gendev);
@@ -2853,6 +2896,7 @@ static struct attribute *host_v3_hw_attrs[] = {
	&dev_attr_intr_conv_v3_hw.attr,
	&dev_attr_intr_coal_ticks_v3_hw.attr,
	&dev_attr_intr_coal_count_v3_hw.attr,
	&dev_attr_iopoll_q_cnt_v3_hw.attr,
	NULL
};

@@ -3039,7 +3083,7 @@ static void debugfs_snapshot_prepare_v3_hw(struct hisi_hba *hisi_hba)

	wait_cmds_complete_timeout_v3_hw(hisi_hba, 100, 5000);

	hisi_sas_sync_irqs(hisi_hba);
	hisi_sas_sync_cqs(hisi_hba);
}

static void debugfs_snapshot_restore_v3_hw(struct hisi_hba *hisi_hba)
@@ -3211,9 +3255,31 @@ static int debugfs_set_bist_v3_hw(struct hisi_hba *hisi_hba, bool enable)
static void hisi_sas_map_queues(struct Scsi_Host *shost)
{
	struct hisi_hba *hisi_hba = shost_priv(shost);
	struct blk_mq_queue_map *qmap = &shost->tag_set.map[HCTX_TYPE_DEFAULT];
	struct blk_mq_queue_map *qmap;
	int i, qoff;

	for (i = 0, qoff = 0; i < shost->nr_maps; i++) {
		qmap = &shost->tag_set.map[i];
		if (i == HCTX_TYPE_DEFAULT) {
			qmap->nr_queues = hisi_hba->cq_nvecs;
		} else if (i == HCTX_TYPE_POLL) {
			qmap->nr_queues = hisi_hba->iopoll_q_cnt;
		} else {
			qmap->nr_queues = 0;
			continue;
		}

	blk_mq_pci_map_queues(qmap, hisi_hba->pci_dev, BASE_VECTORS_V3_HW);
		/* At least one interrupt hardware queue */
		if (!qmap->nr_queues)
			WARN_ON(i == HCTX_TYPE_DEFAULT);
		qmap->queue_offset = qoff;
		if (i == HCTX_TYPE_POLL)
			blk_mq_map_queues(qmap);
		else
			blk_mq_pci_map_queues(qmap, hisi_hba->pci_dev,
					      BASE_VECTORS_V3_HW);
		qoff += qmap->nr_queues;
	}
}

static struct scsi_host_template sht_v3_hw = {
@@ -3245,6 +3311,7 @@ static struct scsi_host_template sht_v3_hw = {
	.tag_alloc_policy	= BLK_TAG_ALLOC_RR,
	.host_reset             = hisi_sas_host_reset,
	.host_tagset		= 1,
	.mq_poll		= queue_complete_v3_hw,
};

static const struct hisi_sas_hw hisi_sas_v3_hw = {
@@ -3304,6 +3371,13 @@ hisi_sas_shost_alloc_pci(struct pci_dev *pdev)
	if (hisi_sas_get_fw_info(hisi_hba) < 0)
		goto err_out;

	if (experimental_iopoll_q_cnt < 0 ||
		experimental_iopoll_q_cnt >= hisi_hba->queue_count)
		dev_err(dev, "iopoll queue count %d cannot exceed or equal 16, using default 0\n",
			experimental_iopoll_q_cnt);
	else
		hisi_hba->iopoll_q_cnt = experimental_iopoll_q_cnt;

	if (hisi_sas_alloc(hisi_hba)) {
		hisi_sas_free(hisi_hba);
		goto err_out;
@@ -4859,6 +4933,10 @@ hisi_sas_v3_probe(struct pci_dev *pdev, const struct pci_device_id *id)
	shost->max_cmd_len = 16;
	shost->can_queue = HISI_SAS_UNRESERVED_IPTT;
	shost->cmd_per_lun = HISI_SAS_UNRESERVED_IPTT;
	if (hisi_hba->iopoll_q_cnt)
		shost->nr_maps = 3;
	else
		shost->nr_maps = 1;

	sha->sas_ha_name = DRV_NAME;
	sha->dev = dev;