cregit-Linux how code gets into the kernel

Release 4.14 drivers/lightnvm/pblk-init.c

Directory: drivers/lightnvm
/*
 * Copyright (C) 2015 IT University of Copenhagen (rrpc.c)
 * Copyright (C) 2016 CNEX Labs
 * Initial release: Javier Gonzalez <javier@cnexlabs.com>
 *                  Matias Bjorling <matias@cnexlabs.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * Implementation of a physical block-device target for Open-channel SSDs.
 *
 * pblk-init.c - pblk's initialization.
 */

#include "pblk.h"




static struct kmem_cache *pblk_blk_ws_cache, *pblk_rec_cache, *pblk_g_rq_cache,
				

*pblk_w_rq_cache, *pblk_line_meta_cache;
static DECLARE_RWSEM(pblk_lock);

struct bio_set *pblk_bio_set;


static int pblk_rw_io(struct request_queue *q, struct pblk *pblk, struct bio *bio) { int ret; /* Read requests must be <= 256kb due to NVMe's 64 bit completion bitmap * constraint. Writes can be of arbitrary size. */ if (bio_data_dir(bio) == READ) { blk_queue_split(q, &bio); ret = pblk_submit_read(pblk, bio); if (ret == NVM_IO_DONE && bio_flagged(bio, BIO_CLONED)) bio_put(bio); return ret; } /* Prevent deadlock in the case of a modest LUN configuration and large * user I/Os. Unless stalled, the rate limiter leaves at least 256KB * available for user I/O. */ if (unlikely(pblk_get_secs(bio) >= pblk_rl_sysfs_rate_show(&pblk->rl))) blk_queue_split(q, &bio); return pblk_write_to_cache(pblk, bio, PBLK_IOTYPE_USER); }

Contributors

PersonTokensPropCommitsCommitProp
Javier González111100.00%1100.00%
Total111100.00%1100.00%


static blk_qc_t pblk_make_rq(struct request_queue *q, struct bio *bio) { struct pblk *pblk = q->queuedata; if (bio_op(bio) == REQ_OP_DISCARD) { pblk_discard(pblk, bio); if (!(bio->bi_opf & REQ_PREFLUSH)) { bio_endio(bio); return BLK_QC_T_NONE; } } switch (pblk_rw_io(q, pblk, bio)) { case NVM_IO_ERR: bio_io_error(bio); break; case NVM_IO_DONE: bio_endio(bio); break; } return BLK_QC_T_NONE; }

Contributors

PersonTokensPropCommitsCommitProp
Javier González98100.00%1100.00%
Total98100.00%1100.00%


static void pblk_l2p_free(struct pblk *pblk) { vfree(pblk->trans_map); }

Contributors

PersonTokensPropCommitsCommitProp
Javier González18100.00%1100.00%
Total18100.00%1100.00%


