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