/*
* Copyright (C) 2015 IT University of Copenhagen
* Initial release: Matias Bjorling <m@bjorling.me>
*
* 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 Round-robin page-based Hybrid FTL for Open-channel SSDs.
*/
#include "rrpc.h"
static struct kmem_cache *rrpc_gcb_cache, *rrpc_rq_cache;
static DECLARE_RWSEM(rrpc_lock);
static int rrpc_submit_io(struct rrpc *rrpc, struct bio *bio,
struct nvm_rq *rqd, unsigned long flags);
#define rrpc_for_each_lun(rrpc, rlun, i) \
for ((i) = 0, rlun = &(rrpc)->luns[0]; \
(i) < (rrpc)->nr_luns; (i)++, rlun = &(rrpc)->luns[(i)])
static void rrpc_page_invalidate(struct rrpc *rrpc, struct rrpc_addr *a)
{
struct nvm_tgt_dev *dev = rrpc->dev;
struct rrpc_block *rblk = a->rblk;
unsigned int pg_offset;
lockdep_assert_held(&rrpc->rev_lock);
if (a->addr == ADDR_EMPTY || !rblk)
return;
spin_lock(&rblk->lock);
div_u64_rem(a->addr, dev->geo.sec_per_blk, &pg_offset);
WARN_ON(test_and_set_bit(pg_offset, rblk->invalid_pages));
rblk->nr_invalid_pages++;
spin_unlock(&rblk->lock);
rrpc->rev_trans_map[a->addr].addr = ADDR_EMPTY;
}
static void rrpc_invalidate_range(struct rrpc *rrpc, sector_t slba,
unsigned int len)
{
sector_t i;
spin_lock(&rrpc->rev_lock);
for (i = slba; i < slba + len; i++) {
struct rrpc_addr *gp = &rrpc->trans_map[i];
rrpc_page_invalidate(rrpc, gp);
gp->rblk = NULL;
}
spin_unlock(&rrpc->rev_lock);
}
static struct nvm_rq *rrpc_inflight_laddr_acquire(struct rrpc *rrpc,
sector_t laddr, unsigned int pages)
{
struct nvm_rq *rqd;
struct rrpc_inflight_rq *inf;
rqd = mempool_alloc(rrpc->rq_pool, GFP_ATOMIC);
if (!rqd)
return ERR_PTR(-ENOMEM);
inf = rrpc_get_inflight_rq(rqd);
if (rrpc_lock_laddr(rrpc, laddr, pages, inf)) {
mempool_free(rqd, rrpc->rq_pool);
return NULL;
}
return rqd;
}
static void rrpc_inflight_laddr_release(struct rrpc *rrpc, struct nvm_rq *rqd)
{
struct rrpc_inflight_rq *inf = rrpc_get_inflight_rq(rqd);
rrpc_unlock_laddr(rrpc, inf);
mempool_free(rqd, rrpc->rq_pool);
}
static void rrpc_discard(struct rrpc *rrpc, struct bio *bio)
{
sector_t slba = bio->bi_iter.bi_sector / NR_PHY_IN_LOG;
sector_t len = bio->bi_iter.bi_size / RRPC_EXPOSED_PAGE_SIZE;
struct nvm_rq *rqd;
while (1) {
rqd = rrpc_inflight_laddr_acquire(rrpc, slba, len);
if (rqd)
break;
schedule();
}
if (IS_ERR(rqd)) {
pr_err("rrpc: unable to acquire inflight IO\n");
bio_io_error(bio);
return;
}
rrpc_invalidate_range(rrpc, slba, len);
rrpc_inflight_laddr_release(rrpc, rqd);
}
static int block_is_full(struct rrpc *rrpc, struct rrpc_block *rblk)
{
struct nvm_tgt_dev *dev = rrpc->dev;
return (rblk->next_page == dev->geo.sec_per_blk);
}
/* Calculate relative addr for the given block, considering instantiated LUNs */
static u64 block_to_rel_addr(struct rrpc *rrpc, struct rrpc_block *rblk)
{
struct nvm_tgt_dev *dev = rrpc->dev;
struct rrpc_lun *rlun = rblk->rlun;
return rlun->id * dev->geo.sec_per_blk;
}
static struct ppa_addr rrpc_ppa_to_gaddr(struct nvm_tgt_dev *dev,
struct rrpc_addr *gp)
{
struct rrpc_block *rblk = gp->rblk;
struct rrpc_lun *rlun = rblk->rlun;
u64 addr = gp->addr;
struct ppa_addr paddr;
paddr.ppa = addr;
paddr = rrpc_linear_to_generic_addr(&dev->geo, paddr);
paddr.g.ch
|