static int pblk_l2p_init(struct pblk *pblk) { sector_t i; struct ppa_addr ppa; int entry_size = 8; if (pblk->ppaf_bitsize < 32) entry_size = 4; pblk->trans_map = vmalloc(entry_size * pblk->rl.nr_secs); if (!pblk->trans_map) return -ENOMEM; pblk_ppa_set_empty(&ppa); for (i = 0; i < pblk->rl.nr_secs; i++) pblk_trans_map_set(pblk, i, ppa); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Javier González96100.00%1100.00%
Total96100.00%1100.00%


static void pblk_rwb_free(struct pblk *pblk) { if (pblk_rb_tear_down_check(&pblk->rwb)) pr_err("pblk: write buffer error on tear down\n"); pblk_rb_data_free(&pblk->rwb); vfree(pblk_rb_entries_ref(&pblk->rwb)); }

Contributors

PersonTokensPropCommitsCommitProp
Javier González45100.00%1100.00%
Total45100.00%1100.00%


static int pblk_rwb_init(struct pblk *pblk) { struct nvm_tgt_dev *dev = pblk->dev; struct nvm_geo *geo = &dev->geo; struct pblk_rb_entry *entries; unsigned long nr_entries; unsigned int power_size, power_seg_sz; nr_entries = pblk_rb_calculate_size(pblk->pgs_in_buffer); entries = vzalloc(nr_entries * sizeof(struct pblk_rb_entry)); if (!entries) return -ENOMEM; power_size = get_count_order(nr_entries); power_seg_sz = get_count_order(geo->sec_size); return pblk_rb_init(&pblk->rwb, entries, power_size, power_seg_sz); }

Contributors

PersonTokensPropCommitsCommitProp
Javier González107100.00%1100.00%
Total107100.00%1100.00%

/* Minimum pages needed within a lun */ #define PAGE_POOL_SIZE 16 #define ADDR_POOL_SIZE 64
static int pblk_set_ppaf(struct pblk *pblk) { struct nvm_tgt_dev *dev = pblk->dev; struct nvm_geo *geo = &dev->geo; struct nvm_addr_format ppaf = geo->ppaf; int power_len; /* Re-calculate channel and lun format to adapt to configuration */ power_len = get_count_order(geo->nr_chnls); if (1 << power_len != geo->nr_chnls) { pr_err("pblk: supports only power-of-two channel config.\n"); return -EINVAL; } ppaf.ch_len = power_len; power_len = get_count_order(geo->luns_per_chnl); if (1 << power_len != geo->luns_per_chnl) { pr_err("pblk: supports only power-of-two LUN config.\n"); return -EINVAL; } ppaf.lun_len = power_len; pblk->ppaf.sec_offset = 0; pblk->ppaf.pln_offset = ppaf.sect_len; pblk->ppaf.ch_offset = pblk->ppaf.pln_offset + ppaf.pln_len; pblk->ppaf.lun_offset = pblk->ppaf.ch_offset + ppaf.ch_len; pblk->ppaf.pg_offset = pblk->ppaf.lun_offset + ppaf.lun_len; pblk->ppaf.blk_offset = pblk->ppaf.pg_offset + ppaf.pg_len; pblk->ppaf.sec_mask = (1ULL << ppaf.sect_len) - 1; pblk->ppaf.pln_mask = ((1ULL << ppaf.pln_len) - 1) << pblk->ppaf.pln_offset; pblk->ppaf.ch_mask = ((1ULL << ppaf.ch_len) - 1) << pblk->ppaf.ch_offset; pblk->ppaf.lun_mask = ((1ULL << ppaf.lun_len) - 1) << pblk->ppaf.lun_offset; pblk->ppaf.pg_mask = ((1ULL << ppaf.pg_len) - 1) << pblk->ppaf.pg_offset; pblk->ppaf.blk_mask = ((1ULL << ppaf.blk_len) - 1) << pblk->ppaf.blk_offset; pblk->ppaf_bitsize = pblk->ppaf.blk_offset + ppaf.blk_len; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Javier González349100.00%1100.00%
Total349100.00%1100.00%


static int pblk_init_global_caches(struct pblk *pblk) { char cache_name[PBLK_CACHE_NAME_LEN]; down_write(&pblk_lock); pblk_blk_ws_cache = kmem_cache_create("pblk_blk_ws", sizeof(struct pblk_line_ws), 0, 0, NULL); if (!pblk_blk_ws_cache) { up_write(&pblk_lock); return -ENOMEM; } pblk_rec_cache = kmem_cache_create("pblk_rec", sizeof(struct pblk_rec_ctx), 0, 0, NULL); if (!pblk_rec_cache) { kmem_cache_destroy(pblk_blk_ws_cache); up_write(&pblk_lock); return -ENOMEM; } pblk_g_rq_cache = kmem_cache_create("pblk_g_rq", pblk_g_rq_size, 0, 0, NULL); if (!pblk_g_rq_cache) { kmem_cache_destroy(pblk_blk_ws_cache); kmem_cache_destroy(pblk_rec_cache); up_write(&pblk_lock); return -ENOMEM; } pblk_w_rq_cache = kmem_cache_create("pblk_w_rq", pblk_w_rq_size, 0, 0, NULL); if (!pblk_w_rq_cache) { kmem_cache_destroy(pblk_blk_ws_cache); kmem_cache_destroy(pblk_rec_cache); kmem_cache_destroy(pblk_g_rq_cache); up_write(&pblk_lock); return -ENOMEM; } snprintf(cache_name, sizeof(cache_name), "pblk_line_m_%s", pblk->disk->disk_name); pblk_line_meta_cache = kmem_cache_create(cache_name, pblk->lm.sec_bitmap_len, 0, 0, NULL); if (!pblk_line_meta_cache) { kmem_cache_destroy(pblk_blk_ws_cache); kmem_cache_destroy(pblk_rec_cache); kmem_cache_destroy(pblk_g_rq_cache); kmem_cache_destroy(pblk_w_rq_cache); up_write(&pblk_lock); return -ENOMEM; } up_write(&pblk_lock); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Javier González272100.00%2100.00%
Total272100.00%2100.00%


static int pblk_core_init(struct pblk *pblk) { struct nvm_tgt_dev *dev = pblk->dev; struct nvm_geo *geo = &dev->geo; pblk->pgs_in_buffer = NVM_MEM_PAGE_WRITE * geo->sec_per_pg * geo->nr_planes * geo->nr_luns; if (pblk_init_global_caches(pblk)) return -ENOMEM; pblk->page_pool = mempool_create_page_pool(PAGE_POOL_SIZE, 0); if (!pblk->page_pool) return -ENOMEM; pblk->line_ws_pool = mempool_create_slab_pool(PBLK_WS_POOL_SIZE, pblk_blk_ws_cache); if (!pblk->line_ws_pool) goto free_page_pool; pblk->rec_pool = mempool_create_slab_pool(geo->nr_luns, pblk_rec_cache); if (!pblk->rec_pool) goto free_blk_ws_pool; pblk->g_rq_pool = mempool_create_slab_pool(PBLK_READ_REQ_POOL_SIZE, pblk_g_rq_cache); if (!pblk->g_rq_pool) goto free_rec_pool; pblk->w_rq_pool = mempool_create_slab_pool(geo->nr_luns * 2, pblk_w_rq_cache); if (!pblk->w_rq_pool) goto free_g_rq_pool; pblk->line_meta_pool = mempool_create_slab_pool(PBLK_META_POOL_SIZE, pblk_line_meta_cache); if (!pblk->line_meta_pool) goto free_w_rq_pool; pblk->close_wq = alloc_workqueue("pblk-close-wq", WQ_MEM_RECLAIM | WQ_UNBOUND, PBLK_NR_CLOSE_JOBS); if (!pblk->close_wq) goto free_line_meta_pool; pblk->bb_wq = alloc_workqueue("pblk-bb-wq", WQ_MEM_RECLAIM | WQ_UNBOUND, 0); if (!pblk->bb_wq) goto free_close_wq; if (pblk_set_ppaf(pblk)) goto free_bb_wq; if (pblk_rwb_init(pblk)) goto free_bb_wq; INIT_LIST_HEAD(&pblk->compl_list); return 0; free_bb_wq: destroy_workqueue(pblk->bb_wq); free_close_wq: destroy_workqueue(pblk->close_wq); free_line_meta_pool: mempool_destroy(pblk->line_meta_pool); free_w_rq_pool: mempool_destroy(pblk->w_rq_pool); free_g_rq_pool: mempool_destroy(pblk->g_rq_pool); free_rec_pool: mempool_destroy(pblk->rec_pool); free_blk_ws_pool: mempool_destroy(pblk->line_ws_pool); free_page_pool: mempool_destroy(pblk->page_pool); return -ENOMEM; }

Contributors

PersonTokensPropCommitsCommitProp
Javier González349100.00%3100.00%
Total349100.00%3100.00%


static void pblk_core_free(struct pblk *pblk) { if (pblk->close_wq) destroy_workqueue(pblk->close_wq); if (pblk->bb_wq) destroy_workqueue(pblk->bb_wq); mempool_destroy(pblk->page_pool); mempool_destroy(pblk->line_ws_pool); mempool_destroy(pblk->rec_pool); mempool_destroy(pblk->g_rq_pool); mempool_destroy(pblk->w_rq_pool); mempool_destroy(pblk->line_meta_pool); kmem_cache_destroy(pblk_blk_ws_cache); kmem_cache_destroy(pblk_rec_cache); kmem_cache_destroy(pblk_g_rq_cache); kmem_cache_destroy(pblk_w_rq_cache); kmem_cache_destroy(pblk_line_meta_cache); }

Contributors

PersonTokensPropCommitsCommitProp
Javier González104100.00%3100.00%
Total104100.00%3100.00%


static void pblk_luns_free(struct pblk *pblk) { kfree(pblk->luns); }

Contributors

PersonTokensPropCommitsCommitProp
Javier González18100.00%1100.00%
Total18100.00%1100.00%


static void pblk_free_line_bitmaps(struct pblk_line *line) { kfree(line->blk_bitmap); kfree(line->erase_bitmap); }

Contributors

PersonTokensPropCommitsCommitProp
Javier González25100.00%1100.00%
Total25100.00%1100.00%


static void pblk_lines_free(struct pblk *pblk) { struct pblk_line_mgmt *l_mg = &pblk->l_mg; struct pblk_line *line; int i; spin_lock(&l_mg->free_lock); for (i = 0; i < l_mg->nr_lines; i++) { line = &pblk->lines[i]; pblk_line_free(pblk, line); pblk_free_line_bitmaps(line); } spin_unlock(&l_mg->free_lock); }

Contributors

PersonTokensPropCommitsCommitProp
Javier González84100.00%2100.00%
Total84100.00%2100.00%


static void pblk_line_meta_free(struct pblk *pblk) { struct pblk_line_mgmt *l_mg = &pblk->l_mg; int i; kfree(l_mg->bb_template); kfree(l_mg->bb_aux); kfree(l_mg->vsc_list); spin_lock(&l_mg->free_lock); for (i = 0; i < PBLK_DATA_LINES; i++) { kfree(l_mg->sline_meta[i]); pblk_mfree(l_mg->eline_meta[i]->buf, l_mg->emeta_alloc_type); kfree(l_mg->eline_meta[i]); } spin_unlock(&l_mg->free_lock); kfree(pblk->lines); }

Contributors

PersonTokensPropCommitsCommitProp
Javier González119100.00%4100.00%
Total119100.00%4100.00%


static int pblk_bb_discovery(struct nvm_tgt_dev *dev, struct pblk_lun *rlun) { struct nvm_geo *geo = &dev->geo; struct ppa_addr ppa; u8 *blks; int nr_blks, ret; nr_blks = geo->blks_per_lun * geo->plane_mode; blks = kmalloc(nr_blks, GFP_KERNEL); if (!blks) return -ENOMEM; ppa.ppa = 0; ppa.g.ch = rlun->bppa.g.ch; ppa.g.lun = rlun->bppa.g.lun; ret = nvm_get_tgt_bb_tbl(dev, ppa, blks); if (ret) goto out; nr_blks = nvm_bb_tbl_fold(dev->parent, blks, nr_blks); if (nr_blks < 0) { ret = nr_blks; goto out; } rlun->bb_list = blks; return 0; out: kfree(blks); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Javier González15593.37%150.00%
Wei Yongjun116.63%150.00%
Total166100.00%2100.00%


static int pblk_bb_line(struct pblk *pblk, struct pblk_line *line, int blk_per_line) { struct nvm_tgt_dev *dev = pblk->dev; struct nvm_geo *geo = &dev->geo; struct pblk_lun *rlun; int bb_cnt = 0; int i; for (i = 0; i < blk_per_line; i++) { rlun = &pblk->luns[i]; if (rlun->bb_list[line->id] == NVM_BLK_T_FREE) continue; set_bit(pblk_ppa_to_pos(geo, rlun->bppa), line->blk_bitmap); bb_cnt++; } return bb_cnt; }

Contributors

PersonTokensPropCommitsCommitProp
Javier González112100.00%3100.00%
Total112100.00%3100.00%


static int pblk_alloc_line_bitmaps(struct pblk *pblk, struct pblk_line *line) { struct pblk_line_meta *lm = &pblk->lm; line->blk_bitmap = kzalloc(lm->blk_bitmap_len, GFP_KERNEL); if (!line->blk_bitmap) return -ENOMEM; line->erase_bitmap = kzalloc(lm->blk_bitmap_len, GFP_KERNEL); if (!line->erase_bitmap) { kfree(line->blk_bitmap); return -ENOMEM; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Javier González86100.00%1100.00%
Total86100.00%1100.00%


static int pblk_luns_init(struct pblk *pblk, struct ppa_addr *luns) { struct nvm_tgt_dev *dev = pblk->dev; struct nvm_geo *geo = &dev->geo; struct pblk_lun *rlun; int i, ret; /* TODO: Implement unbalanced LUN support */ if (geo->luns_per_chnl < 0) { pr_err("pblk: unbalanced LUN config.\n"); return -EINVAL; } pblk->luns = kcalloc(geo->nr_luns, sizeof(struct pblk_lun), GFP_KERNEL); if (!pblk->luns) return -ENOMEM; for (i = 0; i < geo->nr_luns; i++) { /* Stripe across channels */ int ch = i % geo->nr_chnls; int lun_raw = i / geo->nr_chnls; int lunid = lun_raw + ch * geo->luns_per_chnl; rlun = &pblk->luns[i]; rlun->bppa = luns[lunid]; sema_init(&rlun->wr_sem, 1); ret = pblk_bb_discovery(dev, rlun); if (ret) { while (--i >= 0) kfree(pblk->luns[i].bb_list); return ret; } } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Javier González211100.00%1100.00%
Total211100.00%1100.00%


static int pblk_lines_configure(struct pblk *pblk, int flags) { struct pblk_line *line = NULL; int ret = 0; if (!(flags & NVM_TARGET_FACTORY)) { line = pblk_recov_l2p(pblk); if (IS_ERR(line)) { pr_err("pblk: could not recover l2p table\n"); ret = -EFAULT; } } if (!line) { /* Configure next line for user data */ line = pblk_line_get_first_data(pblk); if (!line) { pr_err("pblk: line list corrupted\n"); ret = -EFAULT; } } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Javier González98100.00%1100.00%
Total98100.00%1100.00%

/* See comment over struct line_emeta definition */
static unsigned int calc_emeta_len(struct pblk *pblk) { struct pblk_line_meta *lm = &pblk->lm; struct pblk_line_mgmt *l_mg = &pblk->l_mg; struct nvm_tgt_dev *dev = pblk->dev; struct nvm_geo *geo = &dev->geo; /* Round to sector size so that lba_list starts on its own sector */ lm->emeta_sec[1] = DIV_ROUND_UP( sizeof(struct line_emeta) + lm->blk_bitmap_len, geo->sec_size); lm->emeta_len[1] = lm->emeta_sec[1] * geo->sec_size; /* Round to sector size so that vsc_list starts on its own sector */ lm->dsec_per_line = lm->sec_per_line - lm->emeta_sec[0]; lm->emeta_sec[2] = DIV_ROUND_UP(lm->dsec_per_line * sizeof(u64), geo->sec_size); lm->emeta_len[2] = lm->emeta_sec[2] * geo->sec_size; lm->emeta_sec[3] = DIV_ROUND_UP(l_mg->nr_lines * sizeof(u32), geo->sec_size); lm->emeta_len[3] = lm->emeta_sec[3] * geo->sec_size; lm->vsc_list_len = l_mg->nr_lines * sizeof(u32); return (lm->emeta_len[1] + lm->emeta_len[2] + lm->emeta_len[3]); }

Contributors

PersonTokensPropCommitsCommitProp
Javier González229100.00%2100.00%
Total229100.00%2100.00%


static void pblk_set_provision(struct pblk *pblk, long nr_free_blks) { struct nvm_tgt_dev *dev = pblk->dev; struct nvm_geo *geo = &dev->geo; sector_t provisioned; pblk->over_pct = 20; provisioned = nr_free_blks; provisioned *= (100 - pblk->over_pct); sector_div(provisioned, 100); /* Internally pblk manages all free blocks, but all calculations based * on user capacity consider only provisioned blocks */ pblk->rl.total_blocks = nr_free_blks; pblk->rl.nr_secs = nr_free_blks * geo->sec_per_blk; pblk->capacity = provisioned * geo->sec_per_blk; atomic_set(&pblk->rl.free_blocks, nr_free_blks); }

Contributors

PersonTokensPropCommitsCommitProp
Javier González106100.00%2100.00%
Total106100.00%2100.00%


static int pblk_lines_alloc_metadata(struct pblk *pblk) { struct pblk_line_mgmt *l_mg = &pblk->l_mg; struct pblk_line_meta *lm = &pblk->lm; int i; /* smeta is always small enough to fit on a kmalloc memory allocation, * emeta depends on the number of LUNs allocated to the pblk instance */ for (i = 0; i < PBLK_DATA_LINES; i++) { l_mg->sline_meta[i] = kmalloc(lm->smeta_len, GFP_KERNEL); if (!l_mg->sline_meta[i]) goto fail_free_smeta; } /* emeta allocates three different buffers for managing metadata with * in-memory and in-media layouts */ for (i = 0; i < PBLK_DATA_LINES; i++) { struct pblk_emeta *emeta; emeta = kmalloc(sizeof(struct pblk_emeta), GFP_KERNEL); if (!emeta) goto fail_free_emeta; if (lm->emeta_len[0] > KMALLOC_MAX_CACHE_SIZE) { l_mg->emeta_alloc_type = PBLK_VMALLOC_META; emeta->buf = vmalloc(lm->emeta_len[0]); if (!emeta->buf) { kfree(emeta); goto fail_free_emeta; } emeta->nr_entries