/*
* Block Translation Table
* Copyright (c) 2014-2015, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*/
#include <linux/highmem.h>
#include <linux/debugfs.h>
#include <linux/blkdev.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/hdreg.h>
#include <linux/genhd.h>
#include <linux/sizes.h>
#include <linux/ndctl.h>
#include <linux/fs.h>
#include <linux/nd.h>
#include <linux/backing-dev.h>
#include "btt.h"
#include "nd.h"
enum log_ent_request {
LOG_NEW_ENT = 0,
LOG_OLD_ENT
};
static struct device *to_dev(struct arena_info *arena)
{
return &arena->nd_btt->dev;
}
static u64 adjust_initial_offset(struct nd_btt *nd_btt, u64 offset)
{
return offset + nd_btt->initial_offset;
}
static int arena_read_bytes(struct arena_info *arena, resource_size_t offset,
void *buf, size_t n, unsigned long flags)
{
struct nd_btt *nd_btt = arena->nd_btt;
struct nd_namespace_common *ndns = nd_btt->ndns;
/* arena offsets may be shifted from the base of the device */
offset = adjust_initial_offset(nd_btt, offset);
return nvdimm_read_bytes(ndns, offset, buf, n, flags);
}
static int arena_write_bytes(struct arena_info *arena, resource_size_t offset,
void *buf, size_t n, unsigned long flags)
{
struct nd_btt *nd_btt = arena->nd_btt;
struct nd_namespace_common *ndns = nd_btt->ndns;
/* arena offsets may be shifted from the base of the device */
offset = adjust_initial_offset(nd_btt, offset);
return nvdimm_write_bytes(ndns, offset, buf, n, flags);
}
static int btt_info_write(struct arena_info *arena, struct btt_sb *super)
{
int ret;
/*
* infooff and info2off should always be at least 512B aligned.
* We rely on that to make sure rw_bytes does error clearing
* correctly, so make sure that is the case.
*/
dev_WARN_ONCE(to_dev(arena), !IS_ALIGNED(arena->infooff, 512),
"arena->infooff: %#llx is unaligned\n", arena->infooff);
dev_WARN_ONCE(to_dev(arena), !IS_ALIGNED(arena->info2off, 512),
"arena->info2off: %#llx is unaligned\n", arena->info2off);
ret = arena_write_bytes(arena, arena->info2off, super,
sizeof(struct btt_sb), 0);
if (ret)
return ret;
return arena_write_bytes(arena, arena->infooff, super,
sizeof(struct btt_sb), 0);
}
static int btt_info_read(struct arena_info *arena, struct btt_sb *super)
{
return arena_read_bytes(arena, arena->infooff, super,
sizeof(struct btt_sb), 0);
}
/*
* 'raw' version of btt_map write
* Assumptions:
* mapping is in little-endian
* mapping contains 'E' and 'Z' flags as desired
*/
static int __btt_map_write(struct arena_info *arena, u32 lba, __le32 mapping,
unsigned long flags)
{
u64 ns_off = arena->mapoff + (lba * MAP_ENT_SIZE);
if (unlikely(lba >= arena->external_nlba))
dev_err_ratelimited(to_dev(arena),
"%s: lba %#x out of range (max: %#x)\n",
__func__, lba, arena->external_nlba);
return arena_write_bytes(arena, ns_off, &mapping, MAP_ENT_SIZE, flags);
}
static int btt_map_write(struct arena_info *arena, u32 lba, u32 mapping,
u32 z_flag, u32 e_flag, unsigned long rwb_flags)
{
u32 ze;
__le32 mapping_le;
/*
* This 'mapping' is supposed to be just the LBA mapping, without
* any flags set, so strip the flag bits.
*/
mapping = ent_lba(mapping);
ze = (z_flag << 1) + e_flag;
switch (ze) {
case 0:
/*
* We want to set neither of the Z or E flags, and
* in the actual layout, this means setting the bit
* positions of both to '1' to indicate a 'normal'
* map entry
*/
mapping |= MAP_ENT_NORMAL;
break;
case 1:
mapping |= (1 << MAP_ERR_SHIFT);
break;
case 2:
mapping |= (1 << MAP_TRIM_SHIFT);
break;
default:
/*
* The case where Z and E are both sent in as '1' could be
* construed as a valid 'normal' case, but we decide not to,
* to avoid confusion
*/
dev_err_ratelimited(to_dev(arena),
"Invalid use of Z and E flags\n");
return -EIO;
}
mapping_le = cpu_to_le32(mapping);
return __btt_map_write(arena, lba, mapping_le, rwb_flags);
}
static int btt_map_read(struct arena_info *arena, u
|