linux/drivers/lightnvm/pblk-map.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2016 CNEX Labs
   3 * Initial release: Javier Gonzalez <javier@cnexlabs.com>
   4 *                  Matias Bjorling <matias@cnexlabs.com>
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License version
   8 * 2 as published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope that it will be useful, but
  11 * WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13 * General Public License for more details.
  14 *
  15 * pblk-map.c - pblk's lba-ppa mapping strategy
  16 *
  17 */
  18
  19#include "pblk.h"
  20
  21static int pblk_map_page_data(struct pblk *pblk, unsigned int sentry,
  22                              struct ppa_addr *ppa_list,
  23                              unsigned long *lun_bitmap,
  24                              struct pblk_sec_meta *meta_list,
  25                              unsigned int valid_secs)
  26{
  27        struct pblk_line *line = pblk_line_get_data(pblk);
  28        struct pblk_emeta *emeta;
  29        struct pblk_w_ctx *w_ctx;
  30        __le64 *lba_list;
  31        u64 paddr;
  32        int nr_secs = pblk->min_write_pgs;
  33        int i;
  34
  35        if (pblk_line_is_full(line)) {
  36                struct pblk_line *prev_line = line;
  37
  38                /* If we cannot allocate a new line, make sure to store metadata
  39                 * on current line and then fail
  40                 */
  41                line = pblk_line_replace_data(pblk);
  42                pblk_line_close_meta(pblk, prev_line);
  43
  44                if (!line)
  45                        return -EINTR;
  46        }
  47
  48        emeta = line->emeta;
  49        lba_list = emeta_to_lbas(pblk, emeta->buf);
  50
  51        paddr = pblk_alloc_page(pblk, line, nr_secs);
  52
  53        for (i = 0; i < nr_secs; i++, paddr++) {
  54                __le64 addr_empty = cpu_to_le64(ADDR_EMPTY);
  55
  56                /* ppa to be sent to the device */
  57                ppa_list[i] = addr_to_gen_ppa(pblk, paddr, line->id);
  58
  59                /* Write context for target bio completion on write buffer. Note
  60                 * that the write buffer is protected by the sync backpointer,
  61                 * and a single writer thread have access to each specific entry
  62                 * at a time. Thus, it is safe to modify the context for the
  63                 * entry we are setting up for submission without taking any
  64                 * lock or memory barrier.
  65                 */
  66                if (i < valid_secs) {
  67                        kref_get(&line->ref);
  68                        w_ctx = pblk_rb_w_ctx(&pblk->rwb, sentry + i);
  69                        w_ctx->ppa = ppa_list[i];
  70                        meta_list[i].lba = cpu_to_le64(w_ctx->lba);
  71                        lba_list[paddr] = cpu_to_le64(w_ctx->lba);
  72                        if (lba_list[paddr] != addr_empty)
  73                                line->nr_valid_lbas++;
  74                        else
  75                                atomic64_inc(&pblk->pad_wa);
  76                } else {
  77                        lba_list[paddr] = meta_list[i].lba = addr_empty;
  78                        __pblk_map_invalidate(pblk, line, paddr);
  79                }
  80        }
  81
  82        pblk_down_rq(pblk, ppa_list, nr_secs, lun_bitmap);
  83        return 0;
  84}
  85
  86void pblk_map_rq(struct pblk *pblk, struct nvm_rq *rqd, unsigned int sentry,
  87                 unsigned long *lun_bitmap, unsigned int valid_secs,
  88                 unsigned int off)
  89{
  90        struct pblk_sec_meta *meta_list = rqd->meta_list;
  91        unsigned int map_secs;
  92        int min = pblk->min_write_pgs;
  93        int i;
  94
  95        for (i = off; i < rqd->nr_ppas; i += min) {
  96                map_secs = (i + min > valid_secs) ? (valid_secs % min) : min;
  97                if (pblk_map_page_data(pblk, sentry + i, &rqd->ppa_list[i],
  98                                        lun_bitmap, &meta_list[i], map_secs)) {
  99                        bio_put(rqd->bio);
 100                        pblk_free_rqd(pblk, rqd, PBLK_WRITE);
 101                        pblk_pipeline_stop(pblk);
 102                }
 103        }
 104}
 105
 106/* only if erase_ppa is set, acquire erase semaphore */
 107void pblk_map_erase_rq(struct pblk *pblk, struct nvm_rq *rqd,
 108                       unsigned int sentry, unsigned long *lun_bitmap,
 109                       unsigned int valid_secs, struct ppa_addr *erase_ppa)
 110{
 111        struct nvm_tgt_dev *dev = pblk->dev;
 112        struct nvm_geo *geo = &dev->geo;
 113        struct pblk_line_meta *lm = &pblk->lm;
 114        struct pblk_sec_meta *meta_list = rqd->meta_list;
 115        struct pblk_line *e_line, *d_line;
 116        unsigned int map_secs;
 117        int min = pblk->min_write_pgs;
 118        int i, erase_lun;
 119
 120        for (i = 0; i < rqd->nr_ppas; i += min) {
 121                map_secs = (i + min > valid_secs) ? (valid_secs % min) : min;
 122                if (pblk_map_page_data(pblk, sentry + i, &rqd->ppa_list[i],
 123                                        lun_bitmap, &meta_list[i], map_secs)) {
 124                        bio_put(rqd->bio);
 125                        pblk_free_rqd(pblk, rqd, PBLK_WRITE);
 126                        pblk_pipeline_stop(pblk);
 127                }
 128
 129                erase_lun = pblk_ppa_to_pos(geo, rqd->ppa_list[i]);
 130
 131                /* line can change after page map. We might also be writing the
 132                 * last line.
 133                 */
 134                e_line = pblk_line_get_erase(pblk);
 135                if (!e_line)
 136                        return pblk_map_rq(pblk, rqd, sentry, lun_bitmap,
 137                                                        valid_secs, i + min);
 138
 139                spin_lock(&e_line->lock);
 140                if (!test_bit(erase_lun, e_line->erase_bitmap)) {
 141                        set_bit(erase_lun, e_line->erase_bitmap);
 142                        atomic_dec(&e_line->left_eblks);
 143
 144                        *erase_ppa = rqd->ppa_list[i];
 145                        erase_ppa->a.blk = e_line->id;
 146
 147                        spin_unlock(&e_line->lock);
 148
 149                        /* Avoid evaluating e_line->left_eblks */
 150                        return pblk_map_rq(pblk, rqd, sentry, lun_bitmap,
 151                                                        valid_secs, i + min);
 152                }
 153                spin_unlock(&e_line->lock);
 154        }
 155
 156        d_line = pblk_line_get_data(pblk);
 157
 158        /* line can change after page map. We might also be writing the
 159         * last line.
 160         */
 161        e_line = pblk_line_get_erase(pblk);
 162        if (!e_line)
 163                return;
 164
 165        /* Erase blocks that are bad in this line but might not be in next */
 166        if (unlikely(pblk_ppa_empty(*erase_ppa)) &&
 167                        bitmap_weight(d_line->blk_bitmap, lm->blk_per_line)) {
 168                int bit = -1;
 169
 170retry:
 171                bit = find_next_bit(d_line->blk_bitmap,
 172                                                lm->blk_per_line, bit + 1);
 173                if (bit >= lm->blk_per_line)
 174                        return;
 175
 176                spin_lock(&e_line->lock);
 177                if (test_bit(bit, e_line->erase_bitmap)) {
 178                        spin_unlock(&e_line->lock);
 179                        goto retry;
 180                }
 181                spin_unlock(&e_line->lock);
 182
 183                set_bit(bit, e_line->erase_bitmap);
 184                atomic_dec(&e_line->left_eblks);
 185                *erase_ppa = pblk->luns[bit].bppa; /* set ch and lun */
 186                erase_ppa->a.blk = e_line->id;
 187        }
 188}
 189