/*
* 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 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, rrpc->dev->pgs_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 - rrpc->poffset].addr = ADDR_EMPTY;
}
static void rrpc_invalidate_range(struct rrpc *rrpc, sector_t slba,
unsigned 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