You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge branch 'for-4.13/dax' into libnvdimm-for-next
This commit is contained in:
+1
-3
@@ -7592,9 +7592,7 @@ M: Ross Zwisler <ross.zwisler@linux.intel.com>
|
||||
L: linux-nvdimm@lists.01.org
|
||||
Q: https://patchwork.kernel.org/project/linux-nvdimm/list/
|
||||
S: Supported
|
||||
F: drivers/nvdimm/pmem.c
|
||||
F: include/linux/pmem.h
|
||||
F: arch/*/include/asm/pmem.h
|
||||
F: drivers/nvdimm/pmem*
|
||||
|
||||
LIGHTNVM PLATFORM SUPPORT
|
||||
M: Matias Bjorling <mb@lightnvm.io>
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pfn_t.h>
|
||||
#include <linux/uio.h>
|
||||
|
||||
#include <asm/page.h>
|
||||
#include <asm/prom.h>
|
||||
@@ -163,8 +164,15 @@ axon_ram_dax_direct_access(struct dax_device *dax_dev, pgoff_t pgoff, long nr_pa
|
||||
return __axon_ram_direct_access(bank, pgoff, nr_pages, kaddr, pfn);
|
||||
}
|
||||
|
||||
static size_t axon_ram_copy_from_iter(struct dax_device *dax_dev, pgoff_t pgoff,
|
||||
void *addr, size_t bytes, struct iov_iter *i)
|
||||
{
|
||||
return copy_from_iter(addr, bytes, i);
|
||||
}
|
||||
|
||||
static const struct dax_operations axon_ram_dax_ops = {
|
||||
.direct_access = axon_ram_dax_direct_access,
|
||||
.copy_from_iter = axon_ram_copy_from_iter,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -54,6 +54,7 @@ config X86
|
||||
select ARCH_HAS_KCOV if X86_64
|
||||
select ARCH_HAS_MMIO_FLUSH
|
||||
select ARCH_HAS_PMEM_API if X86_64
|
||||
select ARCH_HAS_UACCESS_FLUSHCACHE if X86_64
|
||||
select ARCH_HAS_SET_MEMORY
|
||||
select ARCH_HAS_SG_CHAIN
|
||||
select ARCH_HAS_STRICT_KERNEL_RWX
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
* Copyright(c) 2015 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License 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.
|
||||
*/
|
||||
#ifndef __ASM_X86_PMEM_H__
|
||||
#define __ASM_X86_PMEM_H__
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/cpufeature.h>
|
||||
#include <asm/special_insns.h>
|
||||
|
||||
#ifdef CONFIG_ARCH_HAS_PMEM_API
|
||||
/**
|
||||
* arch_memcpy_to_pmem - copy data to persistent memory
|
||||
* @dst: destination buffer for the copy
|
||||
* @src: source buffer for the copy
|
||||
* @n: length of the copy in bytes
|
||||
*
|
||||
* Copy data to persistent memory media via non-temporal stores so that
|
||||
* a subsequent pmem driver flush operation will drain posted write queues.
|
||||
*/
|
||||
static inline void arch_memcpy_to_pmem(void *dst, const void *src, size_t n)
|
||||
{
|
||||
int rem;
|
||||
|
||||
/*
|
||||
* We are copying between two kernel buffers, if
|
||||
* __copy_from_user_inatomic_nocache() returns an error (page
|
||||
* fault) we would have already reported a general protection fault
|
||||
* before the WARN+BUG.
|
||||
*/
|
||||
rem = __copy_from_user_inatomic_nocache(dst, (void __user *) src, n);
|
||||
if (WARN(rem, "%s: fault copying %p <- %p unwritten: %d\n",
|
||||
__func__, dst, src, rem))
|
||||
BUG();
|
||||
}
|
||||
|
||||
/**
|
||||
* arch_wb_cache_pmem - write back a cache range with CLWB
|
||||
* @vaddr: virtual start address
|
||||
* @size: number of bytes to write back
|
||||
*
|
||||
* Write back a cache range using the CLWB (cache line write back)
|
||||
* instruction. Note that @size is internally rounded up to be cache
|
||||
* line size aligned.
|
||||
*/
|
||||
static inline void arch_wb_cache_pmem(void *addr, size_t size)
|
||||
{
|
||||
u16 x86_clflush_size = boot_cpu_data.x86_clflush_size;
|
||||
unsigned long clflush_mask = x86_clflush_size - 1;
|
||||
void *vend = addr + size;
|
||||
void *p;
|
||||
|
||||
for (p = (void *)((unsigned long)addr & ~clflush_mask);
|
||||
p < vend; p += x86_clflush_size)
|
||||
clwb(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* arch_copy_from_iter_pmem - copy data from an iterator to PMEM
|
||||
* @addr: PMEM destination address
|
||||
* @bytes: number of bytes to copy
|
||||
* @i: iterator with source data
|
||||
*
|
||||
* Copy data from the iterator 'i' to the PMEM buffer starting at 'addr'.
|
||||
*/
|
||||
static inline size_t arch_copy_from_iter_pmem(void *addr, size_t bytes,
|
||||
struct iov_iter *i)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
/* TODO: skip the write-back by always using non-temporal stores */
|
||||
len = copy_from_iter_nocache(addr, bytes, i);
|
||||
|
||||
/*
|
||||
* In the iovec case on x86_64 copy_from_iter_nocache() uses
|
||||
* non-temporal stores for the bulk of the transfer, but we need
|
||||
* to manually flush if the transfer is unaligned. A cached
|
||||
* memory copy is used when destination or size is not naturally
|
||||
* aligned. That is:
|
||||
* - Require 8-byte alignment when size is 8 bytes or larger.
|
||||
* - Require 4-byte alignment when size is 4 bytes.
|
||||
*
|
||||
* In the non-iovec case the entire destination needs to be
|
||||
* flushed.
|
||||
*/
|
||||
if (iter_is_iovec(i)) {
|
||||
unsigned long flushed, dest = (unsigned long) addr;
|
||||
|
||||
if (bytes < 8) {
|
||||
if (!IS_ALIGNED(dest, 4) || (bytes != 4))
|
||||
arch_wb_cache_pmem(addr, bytes);
|
||||
} else {
|
||||
if (!IS_ALIGNED(dest, 8)) {
|
||||
dest = ALIGN(dest, boot_cpu_data.x86_clflush_size);
|
||||
arch_wb_cache_pmem(addr, 1);
|
||||
}
|
||||
|
||||
flushed = dest - (unsigned long) addr;
|
||||
if (bytes > flushed && !IS_ALIGNED(bytes - flushed, 8))
|
||||
arch_wb_cache_pmem(addr + bytes - 1, 1);
|
||||
}
|
||||
} else
|
||||
arch_wb_cache_pmem(addr, bytes);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* arch_clear_pmem - zero a PMEM memory range
|
||||
* @addr: virtual start address
|
||||
* @size: number of bytes to zero
|
||||
*
|
||||
* Write zeros into the memory range starting at 'addr' for 'size' bytes.
|
||||
*/
|
||||
static inline void arch_clear_pmem(void *addr, size_t size)
|
||||
{
|
||||
memset(addr, 0, size);
|
||||
arch_wb_cache_pmem(addr, size);
|
||||
}
|
||||
|
||||
static inline void arch_invalidate_pmem(void *addr, size_t size)
|
||||
{
|
||||
clflush_cache_range(addr, size);
|
||||
}
|
||||
#endif /* CONFIG_ARCH_HAS_PMEM_API */
|
||||
#endif /* __ASM_X86_PMEM_H__ */
|
||||
@@ -109,6 +109,11 @@ memcpy_mcsafe(void *dst, const void *src, size_t cnt)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE
|
||||
#define __HAVE_ARCH_MEMCPY_FLUSHCACHE 1
|
||||
void memcpy_flushcache(void *dst, const void *src, size_t cnt);
|
||||
#endif
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif /* _ASM_X86_STRING_64_H */
|
||||
|
||||
@@ -171,6 +171,10 @@ unsigned long raw_copy_in_user(void __user *dst, const void __user *src, unsigne
|
||||
extern long __copy_user_nocache(void *dst, const void __user *src,
|
||||
unsigned size, int zerorest);
|
||||
|
||||
extern long __copy_user_flushcache(void *dst, const void __user *src, unsigned size);
|
||||
extern void memcpy_page_flushcache(char *to, struct page *page, size_t offset,
|
||||
size_t len);
|
||||
|
||||
static inline int
|
||||
__copy_from_user_inatomic_nocache(void *dst, const void __user *src,
|
||||
unsigned size)
|
||||
@@ -179,6 +183,13 @@ __copy_from_user_inatomic_nocache(void *dst, const void __user *src,
|
||||
return __copy_user_nocache(dst, src, size, 0);
|
||||
}
|
||||
|
||||
static inline int
|
||||
__copy_from_user_flushcache(void *dst, const void __user *src, unsigned size)
|
||||
{
|
||||
kasan_check_write(dst, size);
|
||||
return __copy_user_flushcache(dst, src, size);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
copy_user_handle_tail(char *to, char *from, unsigned len);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
#include <linux/export.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/highmem.h>
|
||||
|
||||
/*
|
||||
* Zero Userspace
|
||||
@@ -73,3 +74,136 @@ copy_user_handle_tail(char *to, char *from, unsigned len)
|
||||
clac();
|
||||
return len;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE
|
||||
/**
|
||||
* clean_cache_range - write back a cache range with CLWB
|
||||
* @vaddr: virtual start address
|
||||
* @size: number of bytes to write back
|
||||
*
|
||||
* Write back a cache range using the CLWB (cache line write back)
|
||||
* instruction. Note that @size is internally rounded up to be cache
|
||||
* line size aligned.
|
||||
*/
|
||||
static void clean_cache_range(void *addr, size_t size)
|
||||
{
|
||||
u16 x86_clflush_size = boot_cpu_data.x86_clflush_size;
|
||||
unsigned long clflush_mask = x86_clflush_size - 1;
|
||||
void *vend = addr + size;
|
||||
void *p;
|
||||
|
||||
for (p = (void *)((unsigned long)addr & ~clflush_mask);
|
||||
p < vend; p += x86_clflush_size)
|
||||
clwb(p);
|
||||
}
|
||||
|
||||
void arch_wb_cache_pmem(void *addr, size_t size)
|
||||
{
|
||||
clean_cache_range(addr, size);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(arch_wb_cache_pmem);
|
||||
|
||||
long __copy_user_flushcache(void *dst, const void __user *src, unsigned size)
|
||||
{
|
||||
unsigned long flushed, dest = (unsigned long) dst;
|
||||
long rc = __copy_user_nocache(dst, src, size, 0);
|
||||
|
||||
/*
|
||||
* __copy_user_nocache() uses non-temporal stores for the bulk
|
||||
* of the transfer, but we need to manually flush if the
|
||||
* transfer is unaligned. A cached memory copy is used when
|
||||
* destination or size is not naturally aligned. That is:
|
||||
* - Require 8-byte alignment when size is 8 bytes or larger.
|
||||
* - Require 4-byte alignment when size is 4 bytes.
|
||||
*/
|
||||
if (size < 8) {
|
||||
if (!IS_ALIGNED(dest, 4) || size != 4)
|
||||
clean_cache_range(dst, 1);
|
||||
} else {
|
||||
if (!IS_ALIGNED(dest, 8)) {
|
||||
dest = ALIGN(dest, boot_cpu_data.x86_clflush_size);
|
||||
clean_cache_range(dst, 1);
|
||||
}
|
||||
|
||||
flushed = dest - (unsigned long) dst;
|
||||
if (size > flushed && !IS_ALIGNED(size - flushed, 8))
|
||||
clean_cache_range(dst + size - 1, 1);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void memcpy_flushcache(void *_dst, const void *_src, size_t size)
|
||||
{
|
||||
unsigned long dest = (unsigned long) _dst;
|
||||
unsigned long source = (unsigned long) _src;
|
||||
|
||||
/* cache copy and flush to align dest */
|
||||
if (!IS_ALIGNED(dest, 8)) {
|
||||
unsigned len = min_t(unsigned, size, ALIGN(dest, 8) - dest);
|
||||
|
||||
memcpy((void *) dest, (void *) source, len);
|
||||
clean_cache_range((void *) dest, len);
|
||||
dest += len;
|
||||
source += len;
|
||||
size -= len;
|
||||
if (!size)
|
||||
return;
|
||||
}
|
||||
|
||||
/* 4x8 movnti loop */
|
||||
while (size >= 32) {
|
||||
asm("movq (%0), %%r8\n"
|
||||
"movq 8(%0), %%r9\n"
|
||||
"movq 16(%0), %%r10\n"
|
||||
"movq 24(%0), %%r11\n"
|
||||
"movnti %%r8, (%1)\n"
|
||||
"movnti %%r9, 8(%1)\n"
|
||||
"movnti %%r10, 16(%1)\n"
|
||||
"movnti %%r11, 24(%1)\n"
|
||||
:: "r" (source), "r" (dest)
|
||||
: "memory", "r8", "r9", "r10", "r11");
|
||||
dest += 32;
|
||||
source += 32;
|
||||
size -= 32;
|
||||
}
|
||||
|
||||
/* 1x8 movnti loop */
|
||||
while (size >= 8) {
|
||||
asm("movq (%0), %%r8\n"
|
||||
"movnti %%r8, (%1)\n"
|
||||
:: "r" (source), "r" (dest)
|
||||
: "memory", "r8");
|
||||
dest += 8;
|
||||
source += 8;
|
||||
size -= 8;
|
||||
}
|
||||
|
||||
/* 1x4 movnti loop */
|
||||
while (size >= 4) {
|
||||
asm("movl (%0), %%r8d\n"
|
||||
"movnti %%r8d, (%1)\n"
|
||||
:: "r" (source), "r" (dest)
|
||||
: "memory", "r8");
|
||||
dest += 4;
|
||||
source += 4;
|
||||
size -= 4;
|
||||
}
|
||||
|
||||
/* cache copy for remaining bytes */
|
||||
if (size) {
|
||||
memcpy((void *) dest, (void *) source, size);
|
||||
clean_cache_range((void *) dest, size);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(memcpy_flushcache);
|
||||
|
||||
void memcpy_page_flushcache(char *to, struct page *page, size_t offset,
|
||||
size_t len)
|
||||
{
|
||||
char *from = kmap_atomic(page);
|
||||
|
||||
memcpy_flushcache(to, from + offset, len);
|
||||
kunmap_atomic(from);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -150,6 +150,12 @@ void clflush_cache_range(void *vaddr, unsigned int size)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clflush_cache_range);
|
||||
|
||||
void arch_invalidate_pmem(void *addr, size_t size)
|
||||
{
|
||||
clflush_cache_range(addr, size);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(arch_invalidate_pmem);
|
||||
|
||||
static void __cpa_flush_all(void *arg)
|
||||
{
|
||||
unsigned long cache = (unsigned long)arg;
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/sort.h>
|
||||
#include <linux/pmem.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/nd.h>
|
||||
#include <asm/cacheflush.h>
|
||||
@@ -1928,8 +1927,7 @@ static int acpi_nfit_blk_single_io(struct nfit_blk *nfit_blk,
|
||||
}
|
||||
|
||||
if (rw)
|
||||
memcpy_to_pmem(mmio->addr.aperture + offset,
|
||||
iobuf + copied, c);
|
||||
memcpy_flushcache(mmio->addr.aperture + offset, iobuf + copied, c);
|
||||
else {
|
||||
if (nfit_blk->dimm_flags & NFIT_BLK_READ_FLUSH)
|
||||
mmio_flush_range((void __force *)
|
||||
@@ -2043,7 +2041,7 @@ static int acpi_nfit_blk_region_enable(struct nvdimm_bus *nvdimm_bus,
|
||||
nfit_blk->bdw_offset = nfit_mem->bdw->offset;
|
||||
mmio = &nfit_blk->mmio[BDW];
|
||||
mmio->addr.base = devm_nvdimm_memremap(dev, nfit_mem->spa_bdw->address,
|
||||
nfit_mem->spa_bdw->length, ARCH_MEMREMAP_PMEM);
|
||||
nfit_mem->spa_bdw->length, nd_blk_memremap_flags(ndbr));
|
||||
if (!mmio->addr.base) {
|
||||
dev_dbg(dev, "%s: %s failed to map bdw\n", __func__,
|
||||
nvdimm_name(nvdimm));
|
||||
@@ -2320,6 +2318,13 @@ static bool nfit_spa_is_virtual(struct acpi_nfit_system_address *spa)
|
||||
nfit_spa_type(spa) == NFIT_SPA_PCD);
|
||||
}
|
||||
|
||||
static bool nfit_spa_is_volatile(struct acpi_nfit_system_address *spa)
|
||||
{
|
||||
return (nfit_spa_type(spa) == NFIT_SPA_VDISK ||
|
||||
nfit_spa_type(spa) == NFIT_SPA_VCD ||
|
||||
nfit_spa_type(spa) == NFIT_SPA_VOLATILE);
|
||||
}
|
||||
|
||||
static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc,
|
||||
struct nfit_spa *nfit_spa)
|
||||
{
|
||||
@@ -2394,7 +2399,7 @@ static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc,
|
||||
ndr_desc);
|
||||
if (!nfit_spa->nd_region)
|
||||
rc = -ENOMEM;
|
||||
} else if (nfit_spa_type(spa) == NFIT_SPA_VOLATILE) {
|
||||
} else if (nfit_spa_is_volatile(spa)) {
|
||||
nfit_spa->nd_region = nvdimm_volatile_region_create(nvdimm_bus,
|
||||
ndr_desc);
|
||||
if (!nfit_spa->nd_region)
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#ifdef CONFIG_BLK_DEV_RAM_DAX
|
||||
#include <linux/pfn_t.h>
|
||||
#include <linux/dax.h>
|
||||
#include <linux/uio.h>
|
||||
#endif
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
@@ -354,8 +355,15 @@ static long brd_dax_direct_access(struct dax_device *dax_dev,
|
||||
return __brd_direct_access(brd, pgoff, nr_pages, kaddr, pfn);
|
||||
}
|
||||
|
||||
static size_t brd_dax_copy_from_iter(struct dax_device *dax_dev, pgoff_t pgoff,
|
||||
void *addr, size_t bytes, struct iov_iter *i)
|
||||
{
|
||||
return copy_from_iter(addr, bytes, i);
|
||||
}
|
||||
|
||||
static const struct dax_operations brd_dax_ops = {
|
||||
.direct_access = brd_dax_direct_access,
|
||||
.copy_from_iter = brd_dax_copy_from_iter,
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
+112
-6
@@ -18,6 +18,7 @@
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/hash.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uio.h>
|
||||
#include <linux/dax.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
@@ -115,13 +116,20 @@ int __bdev_dax_supported(struct super_block *sb, int blocksize)
|
||||
EXPORT_SYMBOL_GPL(__bdev_dax_supported);
|
||||
#endif
|
||||
|
||||
enum dax_device_flags {
|
||||
/* !alive + rcu grace period == no new operations / mappings */
|
||||
DAXDEV_ALIVE,
|
||||
/* gate whether dax_flush() calls the low level flush routine */
|
||||
DAXDEV_WRITE_CACHE,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dax_device - anchor object for dax services
|
||||
* @inode: core vfs
|
||||
* @cdev: optional character interface for "device dax"
|
||||
* @host: optional name for lookups where the device path is not available
|
||||
* @private: dax driver private data
|
||||
* @alive: !alive + rcu grace period == no new operations / mappings
|
||||
* @flags: state and boolean properties
|
||||
*/
|
||||
struct dax_device {
|
||||
struct hlist_node list;
|
||||
@@ -129,10 +137,75 @@ struct dax_device {
|
||||
struct cdev cdev;
|
||||
const char *host;
|
||||
void *private;
|
||||
bool alive;
|
||||
unsigned long flags;
|
||||
const struct dax_operations *ops;
|
||||
};
|
||||
|
||||
static ssize_t write_cache_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct dax_device *dax_dev = dax_get_by_host(dev_name(dev));
|
||||
ssize_t rc;
|
||||
|
||||
WARN_ON_ONCE(!dax_dev);
|
||||
if (!dax_dev)
|
||||
return -ENXIO;
|
||||
|
||||
rc = sprintf(buf, "%d\n", !!test_bit(DAXDEV_WRITE_CACHE,
|
||||
&dax_dev->flags));
|
||||
put_dax(dax_dev);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t write_cache_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t len)
|
||||
{
|
||||
bool write_cache;
|
||||
int rc = strtobool(buf, &write_cache);
|
||||
struct dax_device *dax_dev = dax_get_by_host(dev_name(dev));
|
||||
|
||||
WARN_ON_ONCE(!dax_dev);
|
||||
if (!dax_dev)
|
||||
return -ENXIO;
|
||||
|
||||
if (rc)
|
||||
len = rc;
|
||||
else if (write_cache)
|
||||
set_bit(DAXDEV_WRITE_CACHE, &dax_dev->flags);
|
||||
else
|
||||
clear_bit(DAXDEV_WRITE_CACHE, &dax_dev->flags);
|
||||
|
||||
put_dax(dax_dev);
|
||||
return len;
|
||||
}
|
||||
static DEVICE_ATTR_RW(write_cache);
|
||||
|
||||
static umode_t dax_visible(struct kobject *kobj, struct attribute *a, int n)
|
||||
{
|
||||
struct device *dev = container_of(kobj, typeof(*dev), kobj);
|
||||
struct dax_device *dax_dev = dax_get_by_host(dev_name(dev));
|
||||
|
||||
WARN_ON_ONCE(!dax_dev);
|
||||
if (!dax_dev)
|
||||
return 0;
|
||||
|
||||
if (a == &dev_attr_write_cache.attr && !dax_dev->ops->flush)
|
||||
return 0;
|
||||
return a->mode;
|
||||
}
|
||||
|
||||
static struct attribute *dax_attributes[] = {
|
||||
&dev_attr_write_cache.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
struct attribute_group dax_attribute_group = {
|
||||
.name = "dax",
|
||||
.attrs = dax_attributes,
|
||||
.is_visible = dax_visible,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(dax_attribute_group);
|
||||
|
||||
/**
|
||||
* dax_direct_access() - translate a device pgoff to an absolute pfn
|
||||
* @dax_dev: a dax_device instance representing the logical memory range
|
||||
@@ -172,10 +245,43 @@ long dax_direct_access(struct dax_device *dax_dev, pgoff_t pgoff, long nr_pages,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dax_direct_access);
|
||||
|
||||
size_t dax_copy_from_iter(struct dax_device *dax_dev, pgoff_t pgoff, void *addr,
|
||||
size_t bytes, struct iov_iter *i)
|
||||
{
|
||||
if (!dax_alive(dax_dev))
|
||||
return 0;
|
||||
|
||||
return dax_dev->ops->copy_from_iter(dax_dev, pgoff, addr, bytes, i);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dax_copy_from_iter);
|
||||
|
||||
void dax_flush(struct dax_device *dax_dev, pgoff_t pgoff, void *addr,
|
||||
size_t size)
|
||||
{
|
||||
if (!dax_alive(dax_dev))
|
||||
return;
|
||||
|
||||
if (!test_bit(DAXDEV_WRITE_CACHE, &dax_dev->flags))
|
||||
return;
|
||||
|
||||
if (dax_dev->ops->flush)
|
||||
dax_dev->ops->flush(dax_dev, pgoff, addr, size);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dax_flush);
|
||||
|
||||
void dax_write_cache(struct dax_device *dax_dev, bool wc)
|
||||
{
|
||||
if (wc)
|
||||
set_bit(DAXDEV_WRITE_CACHE, &dax_dev->flags);
|
||||
else
|
||||
clear_bit(DAXDEV_WRITE_CACHE, &dax_dev->flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dax_write_cache);
|
||||
|
||||
bool dax_alive(struct dax_device *dax_dev)
|
||||
{
|
||||
lockdep_assert_held(&dax_srcu);
|
||||
return dax_dev->alive;
|
||||
return test_bit(DAXDEV_ALIVE, &dax_dev->flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dax_alive);
|
||||
|
||||
@@ -195,7 +301,7 @@ void kill_dax(struct dax_device *dax_dev)
|
||||
if (!dax_dev)
|
||||
return;
|
||||
|
||||
dax_dev->alive = false;
|
||||
clear_bit(DAXDEV_ALIVE, &dax_dev->flags);
|
||||
|
||||
synchronize_srcu(&dax_srcu);
|
||||
|
||||
@@ -235,7 +341,7 @@ static void dax_destroy_inode(struct inode *inode)
|
||||
{
|
||||
struct dax_device *dax_dev = to_dax_dev(inode);
|
||||
|
||||
WARN_ONCE(dax_dev->alive,
|
||||
WARN_ONCE(test_bit(DAXDEV_ALIVE, &dax_dev->flags),
|
||||
"kill_dax() must be called before final iput()\n");
|
||||
call_rcu(&inode->i_rcu, dax_i_callback);
|
||||
}
|
||||
@@ -287,7 +393,7 @@ static struct dax_device *dax_dev_get(dev_t devt)
|
||||
|
||||
dax_dev = to_dax_dev(inode);
|
||||
if (inode->i_state & I_NEW) {
|
||||
dax_dev->alive = true;
|
||||
set_bit(DAXDEV_ALIVE, &dax_dev->flags);
|
||||
inode->i_cdev = &dax_dev->cdev;
|
||||
inode->i_mode = S_IFCHR;
|
||||
inode->i_flags = S_DAX;
|
||||
|
||||
@@ -159,6 +159,34 @@ static long linear_dax_direct_access(struct dm_target *ti, pgoff_t pgoff,
|
||||
return dax_direct_access(dax_dev, pgoff, nr_pages, kaddr, pfn);
|
||||
}
|
||||
|
||||
static size_t linear_dax_copy_from_iter(struct dm_target *ti, pgoff_t pgoff,
|
||||
void *addr, size_t bytes, struct iov_iter *i)
|
||||
{
|
||||
struct linear_c *lc = ti->private;
|
||||
struct block_device *bdev = lc->dev->bdev;
|
||||
struct dax_device *dax_dev = lc->dev->dax_dev;
|
||||
sector_t dev_sector, sector = pgoff * PAGE_SECTORS;
|
||||
|
||||
dev_sector = linear_map_sector(ti, sector);
|
||||
if (bdev_dax_pgoff(bdev, dev_sector, ALIGN(bytes, PAGE_SIZE), &pgoff))
|
||||
return 0;
|
||||
return dax_copy_from_iter(dax_dev, pgoff, addr, bytes, i);
|
||||
}
|
||||
|
||||
static void linear_dax_flush(struct dm_target *ti, pgoff_t pgoff, void *addr,
|
||||
size_t size)
|
||||
{
|
||||
struct linear_c *lc = ti->private;
|
||||
struct block_device *bdev = lc->dev->bdev;
|
||||
struct dax_device *dax_dev = lc->dev->dax_dev;
|
||||
sector_t dev_sector, sector = pgoff * PAGE_SECTORS;
|
||||
|
||||
dev_sector = linear_map_sector(ti, sector);
|
||||
if (bdev_dax_pgoff(bdev, dev_sector, ALIGN(size, PAGE_SIZE), &pgoff))
|
||||
return;
|
||||
dax_flush(dax_dev, pgoff, addr, size);
|
||||
}
|
||||
|
||||
static struct target_type linear_target = {
|
||||
.name = "linear",
|
||||
.version = {1, 3, 0},
|
||||
@@ -171,6 +199,8 @@ static struct target_type linear_target = {
|
||||
.prepare_ioctl = linear_prepare_ioctl,
|
||||
.iterate_devices = linear_iterate_devices,
|
||||
.direct_access = linear_dax_direct_access,
|
||||
.dax_copy_from_iter = linear_dax_copy_from_iter,
|
||||
.dax_flush = linear_dax_flush,
|
||||
};
|
||||
|
||||
int __init dm_linear_init(void)
|
||||
|
||||
@@ -332,6 +332,44 @@ static long stripe_dax_direct_access(struct dm_target *ti, pgoff_t pgoff,
|
||||
return dax_direct_access(dax_dev, pgoff, nr_pages, kaddr, pfn);
|
||||
}
|
||||
|
||||
static size_t stripe_dax_copy_from_iter(struct dm_target *ti, pgoff_t pgoff,
|
||||
void *addr, size_t bytes, struct iov_iter *i)
|
||||
{
|
||||
sector_t dev_sector, sector = pgoff * PAGE_SECTORS;
|
||||
struct stripe_c *sc = ti->private;
|
||||
struct dax_device *dax_dev;
|
||||
struct block_device *bdev;
|
||||
uint32_t stripe;
|
||||
|
||||
stripe_map_sector(sc, sector, &stripe, &dev_sector);
|
||||
dev_sector += sc->stripe[stripe].physical_start;
|
||||
dax_dev = sc->stripe[stripe].dev->dax_dev;
|
||||
bdev = sc->stripe[stripe].dev->bdev;
|
||||
|
||||
if (bdev_dax_pgoff(bdev, dev_sector, ALIGN(bytes, PAGE_SIZE), &pgoff))
|
||||
return 0;
|
||||
return dax_copy_from_iter(dax_dev, pgoff, addr, bytes, i);
|
||||
}
|
||||
|
||||
static void stripe_dax_flush(struct dm_target *ti, pgoff_t pgoff, void *addr,
|
||||
size_t size)
|
||||
{
|
||||
sector_t dev_sector, sector = pgoff * PAGE_SECTORS;
|
||||
struct stripe_c *sc = ti->private;
|
||||
struct dax_device *dax_dev;
|
||||
struct block_device *bdev;
|
||||
uint32_t stripe;
|
||||
|
||||
stripe_map_sector(sc, sector, &stripe, &dev_sector);
|
||||
dev_sector += sc->stripe[stripe].physical_start;
|
||||
dax_dev = sc->stripe[stripe].dev->dax_dev;
|
||||
bdev = sc->stripe[stripe].dev->bdev;
|
||||
|
||||
if (bdev_dax_pgoff(bdev, dev_sector, ALIGN(size, PAGE_SIZE), &pgoff))
|
||||
return;
|
||||
dax_flush(dax_dev, pgoff, addr, size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Stripe status:
|
||||
*
|
||||
@@ -451,6 +489,8 @@ static struct target_type stripe_target = {
|
||||
.iterate_devices = stripe_iterate_devices,
|
||||
.io_hints = stripe_io_hints,
|
||||
.direct_access = stripe_dax_direct_access,
|
||||
.dax_copy_from_iter = stripe_dax_copy_from_iter,
|
||||
.dax_flush = stripe_dax_flush,
|
||||
};
|
||||
|
||||
int __init dm_stripe_init(void)
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <linux/dax.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/uio.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/wait.h>
|
||||
@@ -969,6 +970,48 @@ static long dm_dax_direct_access(struct dax_device *dax_dev, pgoff_t pgoff,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static size_t dm_dax_copy_from_iter(struct dax_device *dax_dev, pgoff_t pgoff,
|
||||
void *addr, size_t bytes, struct iov_iter *i)
|
||||
{
|
||||
struct mapped_device *md = dax_get_private(dax_dev);
|
||||
sector_t sector = pgoff * PAGE_SECTORS;
|
||||
struct dm_target *ti;
|
||||
long ret = 0;
|
||||
int srcu_idx;
|
||||
|
||||
ti = dm_dax_get_live_target(md, sector, &srcu_idx);
|
||||
|
||||
if (!ti)
|
||||
goto out;
|
||||
if (!ti->type->dax_copy_from_iter) {
|
||||
ret = copy_from_iter(addr, bytes, i);
|
||||
goto out;
|
||||
}
|
||||
ret = ti->type->dax_copy_from_iter(ti, pgoff, addr, bytes, i);
|
||||
out:
|
||||
dm_put_live_table(md, srcu_idx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dm_dax_flush(struct dax_device *dax_dev, pgoff_t pgoff, void *addr,
|
||||
size_t size)
|
||||
{
|
||||
struct mapped_device *md = dax_get_private(dax_dev);
|
||||
sector_t sector = pgoff * PAGE_SECTORS;
|
||||
struct dm_target *ti;
|
||||
int srcu_idx;
|
||||
|
||||
ti = dm_dax_get_live_target(md, sector, &srcu_idx);
|
||||
|
||||
if (!ti)
|
||||
goto out;
|
||||
if (ti->type->dax_flush)
|
||||
ti->type->dax_flush(ti, pgoff, addr, size);
|
||||
out:
|
||||
dm_put_live_table(md, srcu_idx);
|
||||
}
|
||||
|
||||
/*
|
||||
* A target may call dm_accept_partial_bio only from the map routine. It is
|
||||
* allowed for all bio types except REQ_PREFLUSH.
|
||||
@@ -2859,6 +2902,8 @@ static const struct block_device_operations dm_blk_dops = {
|
||||
|
||||
static const struct dax_operations dm_dax_ops = {
|
||||
.direct_access = dm_dax_direct_access,
|
||||
.copy_from_iter = dm_dax_copy_from_iter,
|
||||
.flush = dm_dax_flush,
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -38,13 +38,13 @@ static int to_nd_device_type(struct device *dev)
|
||||
{
|
||||
if (is_nvdimm(dev))
|
||||
return ND_DEVICE_DIMM;
|
||||
else if (is_nd_pmem(dev))
|
||||
else if (is_memory(dev))
|
||||
return ND_DEVICE_REGION_PMEM;
|
||||
else if (is_nd_blk(dev))
|
||||
return ND_DEVICE_REGION_BLK;
|
||||
else if (is_nd_dax(dev))
|
||||
return ND_DEVICE_DAX_PMEM;
|
||||
else if (is_nd_pmem(dev->parent) || is_nd_blk(dev->parent))
|
||||
else if (is_nd_region(dev->parent))
|
||||
return nd_region_to_nstype(to_nd_region(dev->parent));
|
||||
|
||||
return 0;
|
||||
@@ -56,7 +56,7 @@ static int nvdimm_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
* Ensure that region devices always have their numa node set as
|
||||
* early as possible.
|
||||
*/
|
||||
if (is_nd_pmem(dev) || is_nd_blk(dev))
|
||||
if (is_nd_region(dev))
|
||||
set_dev_node(dev, to_nd_region(dev)->numa_node);
|
||||
return add_uevent_var(env, "MODALIAS=" ND_DEVICE_MODALIAS_FMT,
|
||||
to_nd_device_type(dev));
|
||||
@@ -65,7 +65,7 @@ static int nvdimm_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
static struct module *to_bus_provider(struct device *dev)
|
||||
{
|
||||
/* pin bus providers while regions are enabled */
|
||||
if (is_nd_pmem(dev) || is_nd_blk(dev)) {
|
||||
if (is_nd_region(dev)) {
|
||||
struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
|
||||
|
||||
return nvdimm_bus->nd_desc->module;
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/pmem.h>
|
||||
#include "nd-core.h"
|
||||
#include "pmem.h"
|
||||
#include "pfn.h"
|
||||
#include "btt.h"
|
||||
#include "nd.h"
|
||||
@@ -300,12 +300,12 @@ static int nsio_rw_bytes(struct nd_namespace_common *ndns,
|
||||
cleared /= 512;
|
||||
badblocks_clear(&nsio->bb, sector, cleared);
|
||||
}
|
||||
invalidate_pmem(nsio->addr + offset, size);
|
||||
arch_invalidate_pmem(nsio->addr + offset, size);
|
||||
} else
|
||||
rc = -EIO;
|
||||
}
|
||||
|
||||
memcpy_to_pmem(nsio->addr + offset, buf, size);
|
||||
memcpy_flushcache(nsio->addr + offset, buf, size);
|
||||
nvdimm_flush(to_nd_region(ndns->dev.parent));
|
||||
|
||||
return rc;
|
||||
|
||||
@@ -504,7 +504,7 @@ void nvdimm_badblocks_populate(struct nd_region *nd_region,
|
||||
struct nvdimm_bus *nvdimm_bus;
|
||||
struct list_head *poison_list;
|
||||
|
||||
if (!is_nd_pmem(&nd_region->dev)) {
|
||||
if (!is_memory(&nd_region->dev)) {
|
||||
dev_WARN_ONCE(&nd_region->dev, 1,
|
||||
"%s only valid for pmem regions\n", __func__);
|
||||
return;
|
||||
|
||||
@@ -89,7 +89,7 @@ struct device *nd_dax_create(struct nd_region *nd_region)
|
||||
struct device *dev = NULL;
|
||||
struct nd_dax *nd_dax;
|
||||
|
||||
if (!is_nd_pmem(&nd_region->dev))
|
||||
if (!is_memory(&nd_region->dev))
|
||||
return NULL;
|
||||
|
||||
nd_dax = nd_dax_alloc(nd_region);
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <linux/mm.h>
|
||||
#include "nd-core.h"
|
||||
#include "label.h"
|
||||
#include "pmem.h"
|
||||
#include "nd.h"
|
||||
|
||||
static DEFINE_IDA(dimm_ida);
|
||||
@@ -235,6 +236,13 @@ struct nvdimm *nd_blk_region_to_dimm(struct nd_blk_region *ndbr)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nd_blk_region_to_dimm);
|
||||
|
||||
unsigned long nd_blk_memremap_flags(struct nd_blk_region *ndbr)
|
||||
{
|
||||
/* pmem mapping properties are private to libnvdimm */
|
||||
return ARCH_MEMREMAP_PMEM;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nd_blk_memremap_flags);
|
||||
|
||||
struct nvdimm_drvdata *to_ndd(struct nd_mapping *nd_mapping)
|
||||
{
|
||||
struct nvdimm *nvdimm = nd_mapping->nvdimm;
|
||||
@@ -411,7 +419,7 @@ int alias_dpa_busy(struct device *dev, void *data)
|
||||
struct resource *res;
|
||||
int i;
|
||||
|
||||
if (!is_nd_pmem(dev))
|
||||
if (!is_memory(dev))
|
||||
return 0;
|
||||
|
||||
nd_region = to_nd_region(dev);
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/sort.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pmem.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/nd.h>
|
||||
#include "nd-core.h"
|
||||
#include "pmem.h"
|
||||
#include "nd.h"
|
||||
|
||||
static void namespace_io_release(struct device *dev)
|
||||
@@ -112,7 +112,7 @@ static int is_uuid_busy(struct device *dev, void *data)
|
||||
|
||||
static int is_namespace_uuid_busy(struct device *dev, void *data)
|
||||
{
|
||||
if (is_nd_pmem(dev) || is_nd_blk(dev))
|
||||
if (is_nd_region(dev))
|
||||
return device_for_each_child(dev, data, is_uuid_busy);
|
||||
return 0;
|
||||
}
|
||||
@@ -155,11 +155,7 @@ bool pmem_should_map_pages(struct device *dev)
|
||||
IORES_DESC_NONE) == REGION_MIXED)
|
||||
return false;
|
||||
|
||||
#ifdef ARCH_MEMREMAP_PMEM
|
||||
return ARCH_MEMREMAP_PMEM == MEMREMAP_WB;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL(pmem_should_map_pages);
|
||||
|
||||
@@ -810,7 +806,7 @@ static int __reserve_free_pmem(struct device *dev, void *data)
|
||||
struct nd_label_id label_id;
|
||||
int i;
|
||||
|
||||
if (!is_nd_pmem(dev))
|
||||
if (!is_memory(dev))
|
||||
return 0;
|
||||
|
||||
nd_region = to_nd_region(dev);
|
||||
@@ -2057,7 +2053,7 @@ static struct device *nd_namespace_pmem_create(struct nd_region *nd_region)
|
||||
struct resource *res;
|
||||
struct device *dev;
|
||||
|
||||
if (!is_nd_pmem(&nd_region->dev))
|
||||
if (!is_memory(&nd_region->dev))
|
||||
return NULL;
|
||||
|
||||
nspm = kzalloc(sizeof(*nspm), GFP_KERNEL);
|
||||
@@ -2360,7 +2356,7 @@ static struct device **scan_labels(struct nd_region *nd_region)
|
||||
}
|
||||
dev->parent = &nd_region->dev;
|
||||
devs[count++] = dev;
|
||||
} else if (is_nd_pmem(&nd_region->dev)) {
|
||||
} else if (is_memory(&nd_region->dev)) {
|
||||
/* clean unselected labels */
|
||||
for (i = 0; i < nd_region->ndr_mappings; i++) {
|
||||
struct list_head *l, *e;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user