162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Block driver for s390 storage class memory.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright IBM Corp. 2012
662306a36Sopenharmony_ci * Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#define KMSG_COMPONENT "scm_block"
1062306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/spinlock.h>
1462306a36Sopenharmony_ci#include <linux/mempool.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/blkdev.h>
1762306a36Sopenharmony_ci#include <linux/blk-mq.h>
1862306a36Sopenharmony_ci#include <linux/slab.h>
1962306a36Sopenharmony_ci#include <linux/list.h>
2062306a36Sopenharmony_ci#include <linux/io.h>
2162306a36Sopenharmony_ci#include <asm/eadm.h>
2262306a36Sopenharmony_ci#include "scm_blk.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cidebug_info_t *scm_debug;
2562306a36Sopenharmony_cistatic int scm_major;
2662306a36Sopenharmony_cistatic mempool_t *aidaw_pool;
2762306a36Sopenharmony_cistatic DEFINE_SPINLOCK(list_lock);
2862306a36Sopenharmony_cistatic LIST_HEAD(inactive_requests);
2962306a36Sopenharmony_cistatic unsigned int nr_requests = 64;
3062306a36Sopenharmony_cistatic unsigned int nr_requests_per_io = 8;
3162306a36Sopenharmony_cistatic atomic_t nr_devices = ATOMIC_INIT(0);
3262306a36Sopenharmony_cimodule_param(nr_requests, uint, S_IRUGO);
3362306a36Sopenharmony_ciMODULE_PARM_DESC(nr_requests, "Number of parallel requests.");
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cimodule_param(nr_requests_per_io, uint, S_IRUGO);
3662306a36Sopenharmony_ciMODULE_PARM_DESC(nr_requests_per_io, "Number of requests per IO.");
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ciMODULE_DESCRIPTION("Block driver for s390 storage class memory.");
3962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
4062306a36Sopenharmony_ciMODULE_ALIAS("scm:scmdev*");
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic void __scm_free_rq(struct scm_request *scmrq)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	struct aob_rq_header *aobrq = to_aobrq(scmrq);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	free_page((unsigned long) scmrq->aob);
4762306a36Sopenharmony_ci	kfree(scmrq->request);
4862306a36Sopenharmony_ci	kfree(aobrq);
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic void scm_free_rqs(void)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	struct list_head *iter, *safe;
5462306a36Sopenharmony_ci	struct scm_request *scmrq;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	spin_lock_irq(&list_lock);
5762306a36Sopenharmony_ci	list_for_each_safe(iter, safe, &inactive_requests) {
5862306a36Sopenharmony_ci		scmrq = list_entry(iter, struct scm_request, list);
5962306a36Sopenharmony_ci		list_del(&scmrq->list);
6062306a36Sopenharmony_ci		__scm_free_rq(scmrq);
6162306a36Sopenharmony_ci	}
6262306a36Sopenharmony_ci	spin_unlock_irq(&list_lock);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	mempool_destroy(aidaw_pool);
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic int __scm_alloc_rq(void)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	struct aob_rq_header *aobrq;
7062306a36Sopenharmony_ci	struct scm_request *scmrq;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	aobrq = kzalloc(sizeof(*aobrq) + sizeof(*scmrq), GFP_KERNEL);
7362306a36Sopenharmony_ci	if (!aobrq)
7462306a36Sopenharmony_ci		return -ENOMEM;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	scmrq = (void *) aobrq->data;
7762306a36Sopenharmony_ci	scmrq->aob = (void *) get_zeroed_page(GFP_DMA);
7862306a36Sopenharmony_ci	if (!scmrq->aob)
7962306a36Sopenharmony_ci		goto free;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	scmrq->request = kcalloc(nr_requests_per_io, sizeof(scmrq->request[0]),
8262306a36Sopenharmony_ci				 GFP_KERNEL);
8362306a36Sopenharmony_ci	if (!scmrq->request)
8462306a36Sopenharmony_ci		goto free;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	INIT_LIST_HEAD(&scmrq->list);
8762306a36Sopenharmony_ci	spin_lock_irq(&list_lock);
8862306a36Sopenharmony_ci	list_add(&scmrq->list, &inactive_requests);
8962306a36Sopenharmony_ci	spin_unlock_irq(&list_lock);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return 0;
9262306a36Sopenharmony_cifree:
9362306a36Sopenharmony_ci	__scm_free_rq(scmrq);
9462306a36Sopenharmony_ci	return -ENOMEM;
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic int scm_alloc_rqs(unsigned int nrqs)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	int ret = 0;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	aidaw_pool = mempool_create_page_pool(max(nrqs/8, 1U), 0);
10262306a36Sopenharmony_ci	if (!aidaw_pool)
10362306a36Sopenharmony_ci		return -ENOMEM;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	while (nrqs-- && !ret)
10662306a36Sopenharmony_ci		ret = __scm_alloc_rq();
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	return ret;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic struct scm_request *scm_request_fetch(void)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	struct scm_request *scmrq = NULL;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	spin_lock_irq(&list_lock);
11662306a36Sopenharmony_ci	if (list_empty(&inactive_requests))
11762306a36Sopenharmony_ci		goto out;
11862306a36Sopenharmony_ci	scmrq = list_first_entry(&inactive_requests, struct scm_request, list);
11962306a36Sopenharmony_ci	list_del(&scmrq->list);
12062306a36Sopenharmony_ciout:
12162306a36Sopenharmony_ci	spin_unlock_irq(&list_lock);
12262306a36Sopenharmony_ci	return scmrq;
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic void scm_request_done(struct scm_request *scmrq)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	unsigned long flags;
12862306a36Sopenharmony_ci	struct msb *msb;
12962306a36Sopenharmony_ci	u64 aidaw;
13062306a36Sopenharmony_ci	int i;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	for (i = 0; i < nr_requests_per_io && scmrq->request[i]; i++) {
13362306a36Sopenharmony_ci		msb = &scmrq->aob->msb[i];
13462306a36Sopenharmony_ci		aidaw = (u64)phys_to_virt(msb->data_addr);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci		if ((msb->flags & MSB_FLAG_IDA) && aidaw &&
13762306a36Sopenharmony_ci		    IS_ALIGNED(aidaw, PAGE_SIZE))
13862306a36Sopenharmony_ci			mempool_free(virt_to_page((void *)aidaw), aidaw_pool);
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	spin_lock_irqsave(&list_lock, flags);
14262306a36Sopenharmony_ci	list_add(&scmrq->list, &inactive_requests);
14362306a36Sopenharmony_ci	spin_unlock_irqrestore(&list_lock, flags);
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic bool scm_permit_request(struct scm_blk_dev *bdev, struct request *req)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	return rq_data_dir(req) != WRITE || bdev->state != SCM_WR_PROHIBIT;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic inline struct aidaw *scm_aidaw_alloc(void)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct page *page = mempool_alloc(aidaw_pool, GFP_ATOMIC);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	return page ? page_address(page) : NULL;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic inline unsigned long scm_aidaw_bytes(struct aidaw *aidaw)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	unsigned long _aidaw = (unsigned long) aidaw;
16162306a36Sopenharmony_ci	unsigned long bytes = ALIGN(_aidaw, PAGE_SIZE) - _aidaw;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	return (bytes / sizeof(*aidaw)) * PAGE_SIZE;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistruct aidaw *scm_aidaw_fetch(struct scm_request *scmrq, unsigned int bytes)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	struct aidaw *aidaw;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	if (scm_aidaw_bytes(scmrq->next_aidaw) >= bytes)
17162306a36Sopenharmony_ci		return scmrq->next_aidaw;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	aidaw = scm_aidaw_alloc();
17462306a36Sopenharmony_ci	if (aidaw)
17562306a36Sopenharmony_ci		memset(aidaw, 0, PAGE_SIZE);
17662306a36Sopenharmony_ci	return aidaw;
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic int scm_request_prepare(struct scm_request *scmrq)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	struct scm_blk_dev *bdev = scmrq->bdev;
18262306a36Sopenharmony_ci	struct scm_device *scmdev = bdev->gendisk->private_data;
18362306a36Sopenharmony_ci	int pos = scmrq->aob->request.msb_count;
18462306a36Sopenharmony_ci	struct msb *msb = &scmrq->aob->msb[pos];
18562306a36Sopenharmony_ci	struct request *req = scmrq->request[pos];
18662306a36Sopenharmony_ci	struct req_iterator iter;
18762306a36Sopenharmony_ci	struct aidaw *aidaw;
18862306a36Sopenharmony_ci	struct bio_vec bv;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	aidaw = scm_aidaw_fetch(scmrq, blk_rq_bytes(req));
19162306a36Sopenharmony_ci	if (!aidaw)
19262306a36Sopenharmony_ci		return -ENOMEM;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	msb->bs = MSB_BS_4K;
19562306a36Sopenharmony_ci	scmrq->aob->request.msb_count++;
19662306a36Sopenharmony_ci	msb->scm_addr = scmdev->address + ((u64) blk_rq_pos(req) << 9);
19762306a36Sopenharmony_ci	msb->oc = (rq_data_dir(req) == READ) ? MSB_OC_READ : MSB_OC_WRITE;
19862306a36Sopenharmony_ci	msb->flags |= MSB_FLAG_IDA;
19962306a36Sopenharmony_ci	msb->data_addr = (u64)virt_to_phys(aidaw);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	rq_for_each_segment(bv, req, iter) {
20262306a36Sopenharmony_ci		WARN_ON(bv.bv_offset);
20362306a36Sopenharmony_ci		msb->blk_count += bv.bv_len >> 12;
20462306a36Sopenharmony_ci		aidaw->data_addr = virt_to_phys(page_address(bv.bv_page));
20562306a36Sopenharmony_ci		aidaw++;
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	scmrq->next_aidaw = aidaw;
20962306a36Sopenharmony_ci	return 0;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic inline void scm_request_set(struct scm_request *scmrq,
21362306a36Sopenharmony_ci				   struct request *req)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	scmrq->request[scmrq->aob->request.msb_count] = req;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic inline void scm_request_init(struct scm_blk_dev *bdev,
21962306a36Sopenharmony_ci				    struct scm_request *scmrq)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	struct aob_rq_header *aobrq = to_aobrq(scmrq);
22262306a36Sopenharmony_ci	struct aob *aob = scmrq->aob;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	memset(scmrq->request, 0,
22562306a36Sopenharmony_ci	       nr_requests_per_io * sizeof(scmrq->request[0]));
22662306a36Sopenharmony_ci	memset(aob, 0, sizeof(*aob));
22762306a36Sopenharmony_ci	aobrq->scmdev = bdev->scmdev;
22862306a36Sopenharmony_ci	aob->request.cmd_code = ARQB_CMD_MOVE;
22962306a36Sopenharmony_ci	aob->request.data = (u64) aobrq;
23062306a36Sopenharmony_ci	scmrq->bdev = bdev;
23162306a36Sopenharmony_ci	scmrq->retries = 4;
23262306a36Sopenharmony_ci	scmrq->error = BLK_STS_OK;
23362306a36Sopenharmony_ci	/* We don't use all msbs - place aidaws at the end of the aob page. */
23462306a36Sopenharmony_ci	scmrq->next_aidaw = (void *) &aob->msb[nr_requests_per_io];
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic void scm_request_requeue(struct scm_request *scmrq)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	struct scm_blk_dev *bdev = scmrq->bdev;
24062306a36Sopenharmony_ci	int i;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	for (i = 0; i < nr_requests_per_io && scmrq->request[i]; i++)
24362306a36Sopenharmony_ci		blk_mq_requeue_request(scmrq->request[i], false);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	atomic_dec(&bdev->queued_reqs);
24662306a36Sopenharmony_ci	scm_request_done(scmrq);
24762306a36Sopenharmony_ci	blk_mq_kick_requeue_list(bdev->rq);
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic void scm_request_finish(struct scm_request *scmrq)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	struct scm_blk_dev *bdev = scmrq->bdev;
25362306a36Sopenharmony_ci	blk_status_t *error;
25462306a36Sopenharmony_ci	int i;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	for (i = 0; i < nr_requests_per_io && scmrq->request[i]; i++) {
25762306a36Sopenharmony_ci		error = blk_mq_rq_to_pdu(scmrq->request[i]);
25862306a36Sopenharmony_ci		*error = scmrq->error;
25962306a36Sopenharmony_ci		if (likely(!blk_should_fake_timeout(scmrq->request[i]->q)))
26062306a36Sopenharmony_ci			blk_mq_complete_request(scmrq->request[i]);
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	atomic_dec(&bdev->queued_reqs);
26462306a36Sopenharmony_ci	scm_request_done(scmrq);
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic void scm_request_start(struct scm_request *scmrq)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	struct scm_blk_dev *bdev = scmrq->bdev;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	atomic_inc(&bdev->queued_reqs);
27262306a36Sopenharmony_ci	if (eadm_start_aob(scmrq->aob)) {
27362306a36Sopenharmony_ci		SCM_LOG(5, "no subchannel");
27462306a36Sopenharmony_ci		scm_request_requeue(scmrq);
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistruct scm_queue {
27962306a36Sopenharmony_ci	struct scm_request *scmrq;
28062306a36Sopenharmony_ci	spinlock_t lock;
28162306a36Sopenharmony_ci};
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic blk_status_t scm_blk_request(struct blk_mq_hw_ctx *hctx,
28462306a36Sopenharmony_ci			   const struct blk_mq_queue_data *qd)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	struct scm_device *scmdev = hctx->queue->queuedata;
28762306a36Sopenharmony_ci	struct scm_blk_dev *bdev = dev_get_drvdata(&scmdev->dev);
28862306a36Sopenharmony_ci	struct scm_queue *sq = hctx->driver_data;
28962306a36Sopenharmony_ci	struct request *req = qd->rq;
29062306a36Sopenharmony_ci	struct scm_request *scmrq;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	spin_lock(&sq->lock);
29362306a36Sopenharmony_ci	if (!scm_permit_request(bdev, req)) {
29462306a36Sopenharmony_ci		spin_unlock(&sq->lock);
29562306a36Sopenharmony_ci		return BLK_STS_RESOURCE;
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	scmrq = sq->scmrq;
29962306a36Sopenharmony_ci	if (!scmrq) {
30062306a36Sopenharmony_ci		scmrq = scm_request_fetch();
30162306a36Sopenharmony_ci		if (!scmrq) {
30262306a36Sopenharmony_ci			SCM_LOG(5, "no request");
30362306a36Sopenharmony_ci			spin_unlock(&sq->lock);
30462306a36Sopenharmony_ci			return BLK_STS_RESOURCE;
30562306a36Sopenharmony_ci		}
30662306a36Sopenharmony_ci		scm_request_init(bdev, scmrq);
30762306a36Sopenharmony_ci		sq->scmrq = scmrq;
30862306a36Sopenharmony_ci	}
30962306a36Sopenharmony_ci	scm_request_set(scmrq, req);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	if (scm_request_prepare(scmrq)) {
31262306a36Sopenharmony_ci		SCM_LOG(5, "aidaw alloc failed");
31362306a36Sopenharmony_ci		scm_request_set(scmrq, NULL);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci		if (scmrq->aob->request.msb_count)
31662306a36Sopenharmony_ci			scm_request_start(scmrq);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci		sq->scmrq = NULL;
31962306a36Sopenharmony_ci		spin_unlock(&sq->lock);
32062306a36Sopenharmony_ci		return BLK_STS_RESOURCE;
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci	blk_mq_start_request(req);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	if (qd->last || scmrq->aob->request.msb_count == nr_requests_per_io) {
32562306a36Sopenharmony_ci		scm_request_start(scmrq);
32662306a36Sopenharmony_ci		sq->scmrq = NULL;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci	spin_unlock(&sq->lock);
32962306a36Sopenharmony_ci	return BLK_STS_OK;
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic int scm_blk_init_hctx(struct blk_mq_hw_ctx *hctx, void *data,
33362306a36Sopenharmony_ci			     unsigned int idx)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	struct scm_queue *qd = kzalloc(sizeof(*qd), GFP_KERNEL);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	if (!qd)
33862306a36Sopenharmony_ci		return -ENOMEM;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	spin_lock_init(&qd->lock);
34162306a36Sopenharmony_ci	hctx->driver_data = qd;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	return 0;
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_cistatic void scm_blk_exit_hctx(struct blk_mq_hw_ctx *hctx, unsigned int idx)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	struct scm_queue *qd = hctx->driver_data;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	WARN_ON(qd->scmrq);
35162306a36Sopenharmony_ci	kfree(hctx->driver_data);
35262306a36Sopenharmony_ci	hctx->driver_data = NULL;
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic void __scmrq_log_error(struct scm_request *scmrq)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	struct aob *aob = scmrq->aob;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (scmrq->error == BLK_STS_TIMEOUT)
36062306a36Sopenharmony_ci		SCM_LOG(1, "Request timeout");
36162306a36Sopenharmony_ci	else {
36262306a36Sopenharmony_ci		SCM_LOG(1, "Request error");
36362306a36Sopenharmony_ci		SCM_LOG_HEX(1, &aob->response, sizeof(aob->response));
36462306a36Sopenharmony_ci	}
36562306a36Sopenharmony_ci	if (scmrq->retries)
36662306a36Sopenharmony_ci		SCM_LOG(1, "Retry request");
36762306a36Sopenharmony_ci	else
36862306a36Sopenharmony_ci		pr_err("An I/O operation to SCM failed with rc=%d\n",
36962306a36Sopenharmony_ci		       scmrq->error);
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic void scm_blk_handle_error(struct scm_request *scmrq)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	struct scm_blk_dev *bdev = scmrq->bdev;
37562306a36Sopenharmony_ci	unsigned long flags;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	if (scmrq->error != BLK_STS_IOERR)
37862306a36Sopenharmony_ci		goto restart;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	/* For -EIO the response block is valid. */
38162306a36Sopenharmony_ci	switch (scmrq->aob->response.eqc) {
38262306a36Sopenharmony_ci	case EQC_WR_PROHIBIT:
38362306a36Sopenharmony_ci		spin_lock_irqsave(&bdev->lock, flags);
38462306a36Sopenharmony_ci		if (bdev->state != SCM_WR_PROHIBIT)
38562306a36Sopenharmony_ci			pr_info("%lx: Write access to the SCM increment is suspended\n",
38662306a36Sopenharmony_ci				(unsigned long) bdev->scmdev->address);
38762306a36Sopenharmony_ci		bdev->state = SCM_WR_PROHIBIT;
38862306a36Sopenharmony_ci		spin_unlock_irqrestore(&bdev->lock, flags);
38962306a36Sopenharmony_ci		goto requeue;
39062306a36Sopenharmony_ci	default:
39162306a36Sopenharmony_ci		break;
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cirestart:
39562306a36Sopenharmony_ci	if (!eadm_start_aob(scmrq->aob))
39662306a36Sopenharmony_ci		return;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cirequeue:
39962306a36Sopenharmony_ci	scm_request_requeue(scmrq);
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_civoid scm_blk_irq(struct scm_device *scmdev, void *data, blk_status_t error)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	struct scm_request *scmrq = data;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	scmrq->error = error;
40762306a36Sopenharmony_ci	if (error) {
40862306a36Sopenharmony_ci		__scmrq_log_error(scmrq);
40962306a36Sopenharmony_ci		if (scmrq->retries-- > 0) {
41062306a36Sopenharmony_ci			scm_blk_handle_error(scmrq);
41162306a36Sopenharmony_ci			return;
41262306a36Sopenharmony_ci		}
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	scm_request_finish(scmrq);
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistatic void scm_blk_request_done(struct request *req)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	blk_status_t *error = blk_mq_rq_to_pdu(req);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	blk_mq_end_request(req, *error);
42362306a36Sopenharmony_ci}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_cistatic const struct block_device_operations scm_blk_devops = {
42662306a36Sopenharmony_ci	.owner = THIS_MODULE,
42762306a36Sopenharmony_ci};
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_cistatic const struct blk_mq_ops scm_mq_ops = {
43062306a36Sopenharmony_ci	.queue_rq = scm_blk_request,
43162306a36Sopenharmony_ci	.complete = scm_blk_request_done,
43262306a36Sopenharmony_ci	.init_hctx = scm_blk_init_hctx,
43362306a36Sopenharmony_ci	.exit_hctx = scm_blk_exit_hctx,
43462306a36Sopenharmony_ci};
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ciint scm_blk_dev_setup(struct scm_blk_dev *bdev, struct scm_device *scmdev)
43762306a36Sopenharmony_ci{
43862306a36Sopenharmony_ci	unsigned int devindex, nr_max_blk;
43962306a36Sopenharmony_ci	struct request_queue *rq;
44062306a36Sopenharmony_ci	int len, ret;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	devindex = atomic_inc_return(&nr_devices) - 1;
44362306a36Sopenharmony_ci	/* scma..scmz + scmaa..scmzz */
44462306a36Sopenharmony_ci	if (devindex > 701) {
44562306a36Sopenharmony_ci		ret = -ENODEV;
44662306a36Sopenharmony_ci		goto out;
44762306a36Sopenharmony_ci	}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	bdev->scmdev = scmdev;
45062306a36Sopenharmony_ci	bdev->state = SCM_OPER;
45162306a36Sopenharmony_ci	spin_lock_init(&bdev->lock);
45262306a36Sopenharmony_ci	atomic_set(&bdev->queued_reqs, 0);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	bdev->tag_set.ops = &scm_mq_ops;
45562306a36Sopenharmony_ci	bdev->tag_set.cmd_size = sizeof(blk_status_t);
45662306a36Sopenharmony_ci	bdev->tag_set.nr_hw_queues = nr_requests;
45762306a36Sopenharmony_ci	bdev->tag_set.queue_depth = nr_requests_per_io * nr_requests;
45862306a36Sopenharmony_ci	bdev->tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
45962306a36Sopenharmony_ci	bdev->tag_set.numa_node = NUMA_NO_NODE;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	ret = blk_mq_alloc_tag_set(&bdev->tag_set);
46262306a36Sopenharmony_ci	if (ret)
46362306a36Sopenharmony_ci		goto out;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	bdev->gendisk = blk_mq_alloc_disk(&bdev->tag_set, scmdev);
46662306a36Sopenharmony_ci	if (IS_ERR(bdev->gendisk)) {
46762306a36Sopenharmony_ci		ret = PTR_ERR(bdev->gendisk);
46862306a36Sopenharmony_ci		goto out_tag;
46962306a36Sopenharmony_ci	}
47062306a36Sopenharmony_ci	rq = bdev->rq = bdev->gendisk->queue;
47162306a36Sopenharmony_ci	nr_max_blk = min(scmdev->nr_max_block,
47262306a36Sopenharmony_ci			 (unsigned int) (PAGE_SIZE / sizeof(struct aidaw)));
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	blk_queue_logical_block_size(rq, 1 << 12);
47562306a36Sopenharmony_ci	blk_queue_max_hw_sectors(rq, nr_max_blk << 3); /* 8 * 512 = blk_size */
47662306a36Sopenharmony_ci	blk_queue_max_segments(rq, nr_max_blk);
47762306a36Sopenharmony_ci	blk_queue_flag_set(QUEUE_FLAG_NONROT, rq);
47862306a36Sopenharmony_ci	blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, rq);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	bdev->gendisk->private_data = scmdev;
48162306a36Sopenharmony_ci	bdev->gendisk->fops = &scm_blk_devops;
48262306a36Sopenharmony_ci	bdev->gendisk->major = scm_major;
48362306a36Sopenharmony_ci	bdev->gendisk->first_minor = devindex * SCM_NR_PARTS;
48462306a36Sopenharmony_ci	bdev->gendisk->minors = SCM_NR_PARTS;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	len = snprintf(bdev->gendisk->disk_name, DISK_NAME_LEN, "scm");
48762306a36Sopenharmony_ci	if (devindex > 25) {
48862306a36Sopenharmony_ci		len += snprintf(bdev->gendisk->disk_name + len,
48962306a36Sopenharmony_ci				DISK_NAME_LEN - len, "%c",
49062306a36Sopenharmony_ci				'a' + (devindex / 26) - 1);
49162306a36Sopenharmony_ci		devindex = devindex % 26;
49262306a36Sopenharmony_ci	}
49362306a36Sopenharmony_ci	snprintf(bdev->gendisk->disk_name + len, DISK_NAME_LEN - len, "%c",
49462306a36Sopenharmony_ci		 'a' + devindex);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	/* 512 byte sectors */
49762306a36Sopenharmony_ci	set_capacity(bdev->gendisk, scmdev->size >> 9);
49862306a36Sopenharmony_ci	ret = device_add_disk(&scmdev->dev, bdev->gendisk, NULL);
49962306a36Sopenharmony_ci	if (ret)
50062306a36Sopenharmony_ci		goto out_cleanup_disk;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	return 0;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ciout_cleanup_disk:
50562306a36Sopenharmony_ci	put_disk(bdev->gendisk);
50662306a36Sopenharmony_ciout_tag:
50762306a36Sopenharmony_ci	blk_mq_free_tag_set(&bdev->tag_set);
50862306a36Sopenharmony_ciout:
50962306a36Sopenharmony_ci	atomic_dec(&nr_devices);
51062306a36Sopenharmony_ci	return ret;
51162306a36Sopenharmony_ci}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_civoid scm_blk_dev_cleanup(struct scm_blk_dev *bdev)
51462306a36Sopenharmony_ci{
51562306a36Sopenharmony_ci	del_gendisk(bdev->gendisk);
51662306a36Sopenharmony_ci	put_disk(bdev->gendisk);
51762306a36Sopenharmony_ci	blk_mq_free_tag_set(&bdev->tag_set);
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_civoid scm_blk_set_available(struct scm_blk_dev *bdev)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	unsigned long flags;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	spin_lock_irqsave(&bdev->lock, flags);
52562306a36Sopenharmony_ci	if (bdev->state == SCM_WR_PROHIBIT)
52662306a36Sopenharmony_ci		pr_info("%lx: Write access to the SCM increment is restored\n",
52762306a36Sopenharmony_ci			(unsigned long) bdev->scmdev->address);
52862306a36Sopenharmony_ci	bdev->state = SCM_OPER;
52962306a36Sopenharmony_ci	spin_unlock_irqrestore(&bdev->lock, flags);
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic bool __init scm_blk_params_valid(void)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	if (!nr_requests_per_io || nr_requests_per_io > 64)
53562306a36Sopenharmony_ci		return false;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	return true;
53862306a36Sopenharmony_ci}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_cistatic int __init scm_blk_init(void)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	int ret = -EINVAL;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	if (!scm_blk_params_valid())
54562306a36Sopenharmony_ci		goto out;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	ret = register_blkdev(0, "scm");
54862306a36Sopenharmony_ci	if (ret < 0)
54962306a36Sopenharmony_ci		goto out;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	scm_major = ret;
55262306a36Sopenharmony_ci	ret = scm_alloc_rqs(nr_requests);
55362306a36Sopenharmony_ci	if (ret)
55462306a36Sopenharmony_ci		goto out_free;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	scm_debug = debug_register("scm_log", 16, 1, 16);
55762306a36Sopenharmony_ci	if (!scm_debug) {
55862306a36Sopenharmony_ci		ret = -ENOMEM;
55962306a36Sopenharmony_ci		goto out_free;
56062306a36Sopenharmony_ci	}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	debug_register_view(scm_debug, &debug_hex_ascii_view);
56362306a36Sopenharmony_ci	debug_set_level(scm_debug, 2);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	ret = scm_drv_init();
56662306a36Sopenharmony_ci	if (ret)
56762306a36Sopenharmony_ci		goto out_dbf;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	return ret;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ciout_dbf:
57262306a36Sopenharmony_ci	debug_unregister(scm_debug);
57362306a36Sopenharmony_ciout_free:
57462306a36Sopenharmony_ci	scm_free_rqs();
57562306a36Sopenharmony_ci	unregister_blkdev(scm_major, "scm");
57662306a36Sopenharmony_ciout:
57762306a36Sopenharmony_ci	return ret;
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_cimodule_init(scm_blk_init);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_cistatic void __exit scm_blk_cleanup(void)
58262306a36Sopenharmony_ci{
58362306a36Sopenharmony_ci	scm_drv_cleanup();
58462306a36Sopenharmony_ci	debug_unregister(scm_debug);
58562306a36Sopenharmony_ci	scm_free_rqs();
58662306a36Sopenharmony_ci	unregister_blkdev(scm_major, "scm");
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_cimodule_exit(scm_blk_cleanup);
589