18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Block driver for s390 storage class memory. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2012 68c2ecf20Sopenharmony_ci * Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "scm_block" 108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 148c2ecf20Sopenharmony_ci#include <linux/mempool.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/blkdev.h> 178c2ecf20Sopenharmony_ci#include <linux/blk-mq.h> 188c2ecf20Sopenharmony_ci#include <linux/genhd.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/list.h> 218c2ecf20Sopenharmony_ci#include <linux/io.h> 228c2ecf20Sopenharmony_ci#include <asm/eadm.h> 238c2ecf20Sopenharmony_ci#include "scm_blk.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cidebug_info_t *scm_debug; 268c2ecf20Sopenharmony_cistatic int scm_major; 278c2ecf20Sopenharmony_cistatic mempool_t *aidaw_pool; 288c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(list_lock); 298c2ecf20Sopenharmony_cistatic LIST_HEAD(inactive_requests); 308c2ecf20Sopenharmony_cistatic unsigned int nr_requests = 64; 318c2ecf20Sopenharmony_cistatic unsigned int nr_requests_per_io = 8; 328c2ecf20Sopenharmony_cistatic atomic_t nr_devices = ATOMIC_INIT(0); 338c2ecf20Sopenharmony_cimodule_param(nr_requests, uint, S_IRUGO); 348c2ecf20Sopenharmony_ciMODULE_PARM_DESC(nr_requests, "Number of parallel requests."); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cimodule_param(nr_requests_per_io, uint, S_IRUGO); 378c2ecf20Sopenharmony_ciMODULE_PARM_DESC(nr_requests_per_io, "Number of requests per IO."); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Block driver for s390 storage class memory."); 408c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 418c2ecf20Sopenharmony_ciMODULE_ALIAS("scm:scmdev*"); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic void __scm_free_rq(struct scm_request *scmrq) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct aob_rq_header *aobrq = to_aobrq(scmrq); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci free_page((unsigned long) scmrq->aob); 488c2ecf20Sopenharmony_ci kfree(scmrq->request); 498c2ecf20Sopenharmony_ci kfree(aobrq); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic void scm_free_rqs(void) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct list_head *iter, *safe; 558c2ecf20Sopenharmony_ci struct scm_request *scmrq; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci spin_lock_irq(&list_lock); 588c2ecf20Sopenharmony_ci list_for_each_safe(iter, safe, &inactive_requests) { 598c2ecf20Sopenharmony_ci scmrq = list_entry(iter, struct scm_request, list); 608c2ecf20Sopenharmony_ci list_del(&scmrq->list); 618c2ecf20Sopenharmony_ci __scm_free_rq(scmrq); 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci spin_unlock_irq(&list_lock); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci mempool_destroy(aidaw_pool); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic int __scm_alloc_rq(void) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct aob_rq_header *aobrq; 718c2ecf20Sopenharmony_ci struct scm_request *scmrq; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci aobrq = kzalloc(sizeof(*aobrq) + sizeof(*scmrq), GFP_KERNEL); 748c2ecf20Sopenharmony_ci if (!aobrq) 758c2ecf20Sopenharmony_ci return -ENOMEM; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci scmrq = (void *) aobrq->data; 788c2ecf20Sopenharmony_ci scmrq->aob = (void *) get_zeroed_page(GFP_DMA); 798c2ecf20Sopenharmony_ci if (!scmrq->aob) 808c2ecf20Sopenharmony_ci goto free; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci scmrq->request = kcalloc(nr_requests_per_io, sizeof(scmrq->request[0]), 838c2ecf20Sopenharmony_ci GFP_KERNEL); 848c2ecf20Sopenharmony_ci if (!scmrq->request) 858c2ecf20Sopenharmony_ci goto free; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&scmrq->list); 888c2ecf20Sopenharmony_ci spin_lock_irq(&list_lock); 898c2ecf20Sopenharmony_ci list_add(&scmrq->list, &inactive_requests); 908c2ecf20Sopenharmony_ci spin_unlock_irq(&list_lock); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return 0; 938c2ecf20Sopenharmony_cifree: 948c2ecf20Sopenharmony_ci __scm_free_rq(scmrq); 958c2ecf20Sopenharmony_ci return -ENOMEM; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int scm_alloc_rqs(unsigned int nrqs) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci int ret = 0; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci aidaw_pool = mempool_create_page_pool(max(nrqs/8, 1U), 0); 1038c2ecf20Sopenharmony_ci if (!aidaw_pool) 1048c2ecf20Sopenharmony_ci return -ENOMEM; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci while (nrqs-- && !ret) 1078c2ecf20Sopenharmony_ci ret = __scm_alloc_rq(); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci return ret; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic struct scm_request *scm_request_fetch(void) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci struct scm_request *scmrq = NULL; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci spin_lock_irq(&list_lock); 1178c2ecf20Sopenharmony_ci if (list_empty(&inactive_requests)) 1188c2ecf20Sopenharmony_ci goto out; 1198c2ecf20Sopenharmony_ci scmrq = list_first_entry(&inactive_requests, struct scm_request, list); 1208c2ecf20Sopenharmony_ci list_del(&scmrq->list); 1218c2ecf20Sopenharmony_ciout: 1228c2ecf20Sopenharmony_ci spin_unlock_irq(&list_lock); 1238c2ecf20Sopenharmony_ci return scmrq; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic void scm_request_done(struct scm_request *scmrq) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci unsigned long flags; 1298c2ecf20Sopenharmony_ci struct msb *msb; 1308c2ecf20Sopenharmony_ci u64 aidaw; 1318c2ecf20Sopenharmony_ci int i; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci for (i = 0; i < nr_requests_per_io && scmrq->request[i]; i++) { 1348c2ecf20Sopenharmony_ci msb = &scmrq->aob->msb[i]; 1358c2ecf20Sopenharmony_ci aidaw = (u64)phys_to_virt(msb->data_addr); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if ((msb->flags & MSB_FLAG_IDA) && aidaw && 1388c2ecf20Sopenharmony_ci IS_ALIGNED(aidaw, PAGE_SIZE)) 1398c2ecf20Sopenharmony_ci mempool_free(virt_to_page(aidaw), aidaw_pool); 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci spin_lock_irqsave(&list_lock, flags); 1438c2ecf20Sopenharmony_ci list_add(&scmrq->list, &inactive_requests); 1448c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&list_lock, flags); 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic bool scm_permit_request(struct scm_blk_dev *bdev, struct request *req) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci return rq_data_dir(req) != WRITE || bdev->state != SCM_WR_PROHIBIT; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic inline struct aidaw *scm_aidaw_alloc(void) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct page *page = mempool_alloc(aidaw_pool, GFP_ATOMIC); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return page ? page_address(page) : NULL; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic inline unsigned long scm_aidaw_bytes(struct aidaw *aidaw) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci unsigned long _aidaw = (unsigned long) aidaw; 1628c2ecf20Sopenharmony_ci unsigned long bytes = ALIGN(_aidaw, PAGE_SIZE) - _aidaw; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci return (bytes / sizeof(*aidaw)) * PAGE_SIZE; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistruct aidaw *scm_aidaw_fetch(struct scm_request *scmrq, unsigned int bytes) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci struct aidaw *aidaw; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (scm_aidaw_bytes(scmrq->next_aidaw) >= bytes) 1728c2ecf20Sopenharmony_ci return scmrq->next_aidaw; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci aidaw = scm_aidaw_alloc(); 1758c2ecf20Sopenharmony_ci if (aidaw) 1768c2ecf20Sopenharmony_ci memset(aidaw, 0, PAGE_SIZE); 1778c2ecf20Sopenharmony_ci return aidaw; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic int scm_request_prepare(struct scm_request *scmrq) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci struct scm_blk_dev *bdev = scmrq->bdev; 1838c2ecf20Sopenharmony_ci struct scm_device *scmdev = bdev->gendisk->private_data; 1848c2ecf20Sopenharmony_ci int pos = scmrq->aob->request.msb_count; 1858c2ecf20Sopenharmony_ci struct msb *msb = &scmrq->aob->msb[pos]; 1868c2ecf20Sopenharmony_ci struct request *req = scmrq->request[pos]; 1878c2ecf20Sopenharmony_ci struct req_iterator iter; 1888c2ecf20Sopenharmony_ci struct aidaw *aidaw; 1898c2ecf20Sopenharmony_ci struct bio_vec bv; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci aidaw = scm_aidaw_fetch(scmrq, blk_rq_bytes(req)); 1928c2ecf20Sopenharmony_ci if (!aidaw) 1938c2ecf20Sopenharmony_ci return -ENOMEM; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci msb->bs = MSB_BS_4K; 1968c2ecf20Sopenharmony_ci scmrq->aob->request.msb_count++; 1978c2ecf20Sopenharmony_ci msb->scm_addr = scmdev->address + ((u64) blk_rq_pos(req) << 9); 1988c2ecf20Sopenharmony_ci msb->oc = (rq_data_dir(req) == READ) ? MSB_OC_READ : MSB_OC_WRITE; 1998c2ecf20Sopenharmony_ci msb->flags |= MSB_FLAG_IDA; 2008c2ecf20Sopenharmony_ci msb->data_addr = (u64)virt_to_phys(aidaw); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci rq_for_each_segment(bv, req, iter) { 2038c2ecf20Sopenharmony_ci WARN_ON(bv.bv_offset); 2048c2ecf20Sopenharmony_ci msb->blk_count += bv.bv_len >> 12; 2058c2ecf20Sopenharmony_ci aidaw->data_addr = virt_to_phys(page_address(bv.bv_page)); 2068c2ecf20Sopenharmony_ci aidaw++; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci scmrq->next_aidaw = aidaw; 2108c2ecf20Sopenharmony_ci return 0; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic inline void scm_request_set(struct scm_request *scmrq, 2148c2ecf20Sopenharmony_ci struct request *req) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci scmrq->request[scmrq->aob->request.msb_count] = req; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic inline void scm_request_init(struct scm_blk_dev *bdev, 2208c2ecf20Sopenharmony_ci struct scm_request *scmrq) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct aob_rq_header *aobrq = to_aobrq(scmrq); 2238c2ecf20Sopenharmony_ci struct aob *aob = scmrq->aob; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci memset(scmrq->request, 0, 2268c2ecf20Sopenharmony_ci nr_requests_per_io * sizeof(scmrq->request[0])); 2278c2ecf20Sopenharmony_ci memset(aob, 0, sizeof(*aob)); 2288c2ecf20Sopenharmony_ci aobrq->scmdev = bdev->scmdev; 2298c2ecf20Sopenharmony_ci aob->request.cmd_code = ARQB_CMD_MOVE; 2308c2ecf20Sopenharmony_ci aob->request.data = (u64) aobrq; 2318c2ecf20Sopenharmony_ci scmrq->bdev = bdev; 2328c2ecf20Sopenharmony_ci scmrq->retries = 4; 2338c2ecf20Sopenharmony_ci scmrq->error = BLK_STS_OK; 2348c2ecf20Sopenharmony_ci /* We don't use all msbs - place aidaws at the end of the aob page. */ 2358c2ecf20Sopenharmony_ci scmrq->next_aidaw = (void *) &aob->msb[nr_requests_per_io]; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic void scm_request_requeue(struct scm_request *scmrq) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct scm_blk_dev *bdev = scmrq->bdev; 2418c2ecf20Sopenharmony_ci int i; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci for (i = 0; i < nr_requests_per_io && scmrq->request[i]; i++) 2448c2ecf20Sopenharmony_ci blk_mq_requeue_request(scmrq->request[i], false); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci atomic_dec(&bdev->queued_reqs); 2478c2ecf20Sopenharmony_ci scm_request_done(scmrq); 2488c2ecf20Sopenharmony_ci blk_mq_kick_requeue_list(bdev->rq); 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic void scm_request_finish(struct scm_request *scmrq) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct scm_blk_dev *bdev = scmrq->bdev; 2548c2ecf20Sopenharmony_ci blk_status_t *error; 2558c2ecf20Sopenharmony_ci int i; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci for (i = 0; i < nr_requests_per_io && scmrq->request[i]; i++) { 2588c2ecf20Sopenharmony_ci error = blk_mq_rq_to_pdu(scmrq->request[i]); 2598c2ecf20Sopenharmony_ci *error = scmrq->error; 2608c2ecf20Sopenharmony_ci if (likely(!blk_should_fake_timeout(scmrq->request[i]->q))) 2618c2ecf20Sopenharmony_ci blk_mq_complete_request(scmrq->request[i]); 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci atomic_dec(&bdev->queued_reqs); 2658c2ecf20Sopenharmony_ci scm_request_done(scmrq); 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic void scm_request_start(struct scm_request *scmrq) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct scm_blk_dev *bdev = scmrq->bdev; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci atomic_inc(&bdev->queued_reqs); 2738c2ecf20Sopenharmony_ci if (eadm_start_aob(scmrq->aob)) { 2748c2ecf20Sopenharmony_ci SCM_LOG(5, "no subchannel"); 2758c2ecf20Sopenharmony_ci scm_request_requeue(scmrq); 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistruct scm_queue { 2808c2ecf20Sopenharmony_ci struct scm_request *scmrq; 2818c2ecf20Sopenharmony_ci spinlock_t lock; 2828c2ecf20Sopenharmony_ci}; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic blk_status_t scm_blk_request(struct blk_mq_hw_ctx *hctx, 2858c2ecf20Sopenharmony_ci const struct blk_mq_queue_data *qd) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci struct scm_device *scmdev = hctx->queue->queuedata; 2888c2ecf20Sopenharmony_ci struct scm_blk_dev *bdev = dev_get_drvdata(&scmdev->dev); 2898c2ecf20Sopenharmony_ci struct scm_queue *sq = hctx->driver_data; 2908c2ecf20Sopenharmony_ci struct request *req = qd->rq; 2918c2ecf20Sopenharmony_ci struct scm_request *scmrq; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci spin_lock(&sq->lock); 2948c2ecf20Sopenharmony_ci if (!scm_permit_request(bdev, req)) { 2958c2ecf20Sopenharmony_ci spin_unlock(&sq->lock); 2968c2ecf20Sopenharmony_ci return BLK_STS_RESOURCE; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci scmrq = sq->scmrq; 3008c2ecf20Sopenharmony_ci if (!scmrq) { 3018c2ecf20Sopenharmony_ci scmrq = scm_request_fetch(); 3028c2ecf20Sopenharmony_ci if (!scmrq) { 3038c2ecf20Sopenharmony_ci SCM_LOG(5, "no request"); 3048c2ecf20Sopenharmony_ci spin_unlock(&sq->lock); 3058c2ecf20Sopenharmony_ci return BLK_STS_RESOURCE; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci scm_request_init(bdev, scmrq); 3088c2ecf20Sopenharmony_ci sq->scmrq = scmrq; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci scm_request_set(scmrq, req); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (scm_request_prepare(scmrq)) { 3138c2ecf20Sopenharmony_ci SCM_LOG(5, "aidaw alloc failed"); 3148c2ecf20Sopenharmony_ci scm_request_set(scmrq, NULL); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (scmrq->aob->request.msb_count) 3178c2ecf20Sopenharmony_ci scm_request_start(scmrq); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci sq->scmrq = NULL; 3208c2ecf20Sopenharmony_ci spin_unlock(&sq->lock); 3218c2ecf20Sopenharmony_ci return BLK_STS_RESOURCE; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci blk_mq_start_request(req); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (qd->last || scmrq->aob->request.msb_count == nr_requests_per_io) { 3268c2ecf20Sopenharmony_ci scm_request_start(scmrq); 3278c2ecf20Sopenharmony_ci sq->scmrq = NULL; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci spin_unlock(&sq->lock); 3308c2ecf20Sopenharmony_ci return BLK_STS_OK; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic int scm_blk_init_hctx(struct blk_mq_hw_ctx *hctx, void *data, 3348c2ecf20Sopenharmony_ci unsigned int idx) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci struct scm_queue *qd = kzalloc(sizeof(*qd), GFP_KERNEL); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (!qd) 3398c2ecf20Sopenharmony_ci return -ENOMEM; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci spin_lock_init(&qd->lock); 3428c2ecf20Sopenharmony_ci hctx->driver_data = qd; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci return 0; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic void scm_blk_exit_hctx(struct blk_mq_hw_ctx *hctx, unsigned int idx) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci struct scm_queue *qd = hctx->driver_data; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci WARN_ON(qd->scmrq); 3528c2ecf20Sopenharmony_ci kfree(hctx->driver_data); 3538c2ecf20Sopenharmony_ci hctx->driver_data = NULL; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic void __scmrq_log_error(struct scm_request *scmrq) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci struct aob *aob = scmrq->aob; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (scmrq->error == BLK_STS_TIMEOUT) 3618c2ecf20Sopenharmony_ci SCM_LOG(1, "Request timeout"); 3628c2ecf20Sopenharmony_ci else { 3638c2ecf20Sopenharmony_ci SCM_LOG(1, "Request error"); 3648c2ecf20Sopenharmony_ci SCM_LOG_HEX(1, &aob->response, sizeof(aob->response)); 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci if (scmrq->retries) 3678c2ecf20Sopenharmony_ci SCM_LOG(1, "Retry request"); 3688c2ecf20Sopenharmony_ci else 3698c2ecf20Sopenharmony_ci pr_err("An I/O operation to SCM failed with rc=%d\n", 3708c2ecf20Sopenharmony_ci scmrq->error); 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic void scm_blk_handle_error(struct scm_request *scmrq) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci struct scm_blk_dev *bdev = scmrq->bdev; 3768c2ecf20Sopenharmony_ci unsigned long flags; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (scmrq->error != BLK_STS_IOERR) 3798c2ecf20Sopenharmony_ci goto restart; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci /* For -EIO the response block is valid. */ 3828c2ecf20Sopenharmony_ci switch (scmrq->aob->response.eqc) { 3838c2ecf20Sopenharmony_ci case EQC_WR_PROHIBIT: 3848c2ecf20Sopenharmony_ci spin_lock_irqsave(&bdev->lock, flags); 3858c2ecf20Sopenharmony_ci if (bdev->state != SCM_WR_PROHIBIT) 3868c2ecf20Sopenharmony_ci pr_info("%lx: Write access to the SCM increment is suspended\n", 3878c2ecf20Sopenharmony_ci (unsigned long) bdev->scmdev->address); 3888c2ecf20Sopenharmony_ci bdev->state = SCM_WR_PROHIBIT; 3898c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bdev->lock, flags); 3908c2ecf20Sopenharmony_ci goto requeue; 3918c2ecf20Sopenharmony_ci default: 3928c2ecf20Sopenharmony_ci break; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cirestart: 3968c2ecf20Sopenharmony_ci if (!eadm_start_aob(scmrq->aob)) 3978c2ecf20Sopenharmony_ci return; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cirequeue: 4008c2ecf20Sopenharmony_ci scm_request_requeue(scmrq); 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_civoid scm_blk_irq(struct scm_device *scmdev, void *data, blk_status_t error) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci struct scm_request *scmrq = data; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci scmrq->error = error; 4088c2ecf20Sopenharmony_ci if (error) { 4098c2ecf20Sopenharmony_ci __scmrq_log_error(scmrq); 4108c2ecf20Sopenharmony_ci if (scmrq->retries-- > 0) { 4118c2ecf20Sopenharmony_ci scm_blk_handle_error(scmrq); 4128c2ecf20Sopenharmony_ci return; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci scm_request_finish(scmrq); 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistatic void scm_blk_request_done(struct request *req) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci blk_status_t *error = blk_mq_rq_to_pdu(req); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci blk_mq_end_request(req, *error); 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic const struct block_device_operations scm_blk_devops = { 4278c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4288c2ecf20Sopenharmony_ci}; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic const struct blk_mq_ops scm_mq_ops = { 4318c2ecf20Sopenharmony_ci .queue_rq = scm_blk_request, 4328c2ecf20Sopenharmony_ci .complete = scm_blk_request_done, 4338c2ecf20Sopenharmony_ci .init_hctx = scm_blk_init_hctx, 4348c2ecf20Sopenharmony_ci .exit_hctx = scm_blk_exit_hctx, 4358c2ecf20Sopenharmony_ci}; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ciint scm_blk_dev_setup(struct scm_blk_dev *bdev, struct scm_device *scmdev) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci unsigned int devindex, nr_max_blk; 4408c2ecf20Sopenharmony_ci struct request_queue *rq; 4418c2ecf20Sopenharmony_ci int len, ret; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci devindex = atomic_inc_return(&nr_devices) - 1; 4448c2ecf20Sopenharmony_ci /* scma..scmz + scmaa..scmzz */ 4458c2ecf20Sopenharmony_ci if (devindex > 701) { 4468c2ecf20Sopenharmony_ci ret = -ENODEV; 4478c2ecf20Sopenharmony_ci goto out; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci bdev->scmdev = scmdev; 4518c2ecf20Sopenharmony_ci bdev->state = SCM_OPER; 4528c2ecf20Sopenharmony_ci spin_lock_init(&bdev->lock); 4538c2ecf20Sopenharmony_ci atomic_set(&bdev->queued_reqs, 0); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci bdev->tag_set.ops = &scm_mq_ops; 4568c2ecf20Sopenharmony_ci bdev->tag_set.cmd_size = sizeof(blk_status_t); 4578c2ecf20Sopenharmony_ci bdev->tag_set.nr_hw_queues = nr_requests; 4588c2ecf20Sopenharmony_ci bdev->tag_set.queue_depth = nr_requests_per_io * nr_requests; 4598c2ecf20Sopenharmony_ci bdev->tag_set.flags = BLK_MQ_F_SHOULD_MERGE; 4608c2ecf20Sopenharmony_ci bdev->tag_set.numa_node = NUMA_NO_NODE; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci ret = blk_mq_alloc_tag_set(&bdev->tag_set); 4638c2ecf20Sopenharmony_ci if (ret) 4648c2ecf20Sopenharmony_ci goto out; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci rq = blk_mq_init_queue(&bdev->tag_set); 4678c2ecf20Sopenharmony_ci if (IS_ERR(rq)) { 4688c2ecf20Sopenharmony_ci ret = PTR_ERR(rq); 4698c2ecf20Sopenharmony_ci goto out_tag; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci bdev->rq = rq; 4728c2ecf20Sopenharmony_ci nr_max_blk = min(scmdev->nr_max_block, 4738c2ecf20Sopenharmony_ci (unsigned int) (PAGE_SIZE / sizeof(struct aidaw))); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci blk_queue_logical_block_size(rq, 1 << 12); 4768c2ecf20Sopenharmony_ci blk_queue_max_hw_sectors(rq, nr_max_blk << 3); /* 8 * 512 = blk_size */ 4778c2ecf20Sopenharmony_ci blk_queue_max_segments(rq, nr_max_blk); 4788c2ecf20Sopenharmony_ci blk_queue_flag_set(QUEUE_FLAG_NONROT, rq); 4798c2ecf20Sopenharmony_ci blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, rq); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci bdev->gendisk = alloc_disk(SCM_NR_PARTS); 4828c2ecf20Sopenharmony_ci if (!bdev->gendisk) { 4838c2ecf20Sopenharmony_ci ret = -ENOMEM; 4848c2ecf20Sopenharmony_ci goto out_queue; 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci rq->queuedata = scmdev; 4878c2ecf20Sopenharmony_ci bdev->gendisk->private_data = scmdev; 4888c2ecf20Sopenharmony_ci bdev->gendisk->fops = &scm_blk_devops; 4898c2ecf20Sopenharmony_ci bdev->gendisk->queue = rq; 4908c2ecf20Sopenharmony_ci bdev->gendisk->major = scm_major; 4918c2ecf20Sopenharmony_ci bdev->gendisk->first_minor = devindex * SCM_NR_PARTS; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci len = snprintf(bdev->gendisk->disk_name, DISK_NAME_LEN, "scm"); 4948c2ecf20Sopenharmony_ci if (devindex > 25) { 4958c2ecf20Sopenharmony_ci len += snprintf(bdev->gendisk->disk_name + len, 4968c2ecf20Sopenharmony_ci DISK_NAME_LEN - len, "%c", 4978c2ecf20Sopenharmony_ci 'a' + (devindex / 26) - 1); 4988c2ecf20Sopenharmony_ci devindex = devindex % 26; 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci snprintf(bdev->gendisk->disk_name + len, DISK_NAME_LEN - len, "%c", 5018c2ecf20Sopenharmony_ci 'a' + devindex); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci /* 512 byte sectors */ 5048c2ecf20Sopenharmony_ci set_capacity(bdev->gendisk, scmdev->size >> 9); 5058c2ecf20Sopenharmony_ci device_add_disk(&scmdev->dev, bdev->gendisk, NULL); 5068c2ecf20Sopenharmony_ci return 0; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ciout_queue: 5098c2ecf20Sopenharmony_ci blk_cleanup_queue(rq); 5108c2ecf20Sopenharmony_ciout_tag: 5118c2ecf20Sopenharmony_ci blk_mq_free_tag_set(&bdev->tag_set); 5128c2ecf20Sopenharmony_ciout: 5138c2ecf20Sopenharmony_ci atomic_dec(&nr_devices); 5148c2ecf20Sopenharmony_ci return ret; 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_civoid scm_blk_dev_cleanup(struct scm_blk_dev *bdev) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci del_gendisk(bdev->gendisk); 5208c2ecf20Sopenharmony_ci blk_cleanup_queue(bdev->gendisk->queue); 5218c2ecf20Sopenharmony_ci blk_mq_free_tag_set(&bdev->tag_set); 5228c2ecf20Sopenharmony_ci put_disk(bdev->gendisk); 5238c2ecf20Sopenharmony_ci} 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_civoid scm_blk_set_available(struct scm_blk_dev *bdev) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci unsigned long flags; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci spin_lock_irqsave(&bdev->lock, flags); 5308c2ecf20Sopenharmony_ci if (bdev->state == SCM_WR_PROHIBIT) 5318c2ecf20Sopenharmony_ci pr_info("%lx: Write access to the SCM increment is restored\n", 5328c2ecf20Sopenharmony_ci (unsigned long) bdev->scmdev->address); 5338c2ecf20Sopenharmony_ci bdev->state = SCM_OPER; 5348c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bdev->lock, flags); 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_cistatic bool __init scm_blk_params_valid(void) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci if (!nr_requests_per_io || nr_requests_per_io > 64) 5408c2ecf20Sopenharmony_ci return false; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci return true; 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistatic int __init scm_blk_init(void) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci int ret = -EINVAL; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (!scm_blk_params_valid()) 5508c2ecf20Sopenharmony_ci goto out; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci ret = register_blkdev(0, "scm"); 5538c2ecf20Sopenharmony_ci if (ret < 0) 5548c2ecf20Sopenharmony_ci goto out; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci scm_major = ret; 5578c2ecf20Sopenharmony_ci ret = scm_alloc_rqs(nr_requests); 5588c2ecf20Sopenharmony_ci if (ret) 5598c2ecf20Sopenharmony_ci goto out_free; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci scm_debug = debug_register("scm_log", 16, 1, 16); 5628c2ecf20Sopenharmony_ci if (!scm_debug) { 5638c2ecf20Sopenharmony_ci ret = -ENOMEM; 5648c2ecf20Sopenharmony_ci goto out_free; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci debug_register_view(scm_debug, &debug_hex_ascii_view); 5688c2ecf20Sopenharmony_ci debug_set_level(scm_debug, 2); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci ret = scm_drv_init(); 5718c2ecf20Sopenharmony_ci if (ret) 5728c2ecf20Sopenharmony_ci goto out_dbf; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci return ret; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ciout_dbf: 5778c2ecf20Sopenharmony_ci debug_unregister(scm_debug); 5788c2ecf20Sopenharmony_ciout_free: 5798c2ecf20Sopenharmony_ci scm_free_rqs(); 5808c2ecf20Sopenharmony_ci unregister_blkdev(scm_major, "scm"); 5818c2ecf20Sopenharmony_ciout: 5828c2ecf20Sopenharmony_ci return ret; 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_cimodule_init(scm_blk_init); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_cistatic void __exit scm_blk_cleanup(void) 5878c2ecf20Sopenharmony_ci{ 5888c2ecf20Sopenharmony_ci scm_drv_cleanup(); 5898c2ecf20Sopenharmony_ci debug_unregister(scm_debug); 5908c2ecf20Sopenharmony_ci scm_free_rqs(); 5918c2ecf20Sopenharmony_ci unregister_blkdev(scm_major, "scm"); 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_cimodule_exit(scm_blk_cleanup); 594