18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2015 IT University of Copenhagen. All rights reserved. 48c2ecf20Sopenharmony_ci * Initial release: Matias Bjorling <m@bjorling.me> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "nvm: " fmt 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/list.h> 108c2ecf20Sopenharmony_ci#include <linux/types.h> 118c2ecf20Sopenharmony_ci#include <linux/sem.h> 128c2ecf20Sopenharmony_ci#include <linux/bitmap.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 158c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 168c2ecf20Sopenharmony_ci#include <linux/lightnvm.h> 178c2ecf20Sopenharmony_ci#include <linux/sched/sysctl.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic LIST_HEAD(nvm_tgt_types); 208c2ecf20Sopenharmony_cistatic DECLARE_RWSEM(nvm_tgtt_lock); 218c2ecf20Sopenharmony_cistatic LIST_HEAD(nvm_devices); 228c2ecf20Sopenharmony_cistatic DECLARE_RWSEM(nvm_lock); 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* Map between virtual and physical channel and lun */ 258c2ecf20Sopenharmony_cistruct nvm_ch_map { 268c2ecf20Sopenharmony_ci int ch_off; 278c2ecf20Sopenharmony_ci int num_lun; 288c2ecf20Sopenharmony_ci int *lun_offs; 298c2ecf20Sopenharmony_ci}; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct nvm_dev_map { 328c2ecf20Sopenharmony_ci struct nvm_ch_map *chnls; 338c2ecf20Sopenharmony_ci int num_ch; 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic void nvm_free(struct kref *ref); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic struct nvm_target *nvm_find_target(struct nvm_dev *dev, const char *name) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct nvm_target *tgt; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci list_for_each_entry(tgt, &dev->targets, list) 438c2ecf20Sopenharmony_ci if (!strcmp(name, tgt->disk->disk_name)) 448c2ecf20Sopenharmony_ci return tgt; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci return NULL; 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic bool nvm_target_exists(const char *name) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct nvm_dev *dev; 528c2ecf20Sopenharmony_ci struct nvm_target *tgt; 538c2ecf20Sopenharmony_ci bool ret = false; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci down_write(&nvm_lock); 568c2ecf20Sopenharmony_ci list_for_each_entry(dev, &nvm_devices, devices) { 578c2ecf20Sopenharmony_ci mutex_lock(&dev->mlock); 588c2ecf20Sopenharmony_ci list_for_each_entry(tgt, &dev->targets, list) { 598c2ecf20Sopenharmony_ci if (!strcmp(name, tgt->disk->disk_name)) { 608c2ecf20Sopenharmony_ci ret = true; 618c2ecf20Sopenharmony_ci mutex_unlock(&dev->mlock); 628c2ecf20Sopenharmony_ci goto out; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci mutex_unlock(&dev->mlock); 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ciout: 698c2ecf20Sopenharmony_ci up_write(&nvm_lock); 708c2ecf20Sopenharmony_ci return ret; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int nvm_reserve_luns(struct nvm_dev *dev, int lun_begin, int lun_end) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci int i; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci for (i = lun_begin; i <= lun_end; i++) { 788c2ecf20Sopenharmony_ci if (test_and_set_bit(i, dev->lun_map)) { 798c2ecf20Sopenharmony_ci pr_err("lun %d already allocated\n", i); 808c2ecf20Sopenharmony_ci goto err; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci return 0; 858c2ecf20Sopenharmony_cierr: 868c2ecf20Sopenharmony_ci while (--i >= lun_begin) 878c2ecf20Sopenharmony_ci clear_bit(i, dev->lun_map); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci return -EBUSY; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic void nvm_release_luns_err(struct nvm_dev *dev, int lun_begin, 938c2ecf20Sopenharmony_ci int lun_end) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci int i; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci for (i = lun_begin; i <= lun_end; i++) 988c2ecf20Sopenharmony_ci WARN_ON(!test_and_clear_bit(i, dev->lun_map)); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic void nvm_remove_tgt_dev(struct nvm_tgt_dev *tgt_dev, int clear) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct nvm_dev *dev = tgt_dev->parent; 1048c2ecf20Sopenharmony_ci struct nvm_dev_map *dev_map = tgt_dev->map; 1058c2ecf20Sopenharmony_ci int i, j; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci for (i = 0; i < dev_map->num_ch; i++) { 1088c2ecf20Sopenharmony_ci struct nvm_ch_map *ch_map = &dev_map->chnls[i]; 1098c2ecf20Sopenharmony_ci int *lun_offs = ch_map->lun_offs; 1108c2ecf20Sopenharmony_ci int ch = i + ch_map->ch_off; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (clear) { 1138c2ecf20Sopenharmony_ci for (j = 0; j < ch_map->num_lun; j++) { 1148c2ecf20Sopenharmony_ci int lun = j + lun_offs[j]; 1158c2ecf20Sopenharmony_ci int lunid = (ch * dev->geo.num_lun) + lun; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci WARN_ON(!test_and_clear_bit(lunid, 1188c2ecf20Sopenharmony_ci dev->lun_map)); 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci kfree(ch_map->lun_offs); 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci kfree(dev_map->chnls); 1268c2ecf20Sopenharmony_ci kfree(dev_map); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci kfree(tgt_dev->luns); 1298c2ecf20Sopenharmony_ci kfree(tgt_dev); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic struct nvm_tgt_dev *nvm_create_tgt_dev(struct nvm_dev *dev, 1338c2ecf20Sopenharmony_ci u16 lun_begin, u16 lun_end, 1348c2ecf20Sopenharmony_ci u16 op) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct nvm_tgt_dev *tgt_dev = NULL; 1378c2ecf20Sopenharmony_ci struct nvm_dev_map *dev_rmap = dev->rmap; 1388c2ecf20Sopenharmony_ci struct nvm_dev_map *dev_map; 1398c2ecf20Sopenharmony_ci struct ppa_addr *luns; 1408c2ecf20Sopenharmony_ci int num_lun = lun_end - lun_begin + 1; 1418c2ecf20Sopenharmony_ci int luns_left = num_lun; 1428c2ecf20Sopenharmony_ci int num_ch = num_lun / dev->geo.num_lun; 1438c2ecf20Sopenharmony_ci int num_ch_mod = num_lun % dev->geo.num_lun; 1448c2ecf20Sopenharmony_ci int bch = lun_begin / dev->geo.num_lun; 1458c2ecf20Sopenharmony_ci int blun = lun_begin % dev->geo.num_lun; 1468c2ecf20Sopenharmony_ci int lunid = 0; 1478c2ecf20Sopenharmony_ci int lun_balanced = 1; 1488c2ecf20Sopenharmony_ci int sec_per_lun, prev_num_lun; 1498c2ecf20Sopenharmony_ci int i, j; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci num_ch = (num_ch_mod == 0) ? num_ch : num_ch + 1; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci dev_map = kmalloc(sizeof(struct nvm_dev_map), GFP_KERNEL); 1548c2ecf20Sopenharmony_ci if (!dev_map) 1558c2ecf20Sopenharmony_ci goto err_dev; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci dev_map->chnls = kcalloc(num_ch, sizeof(struct nvm_ch_map), GFP_KERNEL); 1588c2ecf20Sopenharmony_ci if (!dev_map->chnls) 1598c2ecf20Sopenharmony_ci goto err_chnls; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci luns = kcalloc(num_lun, sizeof(struct ppa_addr), GFP_KERNEL); 1628c2ecf20Sopenharmony_ci if (!luns) 1638c2ecf20Sopenharmony_ci goto err_luns; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci prev_num_lun = (luns_left > dev->geo.num_lun) ? 1668c2ecf20Sopenharmony_ci dev->geo.num_lun : luns_left; 1678c2ecf20Sopenharmony_ci for (i = 0; i < num_ch; i++) { 1688c2ecf20Sopenharmony_ci struct nvm_ch_map *ch_rmap = &dev_rmap->chnls[i + bch]; 1698c2ecf20Sopenharmony_ci int *lun_roffs = ch_rmap->lun_offs; 1708c2ecf20Sopenharmony_ci struct nvm_ch_map *ch_map = &dev_map->chnls[i]; 1718c2ecf20Sopenharmony_ci int *lun_offs; 1728c2ecf20Sopenharmony_ci int luns_in_chnl = (luns_left > dev->geo.num_lun) ? 1738c2ecf20Sopenharmony_ci dev->geo.num_lun : luns_left; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (lun_balanced && prev_num_lun != luns_in_chnl) 1768c2ecf20Sopenharmony_ci lun_balanced = 0; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci ch_map->ch_off = ch_rmap->ch_off = bch; 1798c2ecf20Sopenharmony_ci ch_map->num_lun = luns_in_chnl; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci lun_offs = kcalloc(luns_in_chnl, sizeof(int), GFP_KERNEL); 1828c2ecf20Sopenharmony_ci if (!lun_offs) 1838c2ecf20Sopenharmony_ci goto err_ch; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci for (j = 0; j < luns_in_chnl; j++) { 1868c2ecf20Sopenharmony_ci luns[lunid].ppa = 0; 1878c2ecf20Sopenharmony_ci luns[lunid].a.ch = i; 1888c2ecf20Sopenharmony_ci luns[lunid++].a.lun = j; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci lun_offs[j] = blun; 1918c2ecf20Sopenharmony_ci lun_roffs[j + blun] = blun; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci ch_map->lun_offs = lun_offs; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* when starting a new channel, lun offset is reset */ 1978c2ecf20Sopenharmony_ci blun = 0; 1988c2ecf20Sopenharmony_ci luns_left -= luns_in_chnl; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci dev_map->num_ch = num_ch; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci tgt_dev = kmalloc(sizeof(struct nvm_tgt_dev), GFP_KERNEL); 2048c2ecf20Sopenharmony_ci if (!tgt_dev) 2058c2ecf20Sopenharmony_ci goto err_ch; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* Inherit device geometry from parent */ 2088c2ecf20Sopenharmony_ci memcpy(&tgt_dev->geo, &dev->geo, sizeof(struct nvm_geo)); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* Target device only owns a portion of the physical device */ 2118c2ecf20Sopenharmony_ci tgt_dev->geo.num_ch = num_ch; 2128c2ecf20Sopenharmony_ci tgt_dev->geo.num_lun = (lun_balanced) ? prev_num_lun : -1; 2138c2ecf20Sopenharmony_ci tgt_dev->geo.all_luns = num_lun; 2148c2ecf20Sopenharmony_ci tgt_dev->geo.all_chunks = num_lun * dev->geo.num_chk; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci tgt_dev->geo.op = op; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci sec_per_lun = dev->geo.clba * dev->geo.num_chk; 2198c2ecf20Sopenharmony_ci tgt_dev->geo.total_secs = num_lun * sec_per_lun; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci tgt_dev->q = dev->q; 2228c2ecf20Sopenharmony_ci tgt_dev->map = dev_map; 2238c2ecf20Sopenharmony_ci tgt_dev->luns = luns; 2248c2ecf20Sopenharmony_ci tgt_dev->parent = dev; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci return tgt_dev; 2278c2ecf20Sopenharmony_cierr_ch: 2288c2ecf20Sopenharmony_ci while (--i >= 0) 2298c2ecf20Sopenharmony_ci kfree(dev_map->chnls[i].lun_offs); 2308c2ecf20Sopenharmony_ci kfree(luns); 2318c2ecf20Sopenharmony_cierr_luns: 2328c2ecf20Sopenharmony_ci kfree(dev_map->chnls); 2338c2ecf20Sopenharmony_cierr_chnls: 2348c2ecf20Sopenharmony_ci kfree(dev_map); 2358c2ecf20Sopenharmony_cierr_dev: 2368c2ecf20Sopenharmony_ci return tgt_dev; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic struct nvm_tgt_type *__nvm_find_target_type(const char *name) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct nvm_tgt_type *tt; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci list_for_each_entry(tt, &nvm_tgt_types, list) 2448c2ecf20Sopenharmony_ci if (!strcmp(name, tt->name)) 2458c2ecf20Sopenharmony_ci return tt; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci return NULL; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic struct nvm_tgt_type *nvm_find_target_type(const char *name) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci struct nvm_tgt_type *tt; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci down_write(&nvm_tgtt_lock); 2558c2ecf20Sopenharmony_ci tt = __nvm_find_target_type(name); 2568c2ecf20Sopenharmony_ci up_write(&nvm_tgtt_lock); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci return tt; 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic int nvm_config_check_luns(struct nvm_geo *geo, int lun_begin, 2628c2ecf20Sopenharmony_ci int lun_end) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci if (lun_begin > lun_end || lun_end >= geo->all_luns) { 2658c2ecf20Sopenharmony_ci pr_err("lun out of bound (%u:%u > %u)\n", 2668c2ecf20Sopenharmony_ci lun_begin, lun_end, geo->all_luns - 1); 2678c2ecf20Sopenharmony_ci return -EINVAL; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic int __nvm_config_simple(struct nvm_dev *dev, 2748c2ecf20Sopenharmony_ci struct nvm_ioctl_create_simple *s) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct nvm_geo *geo = &dev->geo; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (s->lun_begin == -1 && s->lun_end == -1) { 2798c2ecf20Sopenharmony_ci s->lun_begin = 0; 2808c2ecf20Sopenharmony_ci s->lun_end = geo->all_luns - 1; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci return nvm_config_check_luns(geo, s->lun_begin, s->lun_end); 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic int __nvm_config_extended(struct nvm_dev *dev, 2878c2ecf20Sopenharmony_ci struct nvm_ioctl_create_extended *e) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci if (e->lun_begin == 0xFFFF && e->lun_end == 0xFFFF) { 2908c2ecf20Sopenharmony_ci e->lun_begin = 0; 2918c2ecf20Sopenharmony_ci e->lun_end = dev->geo.all_luns - 1; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* op not set falls into target's default */ 2958c2ecf20Sopenharmony_ci if (e->op == 0xFFFF) { 2968c2ecf20Sopenharmony_ci e->op = NVM_TARGET_DEFAULT_OP; 2978c2ecf20Sopenharmony_ci } else if (e->op < NVM_TARGET_MIN_OP || e->op > NVM_TARGET_MAX_OP) { 2988c2ecf20Sopenharmony_ci pr_err("invalid over provisioning value\n"); 2998c2ecf20Sopenharmony_ci return -EINVAL; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci return nvm_config_check_luns(&dev->geo, e->lun_begin, e->lun_end); 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic int nvm_create_tgt(struct nvm_dev *dev, struct nvm_ioctl_create *create) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct nvm_ioctl_create_extended e; 3088c2ecf20Sopenharmony_ci struct request_queue *tqueue; 3098c2ecf20Sopenharmony_ci struct gendisk *tdisk; 3108c2ecf20Sopenharmony_ci struct nvm_tgt_type *tt; 3118c2ecf20Sopenharmony_ci struct nvm_target *t; 3128c2ecf20Sopenharmony_ci struct nvm_tgt_dev *tgt_dev; 3138c2ecf20Sopenharmony_ci void *targetdata; 3148c2ecf20Sopenharmony_ci unsigned int mdts; 3158c2ecf20Sopenharmony_ci int ret; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci switch (create->conf.type) { 3188c2ecf20Sopenharmony_ci case NVM_CONFIG_TYPE_SIMPLE: 3198c2ecf20Sopenharmony_ci ret = __nvm_config_simple(dev, &create->conf.s); 3208c2ecf20Sopenharmony_ci if (ret) 3218c2ecf20Sopenharmony_ci return ret; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci e.lun_begin = create->conf.s.lun_begin; 3248c2ecf20Sopenharmony_ci e.lun_end = create->conf.s.lun_end; 3258c2ecf20Sopenharmony_ci e.op = NVM_TARGET_DEFAULT_OP; 3268c2ecf20Sopenharmony_ci break; 3278c2ecf20Sopenharmony_ci case NVM_CONFIG_TYPE_EXTENDED: 3288c2ecf20Sopenharmony_ci ret = __nvm_config_extended(dev, &create->conf.e); 3298c2ecf20Sopenharmony_ci if (ret) 3308c2ecf20Sopenharmony_ci return ret; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci e = create->conf.e; 3338c2ecf20Sopenharmony_ci break; 3348c2ecf20Sopenharmony_ci default: 3358c2ecf20Sopenharmony_ci pr_err("config type not valid\n"); 3368c2ecf20Sopenharmony_ci return -EINVAL; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci tt = nvm_find_target_type(create->tgttype); 3408c2ecf20Sopenharmony_ci if (!tt) { 3418c2ecf20Sopenharmony_ci pr_err("target type %s not found\n", create->tgttype); 3428c2ecf20Sopenharmony_ci return -EINVAL; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if ((tt->flags & NVM_TGT_F_HOST_L2P) != (dev->geo.dom & NVM_RSP_L2P)) { 3468c2ecf20Sopenharmony_ci pr_err("device is incompatible with target L2P type.\n"); 3478c2ecf20Sopenharmony_ci return -EINVAL; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (nvm_target_exists(create->tgtname)) { 3518c2ecf20Sopenharmony_ci pr_err("target name already exists (%s)\n", 3528c2ecf20Sopenharmony_ci create->tgtname); 3538c2ecf20Sopenharmony_ci return -EINVAL; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci ret = nvm_reserve_luns(dev, e.lun_begin, e.lun_end); 3578c2ecf20Sopenharmony_ci if (ret) 3588c2ecf20Sopenharmony_ci return ret; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci t = kmalloc(sizeof(struct nvm_target), GFP_KERNEL); 3618c2ecf20Sopenharmony_ci if (!t) { 3628c2ecf20Sopenharmony_ci ret = -ENOMEM; 3638c2ecf20Sopenharmony_ci goto err_reserve; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci tgt_dev = nvm_create_tgt_dev(dev, e.lun_begin, e.lun_end, e.op); 3678c2ecf20Sopenharmony_ci if (!tgt_dev) { 3688c2ecf20Sopenharmony_ci pr_err("could not create target device\n"); 3698c2ecf20Sopenharmony_ci ret = -ENOMEM; 3708c2ecf20Sopenharmony_ci goto err_t; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci tdisk = alloc_disk(0); 3748c2ecf20Sopenharmony_ci if (!tdisk) { 3758c2ecf20Sopenharmony_ci ret = -ENOMEM; 3768c2ecf20Sopenharmony_ci goto err_dev; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci tqueue = blk_alloc_queue(dev->q->node); 3808c2ecf20Sopenharmony_ci if (!tqueue) { 3818c2ecf20Sopenharmony_ci ret = -ENOMEM; 3828c2ecf20Sopenharmony_ci goto err_disk; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci strlcpy(tdisk->disk_name, create->tgtname, sizeof(tdisk->disk_name)); 3868c2ecf20Sopenharmony_ci tdisk->flags = GENHD_FL_EXT_DEVT; 3878c2ecf20Sopenharmony_ci tdisk->major = 0; 3888c2ecf20Sopenharmony_ci tdisk->first_minor = 0; 3898c2ecf20Sopenharmony_ci tdisk->fops = tt->bops; 3908c2ecf20Sopenharmony_ci tdisk->queue = tqueue; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci targetdata = tt->init(tgt_dev, tdisk, create->flags); 3938c2ecf20Sopenharmony_ci if (IS_ERR(targetdata)) { 3948c2ecf20Sopenharmony_ci ret = PTR_ERR(targetdata); 3958c2ecf20Sopenharmony_ci goto err_init; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci tdisk->private_data = targetdata; 3998c2ecf20Sopenharmony_ci tqueue->queuedata = targetdata; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci mdts = (dev->geo.csecs >> 9) * NVM_MAX_VLBA; 4028c2ecf20Sopenharmony_ci if (dev->geo.mdts) { 4038c2ecf20Sopenharmony_ci mdts = min_t(u32, dev->geo.mdts, 4048c2ecf20Sopenharmony_ci (dev->geo.csecs >> 9) * NVM_MAX_VLBA); 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci blk_queue_max_hw_sectors(tqueue, mdts); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci set_capacity(tdisk, tt->capacity(targetdata)); 4098c2ecf20Sopenharmony_ci add_disk(tdisk); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (tt->sysfs_init && tt->sysfs_init(tdisk)) { 4128c2ecf20Sopenharmony_ci ret = -ENOMEM; 4138c2ecf20Sopenharmony_ci goto err_sysfs; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci t->type = tt; 4178c2ecf20Sopenharmony_ci t->disk = tdisk; 4188c2ecf20Sopenharmony_ci t->dev = tgt_dev; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci mutex_lock(&dev->mlock); 4218c2ecf20Sopenharmony_ci list_add_tail(&t->list, &dev->targets); 4228c2ecf20Sopenharmony_ci mutex_unlock(&dev->mlock); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci __module_get(tt->owner); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci return 0; 4278c2ecf20Sopenharmony_cierr_sysfs: 4288c2ecf20Sopenharmony_ci if (tt->exit) 4298c2ecf20Sopenharmony_ci tt->exit(targetdata, true); 4308c2ecf20Sopenharmony_cierr_init: 4318c2ecf20Sopenharmony_ci blk_cleanup_queue(tqueue); 4328c2ecf20Sopenharmony_ci tdisk->queue = NULL; 4338c2ecf20Sopenharmony_cierr_disk: 4348c2ecf20Sopenharmony_ci put_disk(tdisk); 4358c2ecf20Sopenharmony_cierr_dev: 4368c2ecf20Sopenharmony_ci nvm_remove_tgt_dev(tgt_dev, 0); 4378c2ecf20Sopenharmony_cierr_t: 4388c2ecf20Sopenharmony_ci kfree(t); 4398c2ecf20Sopenharmony_cierr_reserve: 4408c2ecf20Sopenharmony_ci nvm_release_luns_err(dev, e.lun_begin, e.lun_end); 4418c2ecf20Sopenharmony_ci return ret; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic void __nvm_remove_target(struct nvm_target *t, bool graceful) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci struct nvm_tgt_type *tt = t->type; 4478c2ecf20Sopenharmony_ci struct gendisk *tdisk = t->disk; 4488c2ecf20Sopenharmony_ci struct request_queue *q = tdisk->queue; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci del_gendisk(tdisk); 4518c2ecf20Sopenharmony_ci blk_cleanup_queue(q); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if (tt->sysfs_exit) 4548c2ecf20Sopenharmony_ci tt->sysfs_exit(tdisk); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if (tt->exit) 4578c2ecf20Sopenharmony_ci tt->exit(tdisk->private_data, graceful); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci nvm_remove_tgt_dev(t->dev, 1); 4608c2ecf20Sopenharmony_ci put_disk(tdisk); 4618c2ecf20Sopenharmony_ci module_put(t->type->owner); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci list_del(&t->list); 4648c2ecf20Sopenharmony_ci kfree(t); 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci/** 4688c2ecf20Sopenharmony_ci * nvm_remove_tgt - Removes a target from the media manager 4698c2ecf20Sopenharmony_ci * @remove: ioctl structure with target name to remove. 4708c2ecf20Sopenharmony_ci * 4718c2ecf20Sopenharmony_ci * Returns: 4728c2ecf20Sopenharmony_ci * 0: on success 4738c2ecf20Sopenharmony_ci * 1: on not found 4748c2ecf20Sopenharmony_ci * <0: on error 4758c2ecf20Sopenharmony_ci */ 4768c2ecf20Sopenharmony_cistatic int nvm_remove_tgt(struct nvm_ioctl_remove *remove) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci struct nvm_target *t = NULL; 4798c2ecf20Sopenharmony_ci struct nvm_dev *dev; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci down_read(&nvm_lock); 4828c2ecf20Sopenharmony_ci list_for_each_entry(dev, &nvm_devices, devices) { 4838c2ecf20Sopenharmony_ci mutex_lock(&dev->mlock); 4848c2ecf20Sopenharmony_ci t = nvm_find_target(dev, remove->tgtname); 4858c2ecf20Sopenharmony_ci if (t) { 4868c2ecf20Sopenharmony_ci mutex_unlock(&dev->mlock); 4878c2ecf20Sopenharmony_ci break; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci mutex_unlock(&dev->mlock); 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci up_read(&nvm_lock); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci if (!t) { 4948c2ecf20Sopenharmony_ci pr_err("failed to remove target %s\n", 4958c2ecf20Sopenharmony_ci remove->tgtname); 4968c2ecf20Sopenharmony_ci return 1; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci __nvm_remove_target(t, true); 5008c2ecf20Sopenharmony_ci kref_put(&dev->ref, nvm_free); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci return 0; 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic int nvm_register_map(struct nvm_dev *dev) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci struct nvm_dev_map *rmap; 5088c2ecf20Sopenharmony_ci int i, j; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci rmap = kmalloc(sizeof(struct nvm_dev_map), GFP_KERNEL); 5118c2ecf20Sopenharmony_ci if (!rmap) 5128c2ecf20Sopenharmony_ci goto err_rmap; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci rmap->chnls = kcalloc(dev->geo.num_ch, sizeof(struct nvm_ch_map), 5158c2ecf20Sopenharmony_ci GFP_KERNEL); 5168c2ecf20Sopenharmony_ci if (!rmap->chnls) 5178c2ecf20Sopenharmony_ci goto err_chnls; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci for (i = 0; i < dev->geo.num_ch; i++) { 5208c2ecf20Sopenharmony_ci struct nvm_ch_map *ch_rmap; 5218c2ecf20Sopenharmony_ci int *lun_roffs; 5228c2ecf20Sopenharmony_ci int luns_in_chnl = dev->geo.num_lun; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci ch_rmap = &rmap->chnls[i]; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci ch_rmap->ch_off = -1; 5278c2ecf20Sopenharmony_ci ch_rmap->num_lun = luns_in_chnl; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci lun_roffs = kcalloc(luns_in_chnl, sizeof(int), GFP_KERNEL); 5308c2ecf20Sopenharmony_ci if (!lun_roffs) 5318c2ecf20Sopenharmony_ci goto err_ch; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci for (j = 0; j < luns_in_chnl; j++) 5348c2ecf20Sopenharmony_ci lun_roffs[j] = -1; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci ch_rmap->lun_offs = lun_roffs; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci dev->rmap = rmap; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci return 0; 5428c2ecf20Sopenharmony_cierr_ch: 5438c2ecf20Sopenharmony_ci while (--i >= 0) 5448c2ecf20Sopenharmony_ci kfree(rmap->chnls[i].lun_offs); 5458c2ecf20Sopenharmony_cierr_chnls: 5468c2ecf20Sopenharmony_ci kfree(rmap); 5478c2ecf20Sopenharmony_cierr_rmap: 5488c2ecf20Sopenharmony_ci return -ENOMEM; 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_cistatic void nvm_unregister_map(struct nvm_dev *dev) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci struct nvm_dev_map *rmap = dev->rmap; 5548c2ecf20Sopenharmony_ci int i; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci for (i = 0; i < dev->geo.num_ch; i++) 5578c2ecf20Sopenharmony_ci kfree(rmap->chnls[i].lun_offs); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci kfree(rmap->chnls); 5608c2ecf20Sopenharmony_ci kfree(rmap); 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic void nvm_map_to_dev(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *p) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci struct nvm_dev_map *dev_map = tgt_dev->map; 5668c2ecf20Sopenharmony_ci struct nvm_ch_map *ch_map = &dev_map->chnls[p->a.ch]; 5678c2ecf20Sopenharmony_ci int lun_off = ch_map->lun_offs[p->a.lun]; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci p->a.ch += ch_map->ch_off; 5708c2ecf20Sopenharmony_ci p->a.lun += lun_off; 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cistatic void nvm_map_to_tgt(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *p) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci struct nvm_dev *dev = tgt_dev->parent; 5768c2ecf20Sopenharmony_ci struct nvm_dev_map *dev_rmap = dev->rmap; 5778c2ecf20Sopenharmony_ci struct nvm_ch_map *ch_rmap = &dev_rmap->chnls[p->a.ch]; 5788c2ecf20Sopenharmony_ci int lun_roff = ch_rmap->lun_offs[p->a.lun]; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci p->a.ch -= ch_rmap->ch_off; 5818c2ecf20Sopenharmony_ci p->a.lun -= lun_roff; 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_cistatic void nvm_ppa_tgt_to_dev(struct nvm_tgt_dev *tgt_dev, 5858c2ecf20Sopenharmony_ci struct ppa_addr *ppa_list, int nr_ppas) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci int i; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci for (i = 0; i < nr_ppas; i++) { 5908c2ecf20Sopenharmony_ci nvm_map_to_dev(tgt_dev, &ppa_list[i]); 5918c2ecf20Sopenharmony_ci ppa_list[i] = generic_to_dev_addr(tgt_dev->parent, ppa_list[i]); 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic void nvm_ppa_dev_to_tgt(struct nvm_tgt_dev *tgt_dev, 5968c2ecf20Sopenharmony_ci struct ppa_addr *ppa_list, int nr_ppas) 5978c2ecf20Sopenharmony_ci{ 5988c2ecf20Sopenharmony_ci int i; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci for (i = 0; i < nr_ppas; i++) { 6018c2ecf20Sopenharmony_ci ppa_list[i] = dev_to_generic_addr(tgt_dev->parent, ppa_list[i]); 6028c2ecf20Sopenharmony_ci nvm_map_to_tgt(tgt_dev, &ppa_list[i]); 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci} 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_cistatic void nvm_rq_tgt_to_dev(struct nvm_tgt_dev *tgt_dev, struct nvm_rq *rqd) 6078c2ecf20Sopenharmony_ci{ 6088c2ecf20Sopenharmony_ci struct ppa_addr *ppa_list = nvm_rq_to_ppa_list(rqd); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci nvm_ppa_tgt_to_dev(tgt_dev, ppa_list, rqd->nr_ppas); 6118c2ecf20Sopenharmony_ci} 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_cistatic void nvm_rq_dev_to_tgt(struct nvm_tgt_dev *tgt_dev, struct nvm_rq *rqd) 6148c2ecf20Sopenharmony_ci{ 6158c2ecf20Sopenharmony_ci struct ppa_addr *ppa_list = nvm_rq_to_ppa_list(rqd); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci nvm_ppa_dev_to_tgt(tgt_dev, ppa_list, rqd->nr_ppas); 6188c2ecf20Sopenharmony_ci} 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ciint nvm_register_tgt_type(struct nvm_tgt_type *tt) 6218c2ecf20Sopenharmony_ci{ 6228c2ecf20Sopenharmony_ci int ret = 0; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci down_write(&nvm_tgtt_lock); 6258c2ecf20Sopenharmony_ci if (__nvm_find_target_type(tt->name)) 6268c2ecf20Sopenharmony_ci ret = -EEXIST; 6278c2ecf20Sopenharmony_ci else 6288c2ecf20Sopenharmony_ci list_add(&tt->list, &nvm_tgt_types); 6298c2ecf20Sopenharmony_ci up_write(&nvm_tgtt_lock); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci return ret; 6328c2ecf20Sopenharmony_ci} 6338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nvm_register_tgt_type); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_civoid nvm_unregister_tgt_type(struct nvm_tgt_type *tt) 6368c2ecf20Sopenharmony_ci{ 6378c2ecf20Sopenharmony_ci if (!tt) 6388c2ecf20Sopenharmony_ci return; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci down_write(&nvm_tgtt_lock); 6418c2ecf20Sopenharmony_ci list_del(&tt->list); 6428c2ecf20Sopenharmony_ci up_write(&nvm_tgtt_lock); 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nvm_unregister_tgt_type); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_civoid *nvm_dev_dma_alloc(struct nvm_dev *dev, gfp_t mem_flags, 6478c2ecf20Sopenharmony_ci dma_addr_t *dma_handler) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci return dev->ops->dev_dma_alloc(dev, dev->dma_pool, mem_flags, 6508c2ecf20Sopenharmony_ci dma_handler); 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nvm_dev_dma_alloc); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_civoid nvm_dev_dma_free(struct nvm_dev *dev, void *addr, dma_addr_t dma_handler) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci dev->ops->dev_dma_free(dev->dma_pool, addr, dma_handler); 6578c2ecf20Sopenharmony_ci} 6588c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nvm_dev_dma_free); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_cistatic struct nvm_dev *nvm_find_nvm_dev(const char *name) 6618c2ecf20Sopenharmony_ci{ 6628c2ecf20Sopenharmony_ci struct nvm_dev *dev; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci list_for_each_entry(dev, &nvm_devices, devices) 6658c2ecf20Sopenharmony_ci if (!strcmp(name, dev->name)) 6668c2ecf20Sopenharmony_ci return dev; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci return NULL; 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_cistatic int nvm_set_rqd_ppalist(struct nvm_tgt_dev *tgt_dev, struct nvm_rq *rqd, 6728c2ecf20Sopenharmony_ci const struct ppa_addr *ppas, int nr_ppas) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci struct nvm_dev *dev = tgt_dev->parent; 6758c2ecf20Sopenharmony_ci struct nvm_geo *geo = &tgt_dev->geo; 6768c2ecf20Sopenharmony_ci int i, plane_cnt, pl_idx; 6778c2ecf20Sopenharmony_ci struct ppa_addr ppa; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if (geo->pln_mode == NVM_PLANE_SINGLE && nr_ppas == 1) { 6808c2ecf20Sopenharmony_ci rqd->nr_ppas = nr_ppas; 6818c2ecf20Sopenharmony_ci rqd->ppa_addr = ppas[0]; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci return 0; 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci rqd->nr_ppas = nr_ppas; 6878c2ecf20Sopenharmony_ci rqd->ppa_list = nvm_dev_dma_alloc(dev, GFP_KERNEL, &rqd->dma_ppa_list); 6888c2ecf20Sopenharmony_ci if (!rqd->ppa_list) { 6898c2ecf20Sopenharmony_ci pr_err("failed to allocate dma memory\n"); 6908c2ecf20Sopenharmony_ci return -ENOMEM; 6918c2ecf20Sopenharmony_ci } 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci plane_cnt = geo->pln_mode; 6948c2ecf20Sopenharmony_ci rqd->nr_ppas *= plane_cnt; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci for (i = 0; i < nr_ppas; i++) { 6978c2ecf20Sopenharmony_ci for (pl_idx = 0; pl_idx < plane_cnt; pl_idx++) { 6988c2ecf20Sopenharmony_ci ppa = ppas[i]; 6998c2ecf20Sopenharmony_ci ppa.g.pl = pl_idx; 7008c2ecf20Sopenharmony_ci rqd->ppa_list[(pl_idx * nr_ppas) + i] = ppa; 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci return 0; 7058c2ecf20Sopenharmony_ci} 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cistatic void nvm_free_rqd_ppalist(struct nvm_tgt_dev *tgt_dev, 7088c2ecf20Sopenharmony_ci struct nvm_rq *rqd) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci if (!rqd->ppa_list) 7118c2ecf20Sopenharmony_ci return; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci nvm_dev_dma_free(tgt_dev->parent, rqd->ppa_list, rqd->dma_ppa_list); 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic int nvm_set_flags(struct nvm_geo *geo, struct nvm_rq *rqd) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci int flags = 0; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci if (geo->version == NVM_OCSSD_SPEC_20) 7218c2ecf20Sopenharmony_ci return 0; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci if (rqd->is_seq) 7248c2ecf20Sopenharmony_ci flags |= geo->pln_mode >> 1; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci if (rqd->opcode == NVM_OP_PREAD) 7278c2ecf20Sopenharmony_ci flags |= (NVM_IO_SCRAMBLE_ENABLE | NVM_IO_SUSPEND); 7288c2ecf20Sopenharmony_ci else if (rqd->opcode == NVM_OP_PWRITE) 7298c2ecf20Sopenharmony_ci flags |= NVM_IO_SCRAMBLE_ENABLE; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci return flags; 7328c2ecf20Sopenharmony_ci} 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ciint nvm_submit_io(struct nvm_tgt_dev *tgt_dev, struct nvm_rq *rqd, void *buf) 7358c2ecf20Sopenharmony_ci{ 7368c2ecf20Sopenharmony_ci struct nvm_dev *dev = tgt_dev->parent; 7378c2ecf20Sopenharmony_ci int ret; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci if (!dev->ops->submit_io) 7408c2ecf20Sopenharmony_ci return -ENODEV; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci nvm_rq_tgt_to_dev(tgt_dev, rqd); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci rqd->dev = tgt_dev; 7458c2ecf20Sopenharmony_ci rqd->flags = nvm_set_flags(&tgt_dev->geo, rqd); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci /* In case of error, fail with right address format */ 7488c2ecf20Sopenharmony_ci ret = dev->ops->submit_io(dev, rqd, buf); 7498c2ecf20Sopenharmony_ci if (ret) 7508c2ecf20Sopenharmony_ci nvm_rq_dev_to_tgt(tgt_dev, rqd); 7518c2ecf20Sopenharmony_ci return ret; 7528c2ecf20Sopenharmony_ci} 7538c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nvm_submit_io); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_cistatic void nvm_sync_end_io(struct nvm_rq *rqd) 7568c2ecf20Sopenharmony_ci{ 7578c2ecf20Sopenharmony_ci struct completion *waiting = rqd->private; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci complete(waiting); 7608c2ecf20Sopenharmony_ci} 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_cistatic int nvm_submit_io_wait(struct nvm_dev *dev, struct nvm_rq *rqd, 7638c2ecf20Sopenharmony_ci void *buf) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(wait); 7668c2ecf20Sopenharmony_ci int ret = 0; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci rqd->end_io = nvm_sync_end_io; 7698c2ecf20Sopenharmony_ci rqd->private = &wait; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci ret = dev->ops->submit_io(dev, rqd, buf); 7728c2ecf20Sopenharmony_ci if (ret) 7738c2ecf20Sopenharmony_ci return ret; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci wait_for_completion_io(&wait); 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci return 0; 7788c2ecf20Sopenharmony_ci} 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ciint nvm_submit_io_sync(struct nvm_tgt_dev *tgt_dev, struct nvm_rq *rqd, 7818c2ecf20Sopenharmony_ci void *buf) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci struct nvm_dev *dev = tgt_dev->parent; 7848c2ecf20Sopenharmony_ci int ret; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci if (!dev->ops->submit_io) 7878c2ecf20Sopenharmony_ci return -ENODEV; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci nvm_rq_tgt_to_dev(tgt_dev, rqd); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci rqd->dev = tgt_dev; 7928c2ecf20Sopenharmony_ci rqd->flags = nvm_set_flags(&tgt_dev->geo, rqd); 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci ret = nvm_submit_io_wait(dev, rqd, buf); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci return ret; 7978c2ecf20Sopenharmony_ci} 7988c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nvm_submit_io_sync); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_civoid nvm_end_io(struct nvm_rq *rqd) 8018c2ecf20Sopenharmony_ci{ 8028c2ecf20Sopenharmony_ci struct nvm_tgt_dev *tgt_dev = rqd->dev; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci /* Convert address space */ 8058c2ecf20Sopenharmony_ci if (tgt_dev) 8068c2ecf20Sopenharmony_ci nvm_rq_dev_to_tgt(tgt_dev, rqd); 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci if (rqd->end_io) 8098c2ecf20Sopenharmony_ci rqd->end_io(rqd); 8108c2ecf20Sopenharmony_ci} 8118c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nvm_end_io); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_cistatic int nvm_submit_io_sync_raw(struct nvm_dev *dev, struct nvm_rq *rqd) 8148c2ecf20Sopenharmony_ci{ 8158c2ecf20Sopenharmony_ci if (!dev->ops->submit_io) 8168c2ecf20Sopenharmony_ci return -ENODEV; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci rqd->dev = NULL; 8198c2ecf20Sopenharmony_ci rqd->flags = nvm_set_flags(&dev->geo, rqd); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci return nvm_submit_io_wait(dev, rqd, NULL); 8228c2ecf20Sopenharmony_ci} 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_cistatic int nvm_bb_chunk_sense(struct nvm_dev *dev, struct ppa_addr ppa) 8258c2ecf20Sopenharmony_ci{ 8268c2ecf20Sopenharmony_ci struct nvm_rq rqd = { NULL }; 8278c2ecf20Sopenharmony_ci struct bio bio; 8288c2ecf20Sopenharmony_ci struct bio_vec bio_vec; 8298c2ecf20Sopenharmony_ci struct page *page; 8308c2ecf20Sopenharmony_ci int ret; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci page = alloc_page(GFP_KERNEL); 8338c2ecf20Sopenharmony_ci if (!page) 8348c2ecf20Sopenharmony_ci return -ENOMEM; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci bio_init(&bio, &bio_vec, 1); 8378c2ecf20Sopenharmony_ci bio_add_page(&bio, page, PAGE_SIZE, 0); 8388c2ecf20Sopenharmony_ci bio_set_op_attrs(&bio, REQ_OP_READ, 0); 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci rqd.bio = &bio; 8418c2ecf20Sopenharmony_ci rqd.opcode = NVM_OP_PREAD; 8428c2ecf20Sopenharmony_ci rqd.is_seq = 1; 8438c2ecf20Sopenharmony_ci rqd.nr_ppas = 1; 8448c2ecf20Sopenharmony_ci rqd.ppa_addr = generic_to_dev_addr(dev, ppa); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci ret = nvm_submit_io_sync_raw(dev, &rqd); 8478c2ecf20Sopenharmony_ci __free_page(page); 8488c2ecf20Sopenharmony_ci if (ret) 8498c2ecf20Sopenharmony_ci return ret; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci return rqd.error; 8528c2ecf20Sopenharmony_ci} 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci/* 8558c2ecf20Sopenharmony_ci * Scans a 1.2 chunk first and last page to determine if its state. 8568c2ecf20Sopenharmony_ci * If the chunk is found to be open, also scan it to update the write 8578c2ecf20Sopenharmony_ci * pointer. 8588c2ecf20Sopenharmony_ci */ 8598c2ecf20Sopenharmony_cistatic int nvm_bb_chunk_scan(struct nvm_dev *dev, struct ppa_addr ppa, 8608c2ecf20Sopenharmony_ci struct nvm_chk_meta *meta) 8618c2ecf20Sopenharmony_ci{ 8628c2ecf20Sopenharmony_ci struct nvm_geo *geo = &dev->geo; 8638c2ecf20Sopenharmony_ci int ret, pg, pl; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci /* sense first page */ 8668c2ecf20Sopenharmony_ci ret = nvm_bb_chunk_sense(dev, ppa); 8678c2ecf20Sopenharmony_ci if (ret < 0) /* io error */ 8688c2ecf20Sopenharmony_ci return ret; 8698c2ecf20Sopenharmony_ci else if (ret == 0) /* valid data */ 8708c2ecf20Sopenharmony_ci meta->state = NVM_CHK_ST_OPEN; 8718c2ecf20Sopenharmony_ci else if (ret > 0) { 8728c2ecf20Sopenharmony_ci /* 8738c2ecf20Sopenharmony_ci * If empty page, the chunk is free, else it is an 8748c2ecf20Sopenharmony_ci * actual io error. In that case, mark it offline. 8758c2ecf20Sopenharmony_ci */ 8768c2ecf20Sopenharmony_ci switch (ret) { 8778c2ecf20Sopenharmony_ci case NVM_RSP_ERR_EMPTYPAGE: 8788c2ecf20Sopenharmony_ci meta->state = NVM_CHK_ST_FREE; 8798c2ecf20Sopenharmony_ci return 0; 8808c2ecf20Sopenharmony_ci case NVM_RSP_ERR_FAILCRC: 8818c2ecf20Sopenharmony_ci case NVM_RSP_ERR_FAILECC: 8828c2ecf20Sopenharmony_ci case NVM_RSP_WARN_HIGHECC: 8838c2ecf20Sopenharmony_ci meta->state = NVM_CHK_ST_OPEN; 8848c2ecf20Sopenharmony_ci goto scan; 8858c2ecf20Sopenharmony_ci default: 8868c2ecf20Sopenharmony_ci return -ret; /* other io error */ 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci } 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci /* sense last page */ 8918c2ecf20Sopenharmony_ci ppa.g.pg = geo->num_pg - 1; 8928c2ecf20Sopenharmony_ci ppa.g.pl = geo->num_pln - 1; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci ret = nvm_bb_chunk_sense(dev, ppa); 8958c2ecf20Sopenharmony_ci if (ret < 0) /* io error */ 8968c2ecf20Sopenharmony_ci return ret; 8978c2ecf20Sopenharmony_ci else if (ret == 0) { /* Chunk fully written */ 8988c2ecf20Sopenharmony_ci meta->state = NVM_CHK_ST_CLOSED; 8998c2ecf20Sopenharmony_ci meta->wp = geo->clba; 9008c2ecf20Sopenharmony_ci return 0; 9018c2ecf20Sopenharmony_ci } else if (ret > 0) { 9028c2ecf20Sopenharmony_ci switch (ret) { 9038c2ecf20Sopenharmony_ci case NVM_RSP_ERR_EMPTYPAGE: 9048c2ecf20Sopenharmony_ci case NVM_RSP_ERR_FAILCRC: 9058c2ecf20Sopenharmony_ci case NVM_RSP_ERR_FAILECC: 9068c2ecf20Sopenharmony_ci case NVM_RSP_WARN_HIGHECC: 9078c2ecf20Sopenharmony_ci meta->state = NVM_CHK_ST_OPEN; 9088c2ecf20Sopenharmony_ci break; 9098c2ecf20Sopenharmony_ci default: 9108c2ecf20Sopenharmony_ci return -ret; /* other io error */ 9118c2ecf20Sopenharmony_ci } 9128c2ecf20Sopenharmony_ci } 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ciscan: 9158c2ecf20Sopenharmony_ci /* 9168c2ecf20Sopenharmony_ci * chunk is open, we scan sequentially to update the write pointer. 9178c2ecf20Sopenharmony_ci * We make the assumption that targets write data across all planes 9188c2ecf20Sopenharmony_ci * before moving to the next page. 9198c2ecf20Sopenharmony_ci */ 9208c2ecf20Sopenharmony_ci for (pg = 0; pg < geo->num_pg; pg++) { 9218c2ecf20Sopenharmony_ci for (pl = 0; pl < geo->num_pln; pl++) { 9228c2ecf20Sopenharmony_ci ppa.g.pg = pg; 9238c2ecf20Sopenharmony_ci ppa.g.pl = pl; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci ret = nvm_bb_chunk_sense(dev, ppa); 9268c2ecf20Sopenharmony_ci if (ret < 0) /* io error */ 9278c2ecf20Sopenharmony_ci return ret; 9288c2ecf20Sopenharmony_ci else if (ret == 0) { 9298c2ecf20Sopenharmony_ci meta->wp += geo->ws_min; 9308c2ecf20Sopenharmony_ci } else if (ret > 0) { 9318c2ecf20Sopenharmony_ci switch (ret) { 9328c2ecf20Sopenharmony_ci case NVM_RSP_ERR_EMPTYPAGE: 9338c2ecf20Sopenharmony_ci return 0; 9348c2ecf20Sopenharmony_ci case NVM_RSP_ERR_FAILCRC: 9358c2ecf20Sopenharmony_ci case NVM_RSP_ERR_FAILECC: 9368c2ecf20Sopenharmony_ci case NVM_RSP_WARN_HIGHECC: 9378c2ecf20Sopenharmony_ci meta->wp += geo->ws_min; 9388c2ecf20Sopenharmony_ci break; 9398c2ecf20Sopenharmony_ci default: 9408c2ecf20Sopenharmony_ci return -ret; /* other io error */ 9418c2ecf20Sopenharmony_ci } 9428c2ecf20Sopenharmony_ci } 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci } 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci return 0; 9478c2ecf20Sopenharmony_ci} 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci/* 9508c2ecf20Sopenharmony_ci * folds a bad block list from its plane representation to its 9518c2ecf20Sopenharmony_ci * chunk representation. 9528c2ecf20Sopenharmony_ci * 9538c2ecf20Sopenharmony_ci * If any of the planes status are bad or grown bad, the chunk is marked 9548c2ecf20Sopenharmony_ci * offline. If not bad, the first plane state acts as the chunk state. 9558c2ecf20Sopenharmony_ci */ 9568c2ecf20Sopenharmony_cistatic int nvm_bb_to_chunk(struct nvm_dev *dev, struct ppa_addr ppa, 9578c2ecf20Sopenharmony_ci u8 *blks, int nr_blks, struct nvm_chk_meta *meta) 9588c2ecf20Sopenharmony_ci{ 9598c2ecf20Sopenharmony_ci struct nvm_geo *geo = &dev->geo; 9608c2ecf20Sopenharmony_ci int ret, blk, pl, offset, blktype; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci for (blk = 0; blk < geo->num_chk; blk++) { 9638c2ecf20Sopenharmony_ci offset = blk * geo->pln_mode; 9648c2ecf20Sopenharmony_ci blktype = blks[offset]; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci for (pl = 0; pl < geo->pln_mode; pl++) { 9678c2ecf20Sopenharmony_ci if (blks[offset + pl] & 9688c2ecf20Sopenharmony_ci (NVM_BLK_T_BAD|NVM_BLK_T_GRWN_BAD)) { 9698c2ecf20Sopenharmony_ci blktype = blks[offset + pl]; 9708c2ecf20Sopenharmony_ci break; 9718c2ecf20Sopenharmony_ci } 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci ppa.g.blk = blk; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci meta->wp = 0; 9778c2ecf20Sopenharmony_ci meta->type = NVM_CHK_TP_W_SEQ; 9788c2ecf20Sopenharmony_ci meta->wi = 0; 9798c2ecf20Sopenharmony_ci meta->slba = generic_to_dev_addr(dev, ppa).ppa; 9808c2ecf20Sopenharmony_ci meta->cnlb = dev->geo.clba; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci if (blktype == NVM_BLK_T_FREE) { 9838c2ecf20Sopenharmony_ci ret = nvm_bb_chunk_scan(dev, ppa, meta); 9848c2ecf20Sopenharmony_ci if (ret) 9858c2ecf20Sopenharmony_ci return ret; 9868c2ecf20Sopenharmony_ci } else { 9878c2ecf20Sopenharmony_ci meta->state = NVM_CHK_ST_OFFLINE; 9888c2ecf20Sopenharmony_ci } 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci meta++; 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci return 0; 9948c2ecf20Sopenharmony_ci} 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_cistatic int nvm_get_bb_meta(struct nvm_dev *dev, sector_t slba, 9978c2ecf20Sopenharmony_ci int nchks, struct nvm_chk_meta *meta) 9988c2ecf20Sopenharmony_ci{ 9998c2ecf20Sopenharmony_ci struct nvm_geo *geo = &dev->geo; 10008c2ecf20Sopenharmony_ci struct ppa_addr ppa; 10018c2ecf20Sopenharmony_ci u8 *blks; 10028c2ecf20Sopenharmony_ci int ch, lun, nr_blks; 10038c2ecf20Sopenharmony_ci int ret = 0; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci ppa.ppa = slba; 10068c2ecf20Sopenharmony_ci ppa = dev_to_generic_addr(dev, ppa); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci if (ppa.g.blk != 0) 10098c2ecf20Sopenharmony_ci return -EINVAL; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci if ((nchks % geo->num_chk) != 0) 10128c2ecf20Sopenharmony_ci return -EINVAL; 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci nr_blks = geo->num_chk * geo->pln_mode; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci blks = kmalloc(nr_blks, GFP_KERNEL); 10178c2ecf20Sopenharmony_ci if (!blks) 10188c2ecf20Sopenharmony_ci return -ENOMEM; 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci for (ch = ppa.g.ch; ch < geo->num_ch; ch++) { 10218c2ecf20Sopenharmony_ci for (lun = ppa.g.lun; lun < geo->num_lun; lun++) { 10228c2ecf20Sopenharmony_ci struct ppa_addr ppa_gen, ppa_dev; 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci if (!nchks) 10258c2ecf20Sopenharmony_ci goto done; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci ppa_gen.ppa = 0; 10288c2ecf20Sopenharmony_ci ppa_gen.g.ch = ch; 10298c2ecf20Sopenharmony_ci ppa_gen.g.lun = lun; 10308c2ecf20Sopenharmony_ci ppa_dev = generic_to_dev_addr(dev, ppa_gen); 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci ret = dev->ops->get_bb_tbl(dev, ppa_dev, blks); 10338c2ecf20Sopenharmony_ci if (ret) 10348c2ecf20Sopenharmony_ci goto done; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci ret = nvm_bb_to_chunk(dev, ppa_gen, blks, nr_blks, 10378c2ecf20Sopenharmony_ci meta); 10388c2ecf20Sopenharmony_ci if (ret) 10398c2ecf20Sopenharmony_ci goto done; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci meta += geo->num_chk; 10428c2ecf20Sopenharmony_ci nchks -= geo->num_chk; 10438c2ecf20Sopenharmony_ci } 10448c2ecf20Sopenharmony_ci } 10458c2ecf20Sopenharmony_cidone: 10468c2ecf20Sopenharmony_ci kfree(blks); 10478c2ecf20Sopenharmony_ci return ret; 10488c2ecf20Sopenharmony_ci} 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ciint nvm_get_chunk_meta(struct nvm_tgt_dev *tgt_dev, struct ppa_addr ppa, 10518c2ecf20Sopenharmony_ci int nchks, struct nvm_chk_meta *meta) 10528c2ecf20Sopenharmony_ci{ 10538c2ecf20Sopenharmony_ci struct nvm_dev *dev = tgt_dev->parent; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci nvm_ppa_tgt_to_dev(tgt_dev, &ppa, 1); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci if (dev->geo.version == NVM_OCSSD_SPEC_12) 10588c2ecf20Sopenharmony_ci return nvm_get_bb_meta(dev, (sector_t)ppa.ppa, nchks, meta); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci return dev->ops->get_chk_meta(dev, (sector_t)ppa.ppa, nchks, meta); 10618c2ecf20Sopenharmony_ci} 10628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvm_get_chunk_meta); 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ciint nvm_set_chunk_meta(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *ppas, 10658c2ecf20Sopenharmony_ci int nr_ppas, int type) 10668c2ecf20Sopenharmony_ci{ 10678c2ecf20Sopenharmony_ci struct nvm_dev *dev = tgt_dev->parent; 10688c2ecf20Sopenharmony_ci struct nvm_rq rqd; 10698c2ecf20Sopenharmony_ci int ret; 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci if (dev->geo.version == NVM_OCSSD_SPEC_20) 10728c2ecf20Sopenharmony_ci return 0; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci if (nr_ppas > NVM_MAX_VLBA) { 10758c2ecf20Sopenharmony_ci pr_err("unable to update all blocks atomically\n"); 10768c2ecf20Sopenharmony_ci return -EINVAL; 10778c2ecf20Sopenharmony_ci } 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci memset(&rqd, 0, sizeof(struct nvm_rq)); 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci nvm_set_rqd_ppalist(tgt_dev, &rqd, ppas, nr_ppas); 10828c2ecf20Sopenharmony_ci nvm_rq_tgt_to_dev(tgt_dev, &rqd); 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci ret = dev->ops->set_bb_tbl(dev, &rqd.ppa_addr, rqd.nr_ppas, type); 10858c2ecf20Sopenharmony_ci nvm_free_rqd_ppalist(tgt_dev, &rqd); 10868c2ecf20Sopenharmony_ci if (ret) 10878c2ecf20Sopenharmony_ci return -EINVAL; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci return 0; 10908c2ecf20Sopenharmony_ci} 10918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvm_set_chunk_meta); 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_cistatic int nvm_core_init(struct nvm_dev *dev) 10948c2ecf20Sopenharmony_ci{ 10958c2ecf20Sopenharmony_ci struct nvm_geo *geo = &dev->geo; 10968c2ecf20Sopenharmony_ci int ret; 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci dev->lun_map = kcalloc(BITS_TO_LONGS(geo->all_luns), 10998c2ecf20Sopenharmony_ci sizeof(unsigned long), GFP_KERNEL); 11008c2ecf20Sopenharmony_ci if (!dev->lun_map) 11018c2ecf20Sopenharmony_ci return -ENOMEM; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dev->area_list); 11048c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dev->targets); 11058c2ecf20Sopenharmony_ci mutex_init(&dev->mlock); 11068c2ecf20Sopenharmony_ci spin_lock_init(&dev->lock); 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci ret = nvm_register_map(dev); 11098c2ecf20Sopenharmony_ci if (ret) 11108c2ecf20Sopenharmony_ci goto err_fmtype; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci return 0; 11138c2ecf20Sopenharmony_cierr_fmtype: 11148c2ecf20Sopenharmony_ci kfree(dev->lun_map); 11158c2ecf20Sopenharmony_ci return ret; 11168c2ecf20Sopenharmony_ci} 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_cistatic void nvm_free(struct kref *ref) 11198c2ecf20Sopenharmony_ci{ 11208c2ecf20Sopenharmony_ci struct nvm_dev *dev = container_of(ref, struct nvm_dev, ref); 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci if (dev->dma_pool) 11238c2ecf20Sopenharmony_ci dev->ops->destroy_dma_pool(dev->dma_pool); 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci if (dev->rmap) 11268c2ecf20Sopenharmony_ci nvm_unregister_map(dev); 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci kfree(dev->lun_map); 11298c2ecf20Sopenharmony_ci kfree(dev); 11308c2ecf20Sopenharmony_ci} 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_cistatic int nvm_init(struct nvm_dev *dev) 11338c2ecf20Sopenharmony_ci{ 11348c2ecf20Sopenharmony_ci struct nvm_geo *geo = &dev->geo; 11358c2ecf20Sopenharmony_ci int ret = -EINVAL; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci if (dev->ops->identity(dev)) { 11388c2ecf20Sopenharmony_ci pr_err("device could not be identified\n"); 11398c2ecf20Sopenharmony_ci goto err; 11408c2ecf20Sopenharmony_ci } 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci pr_debug("ver:%u.%u nvm_vendor:%x\n", geo->major_ver_id, 11438c2ecf20Sopenharmony_ci geo->minor_ver_id, geo->vmnt); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci ret = nvm_core_init(dev); 11468c2ecf20Sopenharmony_ci if (ret) { 11478c2ecf20Sopenharmony_ci pr_err("could not initialize core structures.\n"); 11488c2ecf20Sopenharmony_ci goto err; 11498c2ecf20Sopenharmony_ci } 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci pr_info("registered %s [%u/%u/%u/%u/%u]\n", 11528c2ecf20Sopenharmony_ci dev->name, dev->geo.ws_min, dev->geo.ws_opt, 11538c2ecf20Sopenharmony_ci dev->geo.num_chk, dev->geo.all_luns, 11548c2ecf20Sopenharmony_ci dev->geo.num_ch); 11558c2ecf20Sopenharmony_ci return 0; 11568c2ecf20Sopenharmony_cierr: 11578c2ecf20Sopenharmony_ci pr_err("failed to initialize nvm\n"); 11588c2ecf20Sopenharmony_ci return ret; 11598c2ecf20Sopenharmony_ci} 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_cistruct nvm_dev *nvm_alloc_dev(int node) 11628c2ecf20Sopenharmony_ci{ 11638c2ecf20Sopenharmony_ci struct nvm_dev *dev; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci dev = kzalloc_node(sizeof(struct nvm_dev), GFP_KERNEL, node); 11668c2ecf20Sopenharmony_ci if (dev) 11678c2ecf20Sopenharmony_ci kref_init(&dev->ref); 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci return dev; 11708c2ecf20Sopenharmony_ci} 11718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nvm_alloc_dev); 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ciint nvm_register(struct nvm_dev *dev) 11748c2ecf20Sopenharmony_ci{ 11758c2ecf20Sopenharmony_ci int ret, exp_pool_size; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci if (!dev->q || !dev->ops) { 11788c2ecf20Sopenharmony_ci kref_put(&dev->ref, nvm_free); 11798c2ecf20Sopenharmony_ci return -EINVAL; 11808c2ecf20Sopenharmony_ci } 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci ret = nvm_init(dev); 11838c2ecf20Sopenharmony_ci if (ret) { 11848c2ecf20Sopenharmony_ci kref_put(&dev->ref, nvm_free); 11858c2ecf20Sopenharmony_ci return ret; 11868c2ecf20Sopenharmony_ci } 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci exp_pool_size = max_t(int, PAGE_SIZE, 11898c2ecf20Sopenharmony_ci (NVM_MAX_VLBA * (sizeof(u64) + dev->geo.sos))); 11908c2ecf20Sopenharmony_ci exp_pool_size = round_up(exp_pool_size, PAGE_SIZE); 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci dev->dma_pool = dev->ops->create_dma_pool(dev, "ppalist", 11938c2ecf20Sopenharmony_ci exp_pool_size); 11948c2ecf20Sopenharmony_ci if (!dev->dma_pool) { 11958c2ecf20Sopenharmony_ci pr_err("could not create dma pool\n"); 11968c2ecf20Sopenharmony_ci kref_put(&dev->ref, nvm_free); 11978c2ecf20Sopenharmony_ci return -ENOMEM; 11988c2ecf20Sopenharmony_ci } 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci /* register device with a supported media manager */ 12018c2ecf20Sopenharmony_ci down_write(&nvm_lock); 12028c2ecf20Sopenharmony_ci list_add(&dev->devices, &nvm_devices); 12038c2ecf20Sopenharmony_ci up_write(&nvm_lock); 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci return 0; 12068c2ecf20Sopenharmony_ci} 12078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nvm_register); 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_civoid nvm_unregister(struct nvm_dev *dev) 12108c2ecf20Sopenharmony_ci{ 12118c2ecf20Sopenharmony_ci struct nvm_target *t, *tmp; 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci mutex_lock(&dev->mlock); 12148c2ecf20Sopenharmony_ci list_for_each_entry_safe(t, tmp, &dev->targets, list) { 12158c2ecf20Sopenharmony_ci if (t->dev->parent != dev) 12168c2ecf20Sopenharmony_ci continue; 12178c2ecf20Sopenharmony_ci __nvm_remove_target(t, false); 12188c2ecf20Sopenharmony_ci kref_put(&dev->ref, nvm_free); 12198c2ecf20Sopenharmony_ci } 12208c2ecf20Sopenharmony_ci mutex_unlock(&dev->mlock); 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci down_write(&nvm_lock); 12238c2ecf20Sopenharmony_ci list_del(&dev->devices); 12248c2ecf20Sopenharmony_ci up_write(&nvm_lock); 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci kref_put(&dev->ref, nvm_free); 12278c2ecf20Sopenharmony_ci} 12288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nvm_unregister); 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_cistatic int __nvm_configure_create(struct nvm_ioctl_create *create) 12318c2ecf20Sopenharmony_ci{ 12328c2ecf20Sopenharmony_ci struct nvm_dev *dev; 12338c2ecf20Sopenharmony_ci int ret; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci down_write(&nvm_lock); 12368c2ecf20Sopenharmony_ci dev = nvm_find_nvm_dev(create->dev); 12378c2ecf20Sopenharmony_ci up_write(&nvm_lock); 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci if (!dev) { 12408c2ecf20Sopenharmony_ci pr_err("device not found\n"); 12418c2ecf20Sopenharmony_ci return -EINVAL; 12428c2ecf20Sopenharmony_ci } 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci kref_get(&dev->ref); 12458c2ecf20Sopenharmony_ci ret = nvm_create_tgt(dev, create); 12468c2ecf20Sopenharmony_ci if (ret) 12478c2ecf20Sopenharmony_ci kref_put(&dev->ref, nvm_free); 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci return ret; 12508c2ecf20Sopenharmony_ci} 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_cistatic long nvm_ioctl_info(struct file *file, void __user *arg) 12538c2ecf20Sopenharmony_ci{ 12548c2ecf20Sopenharmony_ci struct nvm_ioctl_info *info; 12558c2ecf20Sopenharmony_ci struct nvm_tgt_type *tt; 12568c2ecf20Sopenharmony_ci int tgt_iter = 0; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci info = memdup_user(arg, sizeof(struct nvm_ioctl_info)); 12598c2ecf20Sopenharmony_ci if (IS_ERR(info)) 12608c2ecf20Sopenharmony_ci return -EFAULT; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci info->version[0] = NVM_VERSION_MAJOR; 12638c2ecf20Sopenharmony_ci info->version[1] = NVM_VERSION_MINOR; 12648c2ecf20Sopenharmony_ci info->version[2] = NVM_VERSION_PATCH; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci down_write(&nvm_tgtt_lock); 12678c2ecf20Sopenharmony_ci list_for_each_entry(tt, &nvm_tgt_types, list) { 12688c2ecf20Sopenharmony_ci struct nvm_ioctl_info_tgt *tgt = &info->tgts[tgt_iter]; 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci tgt->version[0] = tt->version[0]; 12718c2ecf20Sopenharmony_ci tgt->version[1] = tt->version[1]; 12728c2ecf20Sopenharmony_ci tgt->version[2] = tt->version[2]; 12738c2ecf20Sopenharmony_ci strncpy(tgt->tgtname, tt->name, NVM_TTYPE_NAME_MAX); 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci tgt_iter++; 12768c2ecf20Sopenharmony_ci } 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci info->tgtsize = tgt_iter; 12798c2ecf20Sopenharmony_ci up_write(&nvm_tgtt_lock); 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci if (copy_to_user(arg, info, sizeof(struct nvm_ioctl_info))) { 12828c2ecf20Sopenharmony_ci kfree(info); 12838c2ecf20Sopenharmony_ci return -EFAULT; 12848c2ecf20Sopenharmony_ci } 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci kfree(info); 12878c2ecf20Sopenharmony_ci return 0; 12888c2ecf20Sopenharmony_ci} 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_cistatic long nvm_ioctl_get_devices(struct file *file, void __user *arg) 12918c2ecf20Sopenharmony_ci{ 12928c2ecf20Sopenharmony_ci struct nvm_ioctl_get_devices *devices; 12938c2ecf20Sopenharmony_ci struct nvm_dev *dev; 12948c2ecf20Sopenharmony_ci int i = 0; 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci devices = kzalloc(sizeof(struct nvm_ioctl_get_devices), GFP_KERNEL); 12978c2ecf20Sopenharmony_ci if (!devices) 12988c2ecf20Sopenharmony_ci return -ENOMEM; 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci down_write(&nvm_lock); 13018c2ecf20Sopenharmony_ci list_for_each_entry(dev, &nvm_devices, devices) { 13028c2ecf20Sopenharmony_ci struct nvm_ioctl_device_info *info = &devices->info[i]; 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci strlcpy(info->devname, dev->name, sizeof(info->devname)); 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci /* kept for compatibility */ 13078c2ecf20Sopenharmony_ci info->bmversion[0] = 1; 13088c2ecf20Sopenharmony_ci info->bmversion[1] = 0; 13098c2ecf20Sopenharmony_ci info->bmversion[2] = 0; 13108c2ecf20Sopenharmony_ci strlcpy(info->bmname, "gennvm", sizeof(info->bmname)); 13118c2ecf20Sopenharmony_ci i++; 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci if (i >= ARRAY_SIZE(devices->info)) { 13148c2ecf20Sopenharmony_ci pr_err("max %zd devices can be reported.\n", 13158c2ecf20Sopenharmony_ci ARRAY_SIZE(devices->info)); 13168c2ecf20Sopenharmony_ci break; 13178c2ecf20Sopenharmony_ci } 13188c2ecf20Sopenharmony_ci } 13198c2ecf20Sopenharmony_ci up_write(&nvm_lock); 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci devices->nr_devices = i; 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci if (copy_to_user(arg, devices, 13248c2ecf20Sopenharmony_ci sizeof(struct nvm_ioctl_get_devices))) { 13258c2ecf20Sopenharmony_ci kfree(devices); 13268c2ecf20Sopenharmony_ci return -EFAULT; 13278c2ecf20Sopenharmony_ci } 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci kfree(devices); 13308c2ecf20Sopenharmony_ci return 0; 13318c2ecf20Sopenharmony_ci} 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_cistatic long nvm_ioctl_dev_create(struct file *file, void __user *arg) 13348c2ecf20Sopenharmony_ci{ 13358c2ecf20Sopenharmony_ci struct nvm_ioctl_create create; 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci if (copy_from_user(&create, arg, sizeof(struct nvm_ioctl_create))) 13388c2ecf20Sopenharmony_ci return -EFAULT; 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci if (create.conf.type == NVM_CONFIG_TYPE_EXTENDED && 13418c2ecf20Sopenharmony_ci create.conf.e.rsv != 0) { 13428c2ecf20Sopenharmony_ci pr_err("reserved config field in use\n"); 13438c2ecf20Sopenharmony_ci return -EINVAL; 13448c2ecf20Sopenharmony_ci } 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci create.dev[DISK_NAME_LEN - 1] = '\0'; 13478c2ecf20Sopenharmony_ci create.tgttype[NVM_TTYPE_NAME_MAX - 1] = '\0'; 13488c2ecf20Sopenharmony_ci create.tgtname[DISK_NAME_LEN - 1] = '\0'; 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci if (create.flags != 0) { 13518c2ecf20Sopenharmony_ci __u32 flags = create.flags; 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci /* Check for valid flags */ 13548c2ecf20Sopenharmony_ci if (flags & NVM_TARGET_FACTORY) 13558c2ecf20Sopenharmony_ci flags &= ~NVM_TARGET_FACTORY; 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci if (flags) { 13588c2ecf20Sopenharmony_ci pr_err("flag not supported\n"); 13598c2ecf20Sopenharmony_ci return -EINVAL; 13608c2ecf20Sopenharmony_ci } 13618c2ecf20Sopenharmony_ci } 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci return __nvm_configure_create(&create); 13648c2ecf20Sopenharmony_ci} 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_cistatic long nvm_ioctl_dev_remove(struct file *file, void __user *arg) 13678c2ecf20Sopenharmony_ci{ 13688c2ecf20Sopenharmony_ci struct nvm_ioctl_remove remove; 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci if (copy_from_user(&remove, arg, sizeof(struct nvm_ioctl_remove))) 13718c2ecf20Sopenharmony_ci return -EFAULT; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci remove.tgtname[DISK_NAME_LEN - 1] = '\0'; 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci if (remove.flags != 0) { 13768c2ecf20Sopenharmony_ci pr_err("no flags supported\n"); 13778c2ecf20Sopenharmony_ci return -EINVAL; 13788c2ecf20Sopenharmony_ci } 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci return nvm_remove_tgt(&remove); 13818c2ecf20Sopenharmony_ci} 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci/* kept for compatibility reasons */ 13848c2ecf20Sopenharmony_cistatic long nvm_ioctl_dev_init(struct file *file, void __user *arg) 13858c2ecf20Sopenharmony_ci{ 13868c2ecf20Sopenharmony_ci struct nvm_ioctl_dev_init init; 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci if (copy_from_user(&init, arg, sizeof(struct nvm_ioctl_dev_init))) 13898c2ecf20Sopenharmony_ci return -EFAULT; 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci if (init.flags != 0) { 13928c2ecf20Sopenharmony_ci pr_err("no flags supported\n"); 13938c2ecf20Sopenharmony_ci return -EINVAL; 13948c2ecf20Sopenharmony_ci } 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci return 0; 13978c2ecf20Sopenharmony_ci} 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci/* Kept for compatibility reasons */ 14008c2ecf20Sopenharmony_cistatic long nvm_ioctl_dev_factory(struct file *file, void __user *arg) 14018c2ecf20Sopenharmony_ci{ 14028c2ecf20Sopenharmony_ci struct nvm_ioctl_dev_factory fact; 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci if (copy_from_user(&fact, arg, sizeof(struct nvm_ioctl_dev_factory))) 14058c2ecf20Sopenharmony_ci return -EFAULT; 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci fact.dev[DISK_NAME_LEN - 1] = '\0'; 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci if (fact.flags & ~(NVM_FACTORY_NR_BITS - 1)) 14108c2ecf20Sopenharmony_ci return -EINVAL; 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci return 0; 14138c2ecf20Sopenharmony_ci} 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_cistatic long nvm_ctl_ioctl(struct file *file, uint cmd, unsigned long arg) 14168c2ecf20Sopenharmony_ci{ 14178c2ecf20Sopenharmony_ci void __user *argp = (void __user *)arg; 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 14208c2ecf20Sopenharmony_ci return -EPERM; 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci switch (cmd) { 14238c2ecf20Sopenharmony_ci case NVM_INFO: 14248c2ecf20Sopenharmony_ci return nvm_ioctl_info(file, argp); 14258c2ecf20Sopenharmony_ci case NVM_GET_DEVICES: 14268c2ecf20Sopenharmony_ci return nvm_ioctl_get_devices(file, argp); 14278c2ecf20Sopenharmony_ci case NVM_DEV_CREATE: 14288c2ecf20Sopenharmony_ci return nvm_ioctl_dev_create(file, argp); 14298c2ecf20Sopenharmony_ci case NVM_DEV_REMOVE: 14308c2ecf20Sopenharmony_ci return nvm_ioctl_dev_remove(file, argp); 14318c2ecf20Sopenharmony_ci case NVM_DEV_INIT: 14328c2ecf20Sopenharmony_ci return nvm_ioctl_dev_init(file, argp); 14338c2ecf20Sopenharmony_ci case NVM_DEV_FACTORY: 14348c2ecf20Sopenharmony_ci return nvm_ioctl_dev_factory(file, argp); 14358c2ecf20Sopenharmony_ci } 14368c2ecf20Sopenharmony_ci return 0; 14378c2ecf20Sopenharmony_ci} 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_cistatic const struct file_operations _ctl_fops = { 14408c2ecf20Sopenharmony_ci .open = nonseekable_open, 14418c2ecf20Sopenharmony_ci .unlocked_ioctl = nvm_ctl_ioctl, 14428c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 14438c2ecf20Sopenharmony_ci .llseek = noop_llseek, 14448c2ecf20Sopenharmony_ci}; 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_cistatic struct miscdevice _nvm_misc = { 14478c2ecf20Sopenharmony_ci .minor = MISC_DYNAMIC_MINOR, 14488c2ecf20Sopenharmony_ci .name = "lightnvm", 14498c2ecf20Sopenharmony_ci .nodename = "lightnvm/control", 14508c2ecf20Sopenharmony_ci .fops = &_ctl_fops, 14518c2ecf20Sopenharmony_ci}; 14528c2ecf20Sopenharmony_cibuiltin_misc_device(_nvm_misc); 1453