forked from Minki/linux
libnvdimm for 4.14
* Media error handling support in the Block Translation Table (BTT) driver is reworked to address sleeping-while-atomic locking and memory-allocation-context conflicts. * The dax_device lookup overhead for xfs and ext4 is moved out of the iomap hot-path to a mount-time lookup. * A new 'ecc_unit_size' sysfs attribute is added to advertise the read-modify-write boundary property of a persistent memory range. * Preparatory fix-ups for arm and powerpc pmem support are included along with other miscellaneous fixes. -----BEGIN PGP SIGNATURE----- iQIcBAABAgAGBQJZtsAGAAoJEB7SkWpmfYgCrzMP/2vPvZvrFjZn5pAoZjlmTmHM ySceoOC7vwvVXIsSs52FhSjcxEoXo9cklXPwhXOPVtVUFdSDJBUOIUxwIziE6Y+5 sFJ2xT9K+5zKBUiXJwqFQDg52dn//eBNnnnDz+HQrBSzGrbWQhIZY2m19omPzv1I BeN0OCGOdW3cjSo3BCFl1d+KrSl704e7paeKq/TO3GIiAilIXleTVxcefEEodV2K ZvWHpFIhHeyN8dsF8teI952KcCT92CT/IaabxQIwCxX0/8/GFeDc5aqf77qiYWKi uxCeQXdgnaE8EZNWZWGWIWul6eYEkoCNbLeUQ7eJnECq61VxVajJS0NyGa5T9OiM P046Bo2b1b3R0IHxVIyVG0ZCm3YUMAHSn/3uRxPgESJ4bS/VQ3YP5M6MLxDOlc90 IisLilagitkK6h8/fVuVrwciRNQ71XEC34t6k7GCl/1ZnLlLT+i4/jc5NRZnGEZh aXAAGHdteQ+/mSz6p2UISFUekbd6LerwzKRw8ibDvH6pTud8orYR7g2+JoGhgb6Y pyFVE8DhIcqNKAMxBsjiRZ46OQ7qrT+AemdAG3aVv6FaNoe4o5jPLdw2cEtLqtpk +DNm0/lSWxxxozjrvu6EUZj6hk8R5E19XpRzV5QJkcKUXMu7oSrFLdMcC4FeIjl9 K4hXLV3fVBVRMiS0RA6z =5iGY -----END PGP SIGNATURE----- Merge tag 'libnvdimm-for-4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm Pull libnvdimm from Dan Williams: "A rework of media error handling in the BTT driver and other updates. It has appeared in a few -next releases and collected some late- breaking build-error and warning fixups as a result. Summary: - Media error handling support in the Block Translation Table (BTT) driver is reworked to address sleeping-while-atomic locking and memory-allocation-context conflicts. - The dax_device lookup overhead for xfs and ext4 is moved out of the iomap hot-path to a mount-time lookup. - A new 'ecc_unit_size' sysfs attribute is added to advertise the read-modify-write boundary property of a persistent memory range. - Preparatory fix-ups for arm and powerpc pmem support are included along with other miscellaneous fixes" * tag 'libnvdimm-for-4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm: (26 commits) libnvdimm, btt: fix format string warnings libnvdimm, btt: clean up warning and error messages ext4: fix null pointer dereference on sbi libnvdimm, nfit: move the check on nd_reserved2 to the endpoint dax: fix FS_DAX=n BLOCK=y compilation libnvdimm: fix integer overflow static analysis warning libnvdimm, nd_blk: remove mmio_flush_range() libnvdimm, btt: rework error clearing libnvdimm: fix potential deadlock while clearing errors libnvdimm, btt: cache sector_size in arena_info libnvdimm, btt: ensure that flags were also unchanged during a map_read libnvdimm, btt: refactor map entry operations with macros libnvdimm, btt: fix a missed NVDIMM_IO_ATOMIC case in the write path libnvdimm, nfit: export an 'ecc_unit_size' sysfs attribute ext4: perform dax_device lookup at mount ext2: perform dax_device lookup at mount xfs: perform dax_device lookup at mount dax: introduce a fs_dax_get_by_bdev() helper libnvdimm, btt: check memory allocation failure libnvdimm, label: fix index block size calculation ...
This commit is contained in:
commit
89fd915c40
@ -53,7 +53,6 @@ config X86
|
||||
select ARCH_HAS_FORTIFY_SOURCE
|
||||
select ARCH_HAS_GCOV_PROFILE_ALL
|
||||
select ARCH_HAS_KCOV if X86_64
|
||||
select ARCH_HAS_MMIO_FLUSH
|
||||
select ARCH_HAS_PMEM_API if X86_64
|
||||
# Causing hangs/crashes, see the commit that added this change for details.
|
||||
select ARCH_HAS_REFCOUNT if BROKEN
|
||||
|
@ -7,6 +7,4 @@
|
||||
|
||||
void clflush_cache_range(void *addr, unsigned int size);
|
||||
|
||||
#define mmio_flush_range(addr, size) clflush_cache_range(addr, size)
|
||||
|
||||
#endif /* _ASM_X86_CACHEFLUSH_H */
|
||||
|
@ -2,7 +2,7 @@ config ACPI_NFIT
|
||||
tristate "ACPI NVDIMM Firmware Interface Table (NFIT)"
|
||||
depends on PHYS_ADDR_T_64BIT
|
||||
depends on BLK_DEV
|
||||
depends on ARCH_HAS_MMIO_FLUSH
|
||||
depends on ARCH_HAS_PMEM_API
|
||||
select LIBNVDIMM
|
||||
help
|
||||
Infrastructure to probe ACPI 6 compliant platforms for
|
||||
|
@ -228,6 +228,10 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
|
||||
if (cmd == ND_CMD_CALL) {
|
||||
call_pkg = buf;
|
||||
func = call_pkg->nd_command;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(call_pkg->nd_reserved2); i++)
|
||||
if (call_pkg->nd_reserved2[i])
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (nvdimm) {
|
||||
@ -1674,8 +1678,19 @@ static ssize_t range_index_show(struct device *dev,
|
||||
}
|
||||
static DEVICE_ATTR_RO(range_index);
|
||||
|
||||
static ssize_t ecc_unit_size_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct nd_region *nd_region = to_nd_region(dev);
|
||||
struct nfit_spa *nfit_spa = nd_region_provider_data(nd_region);
|
||||
|
||||
return sprintf(buf, "%d\n", nfit_spa->clear_err_unit);
|
||||
}
|
||||
static DEVICE_ATTR_RO(ecc_unit_size);
|
||||
|
||||
static struct attribute *acpi_nfit_region_attributes[] = {
|
||||
&dev_attr_range_index.attr,
|
||||
&dev_attr_ecc_unit_size.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
@ -1804,6 +1819,7 @@ static int acpi_nfit_init_interleave_set(struct acpi_nfit_desc *acpi_desc,
|
||||
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
|
||||
struct acpi_nfit_memory_map *memdev = memdev_from_spa(acpi_desc,
|
||||
spa->range_index, i);
|
||||
struct acpi_nfit_control_region *dcr = nfit_mem->dcr;
|
||||
|
||||
if (!memdev || !nfit_mem->dcr) {
|
||||
dev_err(dev, "%s: failed to find DCR\n", __func__);
|
||||
@ -1811,13 +1827,13 @@ static int acpi_nfit_init_interleave_set(struct acpi_nfit_desc *acpi_desc,
|
||||
}
|
||||
|
||||
map->region_offset = memdev->region_offset;
|
||||
map->serial_number = nfit_mem->dcr->serial_number;
|
||||
map->serial_number = dcr->serial_number;
|
||||
|
||||
map2->region_offset = memdev->region_offset;
|
||||
map2->serial_number = nfit_mem->dcr->serial_number;
|
||||
map2->vendor_id = nfit_mem->dcr->vendor_id;
|
||||
map2->manufacturing_date = nfit_mem->dcr->manufacturing_date;
|
||||
map2->manufacturing_location = nfit_mem->dcr->manufacturing_location;
|
||||
map2->serial_number = dcr->serial_number;
|
||||
map2->vendor_id = dcr->vendor_id;
|
||||
map2->manufacturing_date = dcr->manufacturing_date;
|
||||
map2->manufacturing_location = dcr->manufacturing_location;
|
||||
}
|
||||
|
||||
/* v1.1 namespaces */
|
||||
@ -1835,6 +1851,28 @@ static int acpi_nfit_init_interleave_set(struct acpi_nfit_desc *acpi_desc,
|
||||
cmp_map_compat, NULL);
|
||||
nd_set->altcookie = nd_fletcher64(info, sizeof_nfit_set_info(nr), 0);
|
||||
|
||||
/* record the result of the sort for the mapping position */
|
||||
for (i = 0; i < nr; i++) {
|
||||
struct nfit_set_info_map2 *map2 = &info2->mapping[i];
|
||||
int j;
|
||||
|
||||
for (j = 0; j < nr; j++) {
|
||||
struct nd_mapping_desc *mapping = &ndr_desc->mapping[j];
|
||||
struct nvdimm *nvdimm = mapping->nvdimm;
|
||||
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
|
||||
struct acpi_nfit_control_region *dcr = nfit_mem->dcr;
|
||||
|
||||
if (map2->serial_number == dcr->serial_number &&
|
||||
map2->vendor_id == dcr->vendor_id &&
|
||||
map2->manufacturing_date == dcr->manufacturing_date &&
|
||||
map2->manufacturing_location
|
||||
== dcr->manufacturing_location) {
|
||||
mapping->position = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ndr_desc->nd_set = nd_set;
|
||||
devm_kfree(dev, info);
|
||||
devm_kfree(dev, info2);
|
||||
@ -1930,7 +1968,7 @@ static int acpi_nfit_blk_single_io(struct nfit_blk *nfit_blk,
|
||||
memcpy_flushcache(mmio->addr.aperture + offset, iobuf + copied, c);
|
||||
else {
|
||||
if (nfit_blk->dimm_flags & NFIT_BLK_READ_FLUSH)
|
||||
mmio_flush_range((void __force *)
|
||||
arch_invalidate_pmem((void __force *)
|
||||
mmio->addr.aperture + offset, c);
|
||||
|
||||
memcpy(iobuf + copied, mmio->addr.aperture + offset, c);
|
||||
|
@ -46,6 +46,8 @@ void dax_read_unlock(int id)
|
||||
EXPORT_SYMBOL_GPL(dax_read_unlock);
|
||||
|
||||
#ifdef CONFIG_BLOCK
|
||||
#include <linux/blkdev.h>
|
||||
|
||||
int bdev_dax_pgoff(struct block_device *bdev, sector_t sector, size_t size,
|
||||
pgoff_t *pgoff)
|
||||
{
|
||||
@ -59,6 +61,16 @@ int bdev_dax_pgoff(struct block_device *bdev, sector_t sector, size_t size,
|
||||
}
|
||||
EXPORT_SYMBOL(bdev_dax_pgoff);
|
||||
|
||||
#if IS_ENABLED(CONFIG_FS_DAX)
|
||||
struct dax_device *fs_dax_get_by_bdev(struct block_device *bdev)
|
||||
{
|
||||
if (!blk_queue_dax(bdev->bd_queue))
|
||||
return NULL;
|
||||
return fs_dax_get_by_host(bdev->bd_disk->disk_name);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fs_dax_get_by_bdev);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* __bdev_dax_supported() - Check if the device supports dax for filesystem
|
||||
* @sb: The superblock of the device
|
||||
|
@ -31,6 +31,16 @@ enum log_ent_request {
|
||||
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)
|
||||
{
|
||||
@ -38,7 +48,7 @@ static int arena_read_bytes(struct arena_info *arena, resource_size_t offset,
|
||||
struct nd_namespace_common *ndns = nd_btt->ndns;
|
||||
|
||||
/* arena offsets may be shifted from the base of the device */
|
||||
offset += arena->nd_btt->initial_offset;
|
||||
offset = adjust_initial_offset(nd_btt, offset);
|
||||
return nvdimm_read_bytes(ndns, offset, buf, n, flags);
|
||||
}
|
||||
|
||||
@ -49,7 +59,7 @@ static int arena_write_bytes(struct arena_info *arena, resource_size_t offset,
|
||||
struct nd_namespace_common *ndns = nd_btt->ndns;
|
||||
|
||||
/* arena offsets may be shifted from the base of the device */
|
||||
offset += arena->nd_btt->initial_offset;
|
||||
offset = adjust_initial_offset(nd_btt, offset);
|
||||
return nvdimm_write_bytes(ndns, offset, buf, n, flags);
|
||||
}
|
||||
|
||||
@ -62,8 +72,10 @@ static int btt_info_write(struct arena_info *arena, struct btt_sb *super)
|
||||
* We rely on that to make sure rw_bytes does error clearing
|
||||
* correctly, so make sure that is the case.
|
||||
*/
|
||||
WARN_ON_ONCE(!IS_ALIGNED(arena->infooff, 512));
|
||||
WARN_ON_ONCE(!IS_ALIGNED(arena->info2off, 512));
|
||||
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);
|
||||
@ -76,7 +88,6 @@ static int btt_info_write(struct arena_info *arena, struct btt_sb *super)
|
||||
|
||||
static int btt_info_read(struct arena_info *arena, struct btt_sb *super)
|
||||
{
|
||||
WARN_ON(!super);
|
||||
return arena_read_bytes(arena, arena->infooff, super,
|
||||
sizeof(struct btt_sb), 0);
|
||||
}
|
||||
@ -92,7 +103,10 @@ static int __btt_map_write(struct arena_info *arena, u32 lba, __le32 mapping,
|
||||
{
|
||||
u64 ns_off = arena->mapoff + (lba * MAP_ENT_SIZE);
|
||||
|
||||
WARN_ON(lba >= arena->external_nlba);
|
||||
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);
|
||||
}
|
||||
|
||||
@ -106,7 +120,7 @@ static int btt_map_write(struct arena_info *arena, u32 lba, u32 mapping,
|
||||
* This 'mapping' is supposed to be just the LBA mapping, without
|
||||
* any flags set, so strip the flag bits.
|
||||
*/
|
||||
mapping &= MAP_LBA_MASK;
|
||||
mapping = ent_lba(mapping);
|
||||
|
||||
ze = (z_flag << 1) + e_flag;
|
||||
switch (ze) {
|
||||
@ -131,7 +145,8 @@ static int btt_map_write(struct arena_info *arena, u32 lba, u32 mapping,
|
||||
* construed as a valid 'normal' case, but we decide not to,
|
||||
* to avoid confusion
|
||||
*/
|
||||
WARN_ONCE(1, "Invalid use of Z and E flags\n");
|
||||
dev_err_ratelimited(to_dev(arena),
|
||||
"Invalid use of Z and E flags\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@ -147,7 +162,10 @@ static int btt_map_read(struct arena_info *arena, u32 lba, u32 *mapping,
|
||||
u32 raw_mapping, postmap, ze, z_flag, e_flag;
|
||||
u64 ns_off = arena->mapoff + (lba * MAP_ENT_SIZE);
|
||||
|
||||
WARN_ON(lba >= arena->external_nlba);
|
||||
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);
|
||||
|
||||
ret = arena_read_bytes(arena, ns_off, &in, MAP_ENT_SIZE, rwb_flags);
|
||||
if (ret)
|
||||
@ -155,10 +173,10 @@ static int btt_map_read(struct arena_info *arena, u32 lba, u32 *mapping,
|
||||
|
||||
raw_mapping = le32_to_cpu(in);
|
||||
|
||||
z_flag = (raw_mapping & MAP_TRIM_MASK) >> MAP_TRIM_SHIFT;
|
||||
e_flag = (raw_mapping & MAP_ERR_MASK) >> MAP_ERR_SHIFT;
|
||||
z_flag = ent_z_flag(raw_mapping);
|
||||
e_flag = ent_e_flag(raw_mapping);
|
||||
ze = (z_flag << 1) + e_flag;
|
||||
postmap = raw_mapping & MAP_LBA_MASK;
|
||||
postmap = ent_lba(raw_mapping);
|
||||
|
||||
/* Reuse the {z,e}_flag variables for *trim and *error */
|
||||
z_flag = 0;
|
||||
@ -195,7 +213,6 @@ static int btt_map_read(struct arena_info *arena, u32 lba, u32 *mapping,
|
||||
static int btt_log_read_pair(struct arena_info *arena, u32 lane,
|
||||
struct log_entry *ent)
|
||||
{
|
||||
WARN_ON(!ent);
|
||||
return arena_read_bytes(arena,
|
||||
arena->logoff + (2 * lane * LOG_ENT_SIZE), ent,
|
||||
2 * LOG_ENT_SIZE, 0);
|
||||
@ -299,11 +316,6 @@ static int btt_log_get_old(struct log_entry *ent)
|
||||
return old;
|
||||
}
|
||||
|
||||
static struct device *to_dev(struct arena_info *arena)
|
||||
{
|
||||
return &arena->nd_btt->dev;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function copies the desired (old/new) log entry into ent if
|
||||
* it is not NULL. It returns the sub-slot number (0 or 1)
|
||||
@ -381,7 +393,9 @@ static int btt_flog_write(struct arena_info *arena, u32 lane, u32 sub,
|
||||
arena->freelist[lane].sub = 1 - arena->freelist[lane].sub;
|
||||
if (++(arena->freelist[lane].seq) == 4)
|
||||
arena->freelist[lane].seq = 1;
|
||||
arena->freelist[lane].block = le32_to_cpu(ent->old_map);
|
||||
if (ent_e_flag(ent->old_map))
|
||||
arena->freelist[lane].has_err = 1;
|
||||
arena->freelist[lane].block = le32_to_cpu(ent_lba(ent->old_map));
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -407,12 +421,14 @@ static int btt_map_init(struct arena_info *arena)
|
||||
* make sure rw_bytes does error clearing correctly, so make sure that
|
||||
* is the case.
|
||||
*/
|
||||
WARN_ON_ONCE(!IS_ALIGNED(arena->mapoff, 512));
|
||||
dev_WARN_ONCE(to_dev(arena), !IS_ALIGNED(arena->mapoff, 512),
|
||||
"arena->mapoff: %#llx is unaligned\n", arena->mapoff);
|
||||
|
||||
while (mapsize) {
|
||||
size_t size = min(mapsize, chunk_size);
|
||||
|
||||
WARN_ON_ONCE(size < 512);
|
||||
dev_WARN_ONCE(to_dev(arena), size < 512,
|
||||
"chunk size: %#zx is unaligned\n", size);
|
||||
ret = arena_write_bytes(arena, arena->mapoff + offset, zerobuf,
|
||||
size, 0);
|
||||
if (ret)
|
||||
@ -449,12 +465,14 @@ static int btt_log_init(struct arena_info *arena)
|
||||
* make sure rw_bytes does error clearing correctly, so make sure that
|
||||
* is the case.
|
||||
*/
|
||||
WARN_ON_ONCE(!IS_ALIGNED(arena->logoff, 512));
|
||||
dev_WARN_ONCE(to_dev(arena), !IS_ALIGNED(arena->logoff, 512),
|
||||
"arena->logoff: %#llx is unaligned\n", arena->logoff);
|
||||
|
||||
while (logsize) {
|
||||
size_t size = min(logsize, chunk_size);
|
||||
|
||||
WARN_ON_ONCE(size < 512);
|
||||
dev_WARN_ONCE(to_dev(arena), size < 512,
|
||||
"chunk size: %#zx is unaligned\n", size);
|
||||
ret = arena_write_bytes(arena, arena->logoff + offset, zerobuf,
|
||||
size, 0);
|
||||
if (ret)
|
||||
@ -480,6 +498,40 @@ static int btt_log_init(struct arena_info *arena)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u64 to_namespace_offset(struct arena_info *arena, u64 lba)
|
||||
{
|
||||
return arena->dataoff + ((u64)lba * arena->internal_lbasize);
|
||||
}
|
||||
|
||||
static int arena_clear_freelist_error(struct arena_info *arena, u32 lane)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (arena->freelist[lane].has_err) {
|
||||
void *zero_page = page_address(ZERO_PAGE(0));
|
||||
u32 lba = arena->freelist[lane].block;
|
||||
u64 nsoff = to_namespace_offset(arena, lba);
|
||||
unsigned long len = arena->sector_size;
|
||||
|
||||
mutex_lock(&arena->err_lock);
|
||||
|
||||
while (len) {
|
||||
unsigned long chunk = min(len, PAGE_SIZE);
|
||||
|
||||
ret = arena_write_bytes(arena, nsoff, zero_page,
|
||||
chunk, 0);
|
||||
if (ret)
|
||||
break;
|
||||
len -= chunk;
|
||||
nsoff += chunk;
|
||||
if (len == 0)
|
||||
arena->freelist[lane].has_err = 0;
|
||||
}
|
||||
mutex_unlock(&arena->err_lock);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int btt_freelist_init(struct arena_info *arena)
|
||||
{
|
||||
int old, new, ret;
|
||||
@ -505,6 +557,17 @@ static int btt_freelist_init(struct arena_info *arena)
|
||||
arena->freelist[i].seq = nd_inc_seq(le32_to_cpu(log_new.seq));
|
||||
arena->freelist[i].block = le32_to_cpu(log_new.old_map);
|
||||
|
||||
/*
|
||||
* FIXME: if error clearing fails during init, we want to make
|
||||
* the BTT read-only
|
||||
*/
|
||||
if (ent_e_flag(log_new.old_map)) {
|
||||
ret = arena_clear_freelist_error(arena, i);
|
||||
if (ret)
|
||||
dev_err_ratelimited(to_dev(arena),
|
||||
"Unable to clear known errors\n");
|
||||
}
|
||||
|
||||
/* This implies a newly created or untouched flog entry */
|
||||
if (log_new.old_map == log_new.new_map)
|
||||
continue;
|
||||
@ -525,7 +588,6 @@ static int btt_freelist_init(struct arena_info *arena)
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -566,6 +628,7 @@ static struct arena_info *alloc_arena(struct btt *btt, size_t size,
|
||||
if (!arena)
|
||||
return NULL;
|
||||
arena->nd_btt = btt->nd_btt;
|
||||
arena->sector_size = btt->sector_size;
|
||||
|
||||
if (!size)
|
||||
return arena;
|
||||
@ -694,6 +757,7 @@ static int discover_arenas(struct btt *btt)
|
||||
arena->external_lba_start = cur_nlba;
|
||||
parse_arena_meta(arena, super, cur_off);
|
||||
|
||||
mutex_init(&arena->err_lock);
|
||||
ret = btt_freelist_init(arena);
|
||||
if (ret)
|
||||
goto out;
|
||||
@ -904,11 +968,6 @@ static void unlock_map(struct arena_info *arena, u32 premap)
|
||||
spin_unlock(&arena->map_locks[idx].lock);
|
||||
}
|
||||
|
||||
static u64 to_namespace_offset(struct arena_info *arena, u64 lba)
|
||||
{
|
||||
return arena->dataoff + ((u64)lba * arena->internal_lbasize);
|
||||
}
|
||||
|
||||
static int btt_data_read(struct arena_info *arena, struct page *page,
|
||||
unsigned int off, u32 lba, u32 len)
|
||||
{
|
||||
@ -1032,6 +1091,7 @@ static int btt_read_pg(struct btt *btt, struct bio_integrity_payload *bip,
|
||||
*/
|
||||
while (1) {
|
||||
u32 new_map;
|
||||
int new_t, new_e;
|
||||
|
||||
if (t_flag) {
|
||||
zero_fill_data(page, off, cur_len);
|
||||
@ -1050,20 +1110,29 @@ static int btt_read_pg(struct btt *btt, struct bio_integrity_payload *bip,
|
||||
*/
|
||||
barrier();
|
||||
|
||||
ret = btt_map_read(arena, premap, &new_map, &t_flag,
|
||||
&e_flag, NVDIMM_IO_ATOMIC);
|
||||
ret = btt_map_read(arena, premap, &new_map, &new_t,
|
||||
&new_e, NVDIMM_IO_ATOMIC);
|
||||
if (ret)
|
||||
goto out_rtt;
|
||||
|
||||
if (postmap == new_map)
|
||||
if ((postmap == new_map) && (t_flag == new_t) &&
|
||||
(e_flag == new_e))
|
||||
break;
|
||||
|
||||
postmap = new_map;
|
||||
t_flag = new_t;
|
||||
e_flag = new_e;
|
||||
}
|
||||
|
||||
ret = btt_data_read(arena, page, off, postmap, cur_len);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
int rc;
|
||||
|
||||
/* Media error - set the e_flag */
|
||||
rc = btt_map_write(arena, premap, postmap, 0, 1,
|
||||
NVDIMM_IO_ATOMIC);
|
||||
goto out_rtt;
|
||||
}
|
||||
|
||||
if (bip) {
|
||||
ret = btt_rw_integrity(btt, bip, arena, postmap, READ);
|
||||
@ -1088,6 +1157,21 @@ static int btt_read_pg(struct btt *btt, struct bio_integrity_payload *bip,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Normally, arena_{read,write}_bytes will take care of the initial offset
|
||||
* adjustment, but in the case of btt_is_badblock, where we query is_bad_pmem,
|
||||
* we need the final, raw namespace offset here
|
||||
*/
|
||||
static bool btt_is_badblock(struct btt *btt, struct arena_info *arena,
|
||||
u32 postmap)
|
||||
{
|
||||
u64 nsoff = adjust_initial_offset(arena->nd_btt,
|
||||
to_namespace_offset(arena, postmap));
|
||||
sector_t phys_sector = nsoff >> 9;
|
||||
|
||||
return is_bad_pmem(btt->phys_bb, phys_sector, arena->internal_lbasize);
|
||||
}
|
||||
|
||||
static int btt_write_pg(struct btt *btt, struct bio_integrity_payload *bip,
|
||||
sector_t sector, struct page *page, unsigned int off,
|
||||
unsigned int len)
|
||||
@ -1100,7 +1184,9 @@ static int btt_write_pg(struct btt *btt, struct bio_integrity_payload *bip,
|
||||
|
||||
while (len) {
|
||||
u32 cur_len;
|
||||
int e_flag;
|
||||
|
||||
retry:
|
||||
lane = nd_region_acquire_lane(btt->nd_region);
|
||||
|
||||
ret = lba_to_arena(btt, sector, &premap, &arena);
|
||||
@ -1113,6 +1199,21 @@ static int btt_write_pg(struct btt *btt, struct bio_integrity_payload *bip,
|
||||
goto out_lane;
|
||||
}
|
||||
|
||||
if (btt_is_badblock(btt, arena, arena->freelist[lane].block))
|
||||
arena->freelist[lane].has_err = 1;
|
||||
|
||||
if (mutex_is_locked(&arena->err_lock)
|
||||
|| arena->freelist[lane].has_err) {
|
||||
nd_region_release_lane(btt->nd_region, lane);
|
||||
|
||||
ret = arena_clear_freelist_error(arena, lane);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* OK to acquire a different lane/free block */
|
||||
goto retry;
|
||||
}
|
||||
|
||||
new_postmap = arena->freelist[lane].block;
|
||||
|
||||
/* Wait if the new block is being read from */
|
||||
@ -1138,7 +1239,7 @@ static int btt_write_pg(struct btt *btt, struct bio_integrity_payload *bip,
|
||||
}
|
||||
|
||||
lock_map(arena, premap);
|
||||
ret = btt_map_read(arena, premap, &old_postmap, NULL, NULL,
|
||||
ret = btt_map_read(arena, premap, &old_postmap, NULL, &e_flag,
|
||||
NVDIMM_IO_ATOMIC);
|
||||
if (ret)
|
||||
goto out_map;
|
||||
@ -1146,6 +1247,8 @@ static int btt_write_pg(struct btt *btt, struct bio_integrity_payload *bip,
|
||||
ret = -EIO;
|
||||
goto out_map;
|
||||
}
|
||||
if (e_flag)
|
||||
set_e_flag(old_postmap);
|
||||
|
||||
log.lba = cpu_to_le32(premap);
|
||||
log.old_map = cpu_to_le32(old_postmap);
|
||||
@ -1156,13 +1259,20 @@ static int btt_write_pg(struct btt *btt, struct bio_integrity_payload *bip,
|
||||
if (ret)
|
||||
goto out_map;
|
||||
|
||||
ret = btt_map_write(arena, premap, new_postmap, 0, 0, 0);
|
||||
ret = btt_map_write(arena, premap, new_postmap, 0, 0,
|
||||
NVDIMM_IO_ATOMIC);
|
||||
if (ret)
|
||||
goto out_map;
|
||||
|
||||
unlock_map(arena, premap);
|
||||
nd_region_release_lane(btt->nd_region, lane);
|
||||
|
||||
if (e_flag) {
|
||||
ret = arena_clear_freelist_error(arena, lane);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
len -= cur_len;
|
||||
off += cur_len;
|
||||
sector += btt->sector_size >> SECTOR_SHIFT;
|
||||
@ -1211,11 +1321,13 @@ static blk_qc_t btt_make_request(struct request_queue *q, struct bio *bio)
|
||||
bio_for_each_segment(bvec, bio, iter) {
|
||||
unsigned int len = bvec.bv_len;
|
||||
|
||||
BUG_ON(len > PAGE_SIZE);
|
||||
/* Make sure len is in multiples of sector size. */
|
||||
/* XXX is this right? */
|
||||
BUG_ON(len < btt->sector_size);
|
||||
BUG_ON(len % btt->sector_size);
|
||||
if (len > PAGE_SIZE || len < btt->sector_size ||
|
||||
len % btt->sector_size) {
|
||||
dev_err_ratelimited(&btt->nd_btt->dev,
|
||||
"unaligned bio segment (len: %d)\n", len);
|
||||
bio->bi_status = BLK_STS_IOERR;
|
||||
break;
|
||||
}
|
||||
|
||||
err = btt_do_bvec(btt, bip, bvec.bv_page, len, bvec.bv_offset,
|
||||
op_is_write(bio_op(bio)), iter.bi_sector);
|
||||
@ -1345,6 +1457,7 @@ static struct btt *btt_init(struct nd_btt *nd_btt, unsigned long long rawsize,
|
||||
{
|
||||
int ret;
|
||||
struct btt *btt;
|
||||
struct nd_namespace_io *nsio;
|
||||
struct device *dev = &nd_btt->dev;
|
||||
|
||||
btt = devm_kzalloc(dev, sizeof(struct btt), GFP_KERNEL);
|
||||
@ -1358,6 +1471,8 @@ static struct btt *btt_init(struct nd_btt *nd_btt, unsigned long long rawsize,
|
||||
INIT_LIST_HEAD(&btt->arena_list);
|
||||
mutex_init(&btt->init_lock);
|
||||
btt->nd_region = nd_region;
|
||||
nsio = to_nd_namespace_io(&nd_btt->ndns->dev);
|
||||
btt->phys_bb = &nsio->bb;
|
||||
|
||||
ret = discover_arenas(btt);
|
||||
if (ret) {
|
||||
@ -1431,6 +1546,8 @@ int nvdimm_namespace_attach_btt(struct nd_namespace_common *ndns)
|
||||
}
|
||||
|
||||
btt_sb = devm_kzalloc(&nd_btt->dev, sizeof(*btt_sb), GFP_KERNEL);
|
||||
if (!btt_sb)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* If this returns < 0, that is ok as it just means there wasn't
|
||||
|
@ -15,6 +15,7 @@
|
||||
#ifndef _LINUX_BTT_H
|
||||
#define _LINUX_BTT_H
|
||||
|
||||
#include <linux/badblocks.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define BTT_SIG_LEN 16
|
||||
@ -38,6 +39,11 @@
|
||||
#define IB_FLAG_ERROR 0x00000001
|
||||
#define IB_FLAG_ERROR_MASK 0x00000001
|
||||
|
||||
#define ent_lba(ent) (ent & MAP_LBA_MASK)
|
||||
#define ent_e_flag(ent) (!!(ent & MAP_ERR_MASK))
|
||||
#define ent_z_flag(ent) (!!(ent & MAP_TRIM_MASK))
|
||||
#define set_e_flag(ent) (ent |= MAP_ERR_MASK)
|
||||
|
||||
enum btt_init_state {
|
||||
INIT_UNCHECKED = 0,
|
||||
INIT_NOTFOUND,
|
||||
@ -78,6 +84,7 @@ struct free_entry {
|
||||
u32 block;
|
||||
u8 sub;
|
||||
u8 seq;
|
||||
u8 has_err;
|
||||
};
|
||||
|
||||
struct aligned_lock {
|
||||
@ -104,6 +111,7 @@ struct aligned_lock {
|
||||
* handle incoming writes.
|
||||
* @version_major: Metadata layout version major.
|
||||
* @version_minor: Metadata layout version minor.
|
||||
* @sector_size: The Linux sector size - 512 or 4096
|
||||
* @nextoff: Offset in bytes to the start of the next arena.
|
||||
* @infooff: Offset in bytes to the info block of this arena.
|
||||
* @dataoff: Offset in bytes to the data area of this arena.
|
||||
@ -131,6 +139,7 @@ struct arena_info {
|
||||
u32 nfree;
|
||||
u16 version_major;
|
||||
u16 version_minor;
|
||||
u32 sector_size;
|
||||
/* Byte offsets to the different on-media structures */
|
||||
u64 nextoff;
|
||||
u64 infooff;
|
||||
@ -147,6 +156,7 @@ struct arena_info {
|
||||
struct dentry *debugfs_dir;
|
||||
/* Arena flags */
|
||||
u32 flags;
|
||||
struct mutex err_lock;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -181,6 +191,7 @@ struct btt {
|
||||
struct mutex init_lock;
|
||||
int init_state;
|
||||
int num_arenas;
|
||||
struct badblocks *phys_bb;
|
||||
};
|
||||
|
||||
bool nd_btt_arena_is_valid(struct nd_btt *nd_btt, struct btt_sb *super);
|
||||
|
@ -61,7 +61,7 @@ static ssize_t sector_size_show(struct device *dev,
|
||||
{
|
||||
struct nd_btt *nd_btt = to_nd_btt(dev);
|
||||
|
||||
return nd_sector_size_show(nd_btt->lbasize, btt_lbasize_supported, buf);
|
||||
return nd_size_select_show(nd_btt->lbasize, btt_lbasize_supported, buf);
|
||||
}
|
||||
|
||||
static ssize_t sector_size_store(struct device *dev,
|
||||
@ -72,7 +72,7 @@ static ssize_t sector_size_store(struct device *dev,
|
||||
|
||||
device_lock(dev);
|
||||
nvdimm_bus_lock(dev);
|
||||
rc = nd_sector_size_store(dev, buf, &nd_btt->lbasize,
|
||||
rc = nd_size_select_store(dev, buf, &nd_btt->lbasize,
|
||||
btt_lbasize_supported);
|
||||
dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__,
|
||||
rc, buf, buf[len - 1] == '\n' ? "" : "\n");
|
||||
|
@ -11,6 +11,7 @@
|
||||
* General Public License for more details.
|
||||
*/
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
#include <linux/sched/mm.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/module.h>
|
||||
@ -234,6 +235,7 @@ long nvdimm_clear_poison(struct device *dev, phys_addr_t phys,
|
||||
struct nd_cmd_clear_error clear_err;
|
||||
struct nd_cmd_ars_cap ars_cap;
|
||||
u32 clear_err_unit, mask;
|
||||
unsigned int noio_flag;
|
||||
int cmd_rc, rc;
|
||||
|
||||
if (!nvdimm_bus)
|
||||
@ -250,8 +252,10 @@ long nvdimm_clear_poison(struct device *dev, phys_addr_t phys,
|
||||
memset(&ars_cap, 0, sizeof(ars_cap));
|
||||
ars_cap.address = phys;
|
||||
ars_cap.length = len;
|
||||
noio_flag = memalloc_noio_save();
|
||||
rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_CAP, &ars_cap,
|
||||
sizeof(ars_cap), &cmd_rc);
|
||||
memalloc_noio_restore(noio_flag);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
if (cmd_rc < 0)
|
||||
@ -266,8 +270,10 @@ long nvdimm_clear_poison(struct device *dev, phys_addr_t phys,
|
||||
memset(&clear_err, 0, sizeof(clear_err));
|
||||
clear_err.address = phys;
|
||||
clear_err.length = len;
|
||||
noio_flag = memalloc_noio_save();
|
||||
rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_CLEAR_ERROR, &clear_err,
|
||||
sizeof(clear_err), &cmd_rc);
|
||||
memalloc_noio_restore(noio_flag);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
if (cmd_rc < 0)
|
||||
@ -905,19 +911,20 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,
|
||||
int read_only, unsigned int ioctl_cmd, unsigned long arg)
|
||||
{
|
||||
struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
|
||||
size_t buf_len = 0, in_len = 0, out_len = 0;
|
||||
static char out_env[ND_CMD_MAX_ENVELOPE];
|
||||
static char in_env[ND_CMD_MAX_ENVELOPE];
|
||||
const struct nd_cmd_desc *desc = NULL;
|
||||
unsigned int cmd = _IOC_NR(ioctl_cmd);
|
||||
unsigned int func = cmd;
|
||||
void __user *p = (void __user *) arg;
|
||||
struct device *dev = &nvdimm_bus->dev;
|
||||
struct nd_cmd_pkg pkg;
|
||||
void __user *p = (void __user *) arg;
|
||||
const char *cmd_name, *dimm_name;
|
||||
u32 in_len = 0, out_len = 0;
|
||||
unsigned int func = cmd;
|
||||
unsigned long cmd_mask;
|
||||
void *buf;
|
||||
struct nd_cmd_pkg pkg;
|
||||
int rc, i, cmd_rc;
|
||||
u64 buf_len = 0;
|
||||
void *buf;
|
||||
|
||||
if (nvdimm) {
|
||||
desc = nd_cmd_dimm_desc(cmd);
|
||||
@ -977,13 +984,9 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,
|
||||
|
||||
if (cmd == ND_CMD_CALL) {
|
||||
func = pkg.nd_command;
|
||||
dev_dbg(dev, "%s:%s, idx: %llu, in: %zu, out: %zu, len %zu\n",
|
||||
dev_dbg(dev, "%s:%s, idx: %llu, in: %u, out: %u, len %llu\n",
|
||||
__func__, dimm_name, pkg.nd_command,
|
||||
in_len, out_len, buf_len);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pkg.nd_reserved2); i++)
|
||||
if (pkg.nd_reserved2[i])
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* process an output envelope */
|
||||
@ -1007,9 +1010,9 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,
|
||||
out_len += out_size;
|
||||
}
|
||||
|
||||
buf_len = out_len + in_len;
|
||||
buf_len = (u64) out_len + (u64) in_len;
|
||||
if (buf_len > ND_IOCTL_MAX_BUFLEN) {
|
||||
dev_dbg(dev, "%s:%s cmd: %s buf_len: %zu > %d\n", __func__,
|
||||
dev_dbg(dev, "%s:%s cmd: %s buf_len: %llu > %d\n", __func__,
|
||||
dimm_name, cmd_name, buf_len,
|
||||
ND_IOCTL_MAX_BUFLEN);
|
||||
return -EINVAL;
|
||||
|
@ -280,18 +280,11 @@ static int nsio_rw_bytes(struct nd_namespace_common *ndns,
|
||||
}
|
||||
|
||||
if (unlikely(is_bad_pmem(&nsio->bb, sector, sz_align))) {
|
||||
/*
|
||||
* FIXME: nsio_rw_bytes() may be called from atomic
|
||||
* context in the btt case and the ACPI DSM path for
|
||||
* clearing the error takes sleeping locks and allocates
|
||||
* memory. An explicit error clearing path, and support
|
||||
* for tracking badblocks in BTT metadata is needed to
|
||||
* work around this collision.
|
||||
*/
|
||||
if (IS_ALIGNED(offset, 512) && IS_ALIGNED(size, 512)
|
||||
&& !(flags & NVDIMM_IO_ATOMIC)) {
|
||||
long cleared;
|
||||
|
||||
might_sleep();
|
||||
cleared = nvdimm_clear_poison(&ndns->dev,
|
||||
nsio->res.start + offset, size);
|
||||
if (cleared < size)
|
||||
|
@ -277,14 +277,14 @@ int nd_uuid_store(struct device *dev, u8 **uuid_out, const char *buf,
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t nd_sector_size_show(unsigned long current_lbasize,
|
||||
ssize_t nd_size_select_show(unsigned long current_size,
|
||||
const unsigned long *supported, char *buf)
|
||||
{
|
||||
ssize_t len = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; supported[i]; i++)
|
||||
if (current_lbasize == supported[i])
|
||||
if (current_size == supported[i])
|
||||
len += sprintf(buf + len, "[%ld] ", supported[i]);
|
||||
else
|
||||
len += sprintf(buf + len, "%ld ", supported[i]);
|
||||
@ -292,8 +292,8 @@ ssize_t nd_sector_size_show(unsigned long current_lbasize,
|
||||
return len;
|
||||
}
|
||||
|
||||
ssize_t nd_sector_size_store(struct device *dev, const char *buf,
|
||||
unsigned long *current_lbasize, const unsigned long *supported)
|
||||
ssize_t nd_size_select_store(struct device *dev, const char *buf,
|
||||
unsigned long *current_size, const unsigned long *supported)
|
||||
{
|
||||
unsigned long lbasize;
|
||||
int rc, i;
|
||||
@ -310,7 +310,7 @@ ssize_t nd_sector_size_store(struct device *dev, const char *buf,
|
||||
break;
|
||||
|
||||
if (supported[i]) {
|
||||
*current_lbasize = lbasize;
|
||||
*current_size = lbasize;
|
||||
return 0;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
|
@ -45,12 +45,14 @@ unsigned sizeof_namespace_label(struct nvdimm_drvdata *ndd)
|
||||
return ndd->nslabel_size;
|
||||
}
|
||||
|
||||
int nvdimm_num_label_slots(struct nvdimm_drvdata *ndd)
|
||||
{
|
||||
return ndd->nsarea.config_size / (sizeof_namespace_label(ndd) + 1);
|
||||
}
|
||||
|
||||
size_t sizeof_namespace_index(struct nvdimm_drvdata *ndd)
|
||||
{
|
||||
u32 index_span;
|
||||
|
||||
if (ndd->nsindex_size)
|
||||
return ndd->nsindex_size;
|
||||
u32 nslot, space, size;
|
||||
|
||||
/*
|
||||
* The minimum index space is 512 bytes, with that amount of
|
||||
@ -60,16 +62,16 @@ size_t sizeof_namespace_index(struct nvdimm_drvdata *ndd)
|
||||
* starts to waste space at larger config_sizes, but it's
|
||||
* unlikely we'll ever see anything but 128K.
|
||||
*/
|
||||
index_span = ndd->nsarea.config_size / (sizeof_namespace_label(ndd) + 1);
|
||||
index_span /= NSINDEX_ALIGN * 2;
|
||||
ndd->nsindex_size = index_span * NSINDEX_ALIGN;
|
||||
nslot = nvdimm_num_label_slots(ndd);
|
||||
space = ndd->nsarea.config_size - nslot * sizeof_namespace_label(ndd);
|
||||
size = ALIGN(sizeof(struct nd_namespace_index) + DIV_ROUND_UP(nslot, 8),
|
||||
NSINDEX_ALIGN) * 2;
|
||||
if (size <= space)
|
||||
return size / 2;
|
||||
|
||||
return ndd->nsindex_size;
|
||||
}
|
||||
|
||||
int nvdimm_num_label_slots(struct nvdimm_drvdata *ndd)
|
||||
{
|
||||
return ndd->nsarea.config_size / (sizeof_namespace_label(ndd) + 1);
|
||||
dev_err(ndd->dev, "label area (%d) too small to host (%d byte) labels\n",
|
||||
ndd->nsarea.config_size, sizeof_namespace_label(ndd));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __nd_label_validate(struct nvdimm_drvdata *ndd)
|
||||
|
@ -1313,14 +1313,14 @@ static ssize_t sector_size_show(struct device *dev,
|
||||
if (is_namespace_blk(dev)) {
|
||||
struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev);
|
||||
|
||||
return nd_sector_size_show(nsblk->lbasize,
|
||||
return nd_size_select_show(nsblk->lbasize,
|
||||
blk_lbasize_supported, buf);
|
||||
}
|
||||
|
||||
if (is_namespace_pmem(dev)) {
|
||||
struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
|
||||
|
||||
return nd_sector_size_show(nspm->lbasize,
|
||||
return nd_size_select_show(nspm->lbasize,
|
||||
pmem_lbasize_supported, buf);
|
||||
}
|
||||
return -ENXIO;
|
||||
@ -1352,7 +1352,7 @@ static ssize_t sector_size_store(struct device *dev,
|
||||
if (to_ndns(dev)->claim)
|
||||
rc = -EBUSY;
|
||||
if (rc >= 0)
|
||||
rc = nd_sector_size_store(dev, buf, lbasize, supported);
|
||||
rc = nd_size_select_store(dev, buf, lbasize, supported);
|
||||
if (rc >= 0)
|
||||
rc = nd_namespace_label_update(nd_region, dev);
|
||||
dev_dbg(dev, "%s: result: %zd %s: %s%s", __func__,
|
||||
|
@ -42,7 +42,7 @@ struct nd_poison {
|
||||
|
||||
struct nvdimm_drvdata {
|
||||
struct device *dev;
|
||||
int nsindex_size, nslabel_size;
|
||||
int nslabel_size;
|
||||
struct nd_cmd_get_config_size nsarea;
|
||||
void *data;
|
||||
int ns_current, ns_next;
|
||||
@ -134,6 +134,7 @@ struct nd_mapping {
|
||||
struct nvdimm *nvdimm;
|
||||
u64 start;
|
||||
u64 size;
|
||||
int position;
|
||||
struct list_head labels;
|
||||
struct mutex lock;
|
||||
/*
|
||||
@ -233,10 +234,10 @@ void nd_device_unregister(struct device *dev, enum nd_async_mode mode);
|
||||
void nd_device_notify(struct device *dev, enum nvdimm_event event);
|
||||
int nd_uuid_store(struct device *dev, u8 **uuid_out, const char *buf,
|
||||
size_t len);
|
||||
ssize_t nd_sector_size_show(unsigned long current_lbasize,
|
||||
ssize_t nd_size_select_show(unsigned long current_size,
|
||||
const unsigned long *supported, char *buf);
|
||||
ssize_t nd_sector_size_store(struct device *dev, const char *buf,
|
||||
unsigned long *current_lbasize, const unsigned long *supported);
|
||||
ssize_t nd_size_select_store(struct device *dev, const char *buf,
|
||||
unsigned long *current_size, const unsigned long *supported);
|
||||
int __init nvdimm_init(void);
|
||||
int __init nd_region_init(void);
|
||||
int __init nd_label_init(void);
|
||||
@ -285,6 +286,13 @@ static inline struct device *nd_btt_create(struct nd_region *nd_region)
|
||||
|
||||
struct nd_pfn *to_nd_pfn(struct device *dev);
|
||||
#if IS_ENABLED(CONFIG_NVDIMM_PFN)
|
||||
|
||||
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
||||
#define PFN_DEFAULT_ALIGNMENT HPAGE_PMD_SIZE
|
||||
#else
|
||||
#define PFN_DEFAULT_ALIGNMENT PAGE_SIZE
|
||||
#endif
|
||||
|
||||
int nd_pfn_probe(struct device *dev, struct nd_namespace_common *ndns);
|
||||
bool is_nd_pfn(struct device *dev);
|
||||
struct device *nd_pfn_create(struct nd_region *nd_region);
|
||||
|
@ -111,24 +111,27 @@ static ssize_t align_show(struct device *dev,
|
||||
return sprintf(buf, "%ld\n", nd_pfn->align);
|
||||
}
|
||||
|
||||
static ssize_t __align_store(struct nd_pfn *nd_pfn, const char *buf)
|
||||
static const unsigned long *nd_pfn_supported_alignments(void)
|
||||
{
|
||||
unsigned long val;
|
||||
int rc;
|
||||
/*
|
||||
* This needs to be a non-static variable because the *_SIZE
|
||||
* macros aren't always constants.
|
||||
*/
|
||||
const unsigned long supported_alignments[] = {
|
||||
PAGE_SIZE,
|
||||
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
||||
HPAGE_PMD_SIZE,
|
||||
#ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD
|
||||
HPAGE_PUD_SIZE,
|
||||
#endif
|
||||
#endif
|
||||
0,
|
||||
};
|
||||
static unsigned long data[ARRAY_SIZE(supported_alignments)];
|
||||
|
||||
rc = kstrtoul(buf, 0, &val);
|
||||
if (rc)
|
||||
return rc;
|
||||
memcpy(data, supported_alignments, sizeof(data));
|
||||
|
||||
if (!is_power_of_2(val) || val < PAGE_SIZE || val > SZ_1G)
|
||||
return -EINVAL;
|
||||
|
||||
if (nd_pfn->dev.driver)
|
||||
return -EBUSY;
|
||||
else
|
||||
nd_pfn->align = val;
|
||||
|
||||
return 0;
|
||||
return data;
|
||||
}
|
||||
|
||||
static ssize_t align_store(struct device *dev,
|
||||
@ -139,7 +142,8 @@ static ssize_t align_store(struct device *dev,
|
||||
|
||||
device_lock(dev);
|
||||
nvdimm_bus_lock(dev);
|
||||
rc = __align_store(nd_pfn, buf);
|
||||
rc = nd_size_select_store(dev, buf, &nd_pfn->align,
|
||||
nd_pfn_supported_alignments());
|
||||
dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__,
|
||||
rc, buf, buf[len - 1] == '\n' ? "" : "\n");
|
||||
nvdimm_bus_unlock(dev);
|
||||
@ -260,6 +264,13 @@ static ssize_t size_show(struct device *dev,
|
||||
}
|
||||
static DEVICE_ATTR_RO(size);
|
||||
|
||||
static ssize_t supported_alignments_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return nd_size_select_show(0, nd_pfn_supported_alignments(), buf);
|
||||
}
|
||||
static DEVICE_ATTR_RO(supported_alignments);
|
||||
|
||||
static struct attribute *nd_pfn_attributes[] = {
|
||||
&dev_attr_mode.attr,
|
||||
&dev_attr_namespace.attr,
|
||||
@ -267,6 +278,7 @@ static struct attribute *nd_pfn_attributes[] = {
|
||||
&dev_attr_align.attr,
|
||||
&dev_attr_resource.attr,
|
||||
&dev_attr_size.attr,
|
||||
&dev_attr_supported_alignments.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
@ -290,7 +302,7 @@ struct device *nd_pfn_devinit(struct nd_pfn *nd_pfn,
|
||||
return NULL;
|
||||
|
||||
nd_pfn->mode = PFN_MODE_NONE;
|
||||
nd_pfn->align = HPAGE_SIZE;
|
||||
nd_pfn->align = PFN_DEFAULT_ALIGNMENT;
|
||||
dev = &nd_pfn->dev;
|
||||
device_initialize(&nd_pfn->dev);
|
||||
if (ndns && !__nd_attach_ndns(&nd_pfn->dev, ndns, &nd_pfn->ndns)) {
|
||||
@ -638,11 +650,12 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn)
|
||||
/ PAGE_SIZE);
|
||||
if (nd_pfn->mode == PFN_MODE_PMEM) {
|
||||
/*
|
||||
* vmemmap_populate_hugepages() allocates the memmap array in
|
||||
* HPAGE_SIZE chunks.
|
||||
* The altmap should be padded out to the block size used
|
||||
* when populating the vmemmap. This *should* be equal to
|
||||
* PMD_SIZE for most architectures.
|
||||
*/
|
||||
offset = ALIGN(start + SZ_8K + 64 * npfns + dax_label_reserve,
|
||||
max(nd_pfn->align, HPAGE_SIZE)) - start;
|
||||
max(nd_pfn->align, PMD_SIZE)) - start;
|
||||
} else if (nd_pfn->mode == PFN_MODE_RAM)
|
||||
offset = ALIGN(start + SZ_8K + dax_label_reserve,
|
||||
nd_pfn->align) - start;
|
||||
|
@ -5,20 +5,6 @@
|
||||
#include <linux/pfn_t.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#ifdef CONFIG_ARCH_HAS_PMEM_API
|
||||
#define ARCH_MEMREMAP_PMEM MEMREMAP_WB
|
||||
void arch_wb_cache_pmem(void *addr, size_t size);
|
||||
void arch_invalidate_pmem(void *addr, size_t size);
|
||||
#else
|
||||
#define ARCH_MEMREMAP_PMEM MEMREMAP_WT
|
||||
static inline void arch_wb_cache_pmem(void *addr, size_t size)
|
||||
{
|
||||
}
|
||||
static inline void arch_invalidate_pmem(void *addr, size_t size)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/* this definition is in it's own header for tools/testing/nvdimm to consume */
|
||||
struct pmem_device {
|
||||
/* One contiguous memory region per device */
|
||||
|
@ -723,8 +723,9 @@ static ssize_t mappingN(struct device *dev, char *buf, int n)
|
||||
nd_mapping = &nd_region->mapping[n];
|
||||
nvdimm = nd_mapping->nvdimm;
|
||||
|
||||
return sprintf(buf, "%s,%llu,%llu\n", dev_name(&nvdimm->dev),
|
||||
nd_mapping->start, nd_mapping->size);
|
||||
return sprintf(buf, "%s,%llu,%llu,%d\n", dev_name(&nvdimm->dev),
|
||||
nd_mapping->start, nd_mapping->size,
|
||||
nd_mapping->position);
|
||||
}
|
||||
|
||||
#define REGION_MAPPING(idx) \
|
||||
@ -965,6 +966,7 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus,
|
||||
nd_region->mapping[i].nvdimm = nvdimm;
|
||||
nd_region->mapping[i].start = mapping->start;
|
||||
nd_region->mapping[i].size = mapping->size;
|
||||
nd_region->mapping[i].position = mapping->position;
|
||||
INIT_LIST_HEAD(&nd_region->mapping[i].labels);
|
||||
mutex_init(&nd_region->mapping[i].lock);
|
||||
|
||||
|
@ -114,6 +114,7 @@ struct ext2_sb_info {
|
||||
*/
|
||||
spinlock_t s_lock;
|
||||
struct mb_cache *s_ea_block_cache;
|
||||
struct dax_device *s_daxdev;
|
||||
};
|
||||
|
||||
static inline spinlock_t *
|
||||
|
@ -800,10 +800,10 @@ int ext2_get_block(struct inode *inode, sector_t iblock,
|
||||
static int ext2_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
|
||||
unsigned flags, struct iomap *iomap)
|
||||
{
|
||||
struct block_device *bdev;
|
||||
unsigned int blkbits = inode->i_blkbits;
|
||||
unsigned long first_block = offset >> blkbits;
|
||||
unsigned long max_blocks = (length + (1 << blkbits) - 1) >> blkbits;
|
||||
struct ext2_sb_info *sbi = EXT2_SB(inode->i_sb);
|
||||
bool new = false, boundary = false;
|
||||
u32 bno;
|
||||
int ret;
|
||||
@ -814,13 +814,9 @@ static int ext2_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
|
||||
return ret;
|
||||
|
||||
iomap->flags = 0;
|
||||
bdev = inode->i_sb->s_bdev;
|
||||
iomap->bdev = bdev;
|
||||
iomap->bdev = inode->i_sb->s_bdev;
|
||||
iomap->offset = (u64)first_block << blkbits;
|
||||
if (blk_queue_dax(bdev->bd_queue))
|
||||
iomap->dax_dev = fs_dax_get_by_host(bdev->bd_disk->disk_name);
|
||||
else
|
||||
iomap->dax_dev = NULL;
|
||||
iomap->dax_dev = sbi->s_daxdev;
|
||||
|
||||
if (ret == 0) {
|
||||
iomap->type = IOMAP_HOLE;
|
||||
@ -842,7 +838,6 @@ static int
|
||||
ext2_iomap_end(struct inode *inode, loff_t offset, loff_t length,
|
||||
ssize_t written, unsigned flags, struct iomap *iomap)
|
||||
{
|
||||
fs_put_dax(iomap->dax_dev);
|
||||
if (iomap->type == IOMAP_MAPPED &&
|
||||
written < length &&
|
||||
(flags & IOMAP_WRITE))
|
||||
|
@ -171,6 +171,7 @@ static void ext2_put_super (struct super_block * sb)
|
||||
brelse (sbi->s_sbh);
|
||||
sb->s_fs_info = NULL;
|
||||
kfree(sbi->s_blockgroup_lock);
|
||||
fs_put_dax(sbi->s_daxdev);
|
||||
kfree(sbi);
|
||||
}
|
||||
|
||||
@ -813,6 +814,7 @@ static unsigned long descriptor_loc(struct super_block *sb,
|
||||
|
||||
static int ext2_fill_super(struct super_block *sb, void *data, int silent)
|
||||
{
|
||||
struct dax_device *dax_dev = fs_dax_get_by_bdev(sb->s_bdev);
|
||||
struct buffer_head * bh;
|
||||
struct ext2_sb_info * sbi;
|
||||
struct ext2_super_block * es;
|
||||
@ -842,6 +844,7 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)
|
||||
}
|
||||
sb->s_fs_info = sbi;
|
||||
sbi->s_sb_block = sb_block;
|
||||
sbi->s_daxdev = dax_dev;
|
||||
|
||||
spin_lock_init(&sbi->s_lock);
|
||||
|
||||
@ -1200,6 +1203,7 @@ failed_sbi:
|
||||
kfree(sbi->s_blockgroup_lock);
|
||||
kfree(sbi);
|
||||
failed:
|
||||
fs_put_dax(dax_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1526,6 +1526,7 @@ struct ext4_sb_info {
|
||||
|
||||
/* Barrier between changing inodes' journal flags and writepages ops. */
|
||||
struct percpu_rw_semaphore s_journal_flag_rwsem;
|
||||
struct dax_device *s_daxdev;
|
||||
};
|
||||
|
||||
static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb)
|
||||
|
@ -3397,7 +3397,7 @@ static int ext4_releasepage(struct page *page, gfp_t wait)
|
||||
static int ext4_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
|
||||
unsigned flags, struct iomap *iomap)
|
||||
{
|
||||
struct block_device *bdev;
|
||||
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
|
||||
unsigned int blkbits = inode->i_blkbits;
|
||||
unsigned long first_block = offset >> blkbits;
|
||||
unsigned long last_block = (offset + length - 1) >> blkbits;
|
||||
@ -3466,12 +3466,8 @@ retry:
|
||||
}
|
||||
|
||||
iomap->flags = 0;
|
||||
bdev = inode->i_sb->s_bdev;
|
||||
iomap->bdev = bdev;
|
||||
if (blk_queue_dax(bdev->bd_queue))
|
||||
iomap->dax_dev = fs_dax_get_by_host(bdev->bd_disk->disk_name);
|
||||
else
|
||||
iomap->dax_dev = NULL;
|
||||
iomap->bdev = inode->i_sb->s_bdev;
|
||||
iomap->dax_dev = sbi->s_daxdev;
|
||||
iomap->offset = first_block << blkbits;
|
||||
|
||||
if (ret == 0) {
|
||||
@ -3504,7 +3500,6 @@ static int ext4_iomap_end(struct inode *inode, loff_t offset, loff_t length,
|
||||
int blkbits = inode->i_blkbits;
|
||||
bool truncate = false;
|
||||
|
||||
fs_put_dax(iomap->dax_dev);
|
||||
if (!(flags & IOMAP_WRITE) || (flags & IOMAP_FAULT))
|
||||
return 0;
|
||||
|
||||
|
@ -951,6 +951,7 @@ static void ext4_put_super(struct super_block *sb)
|
||||
if (sbi->s_chksum_driver)
|
||||
crypto_free_shash(sbi->s_chksum_driver);
|
||||
kfree(sbi->s_blockgroup_lock);
|
||||
fs_put_dax(sbi->s_daxdev);
|
||||
kfree(sbi);
|
||||
}
|
||||
|
||||
@ -3398,6 +3399,7 @@ static void ext4_set_resv_clusters(struct super_block *sb)
|
||||
|
||||
static int ext4_fill_super(struct super_block *sb, void *data, int silent)
|
||||
{
|
||||
struct dax_device *dax_dev = fs_dax_get_by_bdev(sb->s_bdev);
|
||||
char *orig_data = kstrdup(data, GFP_KERNEL);
|
||||
struct buffer_head *bh;
|
||||
struct ext4_super_block *es = NULL;
|
||||
@ -3423,6 +3425,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
|
||||
if ((data && !orig_data) || !sbi)
|
||||
goto out_free_base;
|
||||
|
||||
sbi->s_daxdev = dax_dev;
|
||||
sbi->s_blockgroup_lock =
|
||||
kzalloc(sizeof(struct blockgroup_lock), GFP_KERNEL);
|
||||
if (!sbi->s_blockgroup_lock)
|
||||
@ -4399,6 +4402,7 @@ out_fail:
|
||||
out_free_base:
|
||||
kfree(sbi);
|
||||
kfree(orig_data);
|
||||
fs_put_dax(dax_dev);
|
||||
return err ? err : ret;
|
||||
}
|
||||
|
||||
|
@ -80,6 +80,19 @@ xfs_find_bdev_for_inode(
|
||||
return mp->m_ddev_targp->bt_bdev;
|
||||
}
|
||||
|
||||
struct dax_device *
|
||||
xfs_find_daxdev_for_inode(
|
||||
struct inode *inode)
|
||||
{
|
||||
struct xfs_inode *ip = XFS_I(inode);
|
||||
struct xfs_mount *mp = ip->i_mount;
|
||||
|
||||
if (XFS_IS_REALTIME_INODE(ip))
|
||||
return mp->m_rtdev_targp->bt_daxdev;
|
||||
else
|
||||
return mp->m_ddev_targp->bt_daxdev;
|
||||
}
|
||||
|
||||
/*
|
||||
* We're now finished for good with this page. Update the page state via the
|
||||
* associated buffer_heads, paying attention to the start and end offsets that
|
||||
|
@ -59,5 +59,6 @@ int xfs_setfilesize(struct xfs_inode *ip, xfs_off_t offset, size_t size);
|
||||
|
||||
extern void xfs_count_page_state(struct page *, int *, int *);
|
||||
extern struct block_device *xfs_find_bdev_for_inode(struct inode *);
|
||||
extern struct dax_device *xfs_find_daxdev_for_inode(struct inode *);
|
||||
|
||||
#endif /* __XFS_AOPS_H__ */
|
||||
|
@ -1802,7 +1802,8 @@ xfs_setsize_buftarg_early(
|
||||
xfs_buftarg_t *
|
||||
xfs_alloc_buftarg(
|
||||
struct xfs_mount *mp,
|
||||
struct block_device *bdev)
|
||||
struct block_device *bdev,
|
||||
struct dax_device *dax_dev)
|
||||
{
|
||||
xfs_buftarg_t *btp;
|
||||
|
||||
@ -1811,6 +1812,7 @@ xfs_alloc_buftarg(
|
||||
btp->bt_mount = mp;
|
||||
btp->bt_dev = bdev->bd_dev;
|
||||
btp->bt_bdev = bdev;
|
||||
btp->bt_daxdev = dax_dev;
|
||||
|
||||
if (xfs_setsize_buftarg_early(btp, bdev))
|
||||
goto error;
|
||||
|
@ -108,6 +108,7 @@ typedef unsigned int xfs_buf_flags_t;
|
||||
typedef struct xfs_buftarg {
|
||||
dev_t bt_dev;
|
||||
struct block_device *bt_bdev;
|
||||
struct dax_device *bt_daxdev;
|
||||
struct xfs_mount *bt_mount;
|
||||
unsigned int bt_meta_sectorsize;
|
||||
size_t bt_meta_sectormask;
|
||||
@ -385,7 +386,7 @@ xfs_buf_update_cksum(struct xfs_buf *bp, unsigned long cksum_offset)
|
||||
* Handling of buftargs.
|
||||
*/
|
||||
extern xfs_buftarg_t *xfs_alloc_buftarg(struct xfs_mount *,
|
||||
struct block_device *);
|
||||
struct block_device *, struct dax_device *);
|
||||
extern void xfs_free_buftarg(struct xfs_mount *, struct xfs_buftarg *);
|
||||
extern void xfs_wait_buftarg(xfs_buftarg_t *);
|
||||
extern int xfs_setsize_buftarg(xfs_buftarg_t *, unsigned int);
|
||||
|
@ -69,6 +69,7 @@ xfs_bmbt_to_iomap(
|
||||
iomap->offset = XFS_FSB_TO_B(mp, imap->br_startoff);
|
||||
iomap->length = XFS_FSB_TO_B(mp, imap->br_blockcount);
|
||||
iomap->bdev = xfs_find_bdev_for_inode(VFS_I(ip));
|
||||
iomap->dax_dev = xfs_find_daxdev_for_inode(VFS_I(ip));
|
||||
}
|
||||
|
||||
xfs_extlen_t
|
||||
@ -975,7 +976,6 @@ xfs_file_iomap_begin(
|
||||
int nimaps = 1, error = 0;
|
||||
bool shared = false, trimmed = false;
|
||||
unsigned lockmode;
|
||||
struct block_device *bdev;
|
||||
|
||||
if (XFS_FORCED_SHUTDOWN(mp))
|
||||
return -EIO;
|
||||
@ -1085,13 +1085,6 @@ xfs_file_iomap_begin(
|
||||
|
||||
xfs_bmbt_to_iomap(ip, iomap, &imap);
|
||||
|
||||
/* optionally associate a dax device with the iomap bdev */
|
||||
bdev = iomap->bdev;
|
||||
if (blk_queue_dax(bdev->bd_queue))
|
||||
iomap->dax_dev = fs_dax_get_by_host(bdev->bd_disk->disk_name);
|
||||
else
|
||||
iomap->dax_dev = NULL;
|
||||
|
||||
if (shared)
|
||||
iomap->flags |= IOMAP_F_SHARED;
|
||||
return 0;
|
||||
@ -1169,7 +1162,6 @@ xfs_file_iomap_end(
|
||||
unsigned flags,
|
||||
struct iomap *iomap)
|
||||
{
|
||||
fs_put_dax(iomap->dax_dev);
|
||||
if ((flags & IOMAP_WRITE) && iomap->type == IOMAP_DELALLOC)
|
||||
return xfs_file_iomap_end_delalloc(XFS_I(inode), offset,
|
||||
length, written, iomap);
|
||||
|
@ -714,17 +714,26 @@ STATIC void
|
||||
xfs_close_devices(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
struct dax_device *dax_ddev = mp->m_ddev_targp->bt_daxdev;
|
||||
|
||||
if (mp->m_logdev_targp && mp->m_logdev_targp != mp->m_ddev_targp) {
|
||||
struct block_device *logdev = mp->m_logdev_targp->bt_bdev;
|
||||
struct dax_device *dax_logdev = mp->m_logdev_targp->bt_daxdev;
|
||||
|
||||
xfs_free_buftarg(mp, mp->m_logdev_targp);
|
||||
xfs_blkdev_put(logdev);
|
||||
fs_put_dax(dax_logdev);
|
||||
}
|
||||
if (mp->m_rtdev_targp) {
|
||||
struct block_device *rtdev = mp->m_rtdev_targp->bt_bdev;
|
||||
struct dax_device *dax_rtdev = mp->m_rtdev_targp->bt_daxdev;
|
||||
|
||||
xfs_free_buftarg(mp, mp->m_rtdev_targp);
|
||||
xfs_blkdev_put(rtdev);
|
||||
fs_put_dax(dax_rtdev);
|
||||
}
|
||||
xfs_free_buftarg(mp, mp->m_ddev_targp);
|
||||
fs_put_dax(dax_ddev);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -742,6 +751,8 @@ xfs_open_devices(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
struct block_device *ddev = mp->m_super->s_bdev;
|
||||
struct dax_device *dax_ddev = fs_dax_get_by_bdev(ddev);
|
||||
struct dax_device *dax_logdev = NULL, *dax_rtdev = NULL;
|
||||
struct block_device *logdev = NULL, *rtdev = NULL;
|
||||
int error;
|
||||
|
||||
@ -752,6 +763,7 @@ xfs_open_devices(
|
||||
error = xfs_blkdev_get(mp, mp->m_logname, &logdev);
|
||||
if (error)
|
||||
goto out;
|
||||
dax_logdev = fs_dax_get_by_bdev(logdev);
|
||||
}
|
||||
|
||||
if (mp->m_rtname) {
|
||||
@ -765,24 +777,25 @@ xfs_open_devices(
|
||||
error = -EINVAL;
|
||||
goto out_close_rtdev;
|
||||
}
|
||||
dax_rtdev = fs_dax_get_by_bdev(rtdev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup xfs_mount buffer target pointers
|
||||
*/
|
||||
error = -ENOMEM;
|
||||
mp->m_ddev_targp = xfs_alloc_buftarg(mp, ddev);
|
||||
mp->m_ddev_targp = xfs_alloc_buftarg(mp, ddev, dax_ddev);
|
||||
if (!mp->m_ddev_targp)
|
||||
goto out_close_rtdev;
|
||||
|
||||
if (rtdev) {
|
||||
mp->m_rtdev_targp = xfs_alloc_buftarg(mp, rtdev);
|
||||
mp->m_rtdev_targp = xfs_alloc_buftarg(mp, rtdev, dax_rtdev);
|
||||
if (!mp->m_rtdev_targp)
|
||||
goto out_free_ddev_targ;
|
||||
}
|
||||
|
||||
if (logdev && logdev != ddev) {
|
||||
mp->m_logdev_targp = xfs_alloc_buftarg(mp, logdev);
|
||||
mp->m_logdev_targp = xfs_alloc_buftarg(mp, logdev, dax_logdev);
|
||||
if (!mp->m_logdev_targp)
|
||||
goto out_free_rtdev_targ;
|
||||
} else {
|
||||
@ -798,10 +811,14 @@ xfs_open_devices(
|
||||
xfs_free_buftarg(mp, mp->m_ddev_targp);
|
||||
out_close_rtdev:
|
||||
xfs_blkdev_put(rtdev);
|
||||
fs_put_dax(dax_rtdev);
|
||||
out_close_logdev:
|
||||
if (logdev && logdev != ddev)
|
||||
if (logdev && logdev != ddev) {
|
||||
xfs_blkdev_put(logdev);
|
||||
fs_put_dax(dax_logdev);
|
||||
}
|
||||
out:
|
||||
fs_put_dax(dax_ddev);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -57,6 +57,7 @@ static inline void fs_put_dax(struct dax_device *dax_dev)
|
||||
put_dax(dax_dev);
|
||||
}
|
||||
|
||||
struct dax_device *fs_dax_get_by_bdev(struct block_device *bdev);
|
||||
#else
|
||||
static inline int bdev_dax_supported(struct super_block *sb, int blocksize)
|
||||
{
|
||||
@ -71,6 +72,11 @@ static inline struct dax_device *fs_dax_get_by_host(const char *host)
|
||||
static inline void fs_put_dax(struct dax_device *dax_dev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline struct dax_device *fs_dax_get_by_bdev(struct block_device *bdev)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
int dax_read_lock(void);
|
||||
|
@ -87,6 +87,7 @@ struct nd_mapping_desc {
|
||||
struct nvdimm *nvdimm;
|
||||
u64 start;
|
||||
u64 size;
|
||||
int position;
|
||||
};
|
||||
|
||||
struct nd_region_desc {
|
||||
@ -173,4 +174,19 @@ u64 nd_fletcher64(void *addr, size_t len, bool le);
|
||||
void nvdimm_flush(struct nd_region *nd_region);
|
||||
int nvdimm_has_flush(struct nd_region *nd_region);
|
||||
int nvdimm_has_cache(struct nd_region *nd_region);
|
||||
|
||||
#ifdef CONFIG_ARCH_HAS_PMEM_API
|
||||
#define ARCH_MEMREMAP_PMEM MEMREMAP_WB
|
||||
void arch_wb_cache_pmem(void *addr, size_t size);
|
||||
void arch_invalidate_pmem(void *addr, size_t size);
|
||||
#else
|
||||
#define ARCH_MEMREMAP_PMEM MEMREMAP_WT
|
||||
static inline void arch_wb_cache_pmem(void *addr, size_t size)
|
||||
{
|
||||
}
|
||||
static inline void arch_invalidate_pmem(void *addr, size_t size)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __LIBNVDIMM_H__ */
|
||||
|
@ -559,9 +559,6 @@ config ARCH_HAS_PMEM_API
|
||||
config ARCH_HAS_UACCESS_FLUSHCACHE
|
||||
bool
|
||||
|
||||
config ARCH_HAS_MMIO_FLUSH
|
||||
bool
|
||||
|
||||
config STACKDEPOT
|
||||
bool
|
||||
select STACKTRACE
|
||||
|
@ -1546,8 +1546,8 @@ static int nfit_test_blk_do_io(struct nd_blk_region *ndbr, resource_size_t dpa,
|
||||
else {
|
||||
memcpy(iobuf, mmio->addr.base + dpa, len);
|
||||
|
||||
/* give us some some coverage of the mmio_flush_range() API */
|
||||
mmio_flush_range(mmio->addr.base + dpa, len);
|
||||
/* give us some some coverage of the arch_invalidate_pmem() API */
|
||||
arch_invalidate_pmem(mmio->addr.base + dpa, len);
|
||||
}
|
||||
nd_region_release_lane(nd_region, lane);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user