2019-05-29 14:18:09 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2015-06-17 21:23:32 +00:00
|
|
|
/*
|
|
|
|
* Copyright(c) 2013-2015 Intel Corporation. All rights reserved.
|
|
|
|
*/
|
2016-06-16 03:17:02 +00:00
|
|
|
#include <linux/memremap.h>
|
2015-06-17 21:23:32 +00:00
|
|
|
#include <linux/rculist.h>
|
|
|
|
#include <linux/export.h>
|
|
|
|
#include <linux/ioport.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/types.h>
|
2016-06-16 03:17:02 +00:00
|
|
|
#include <linux/pfn_t.h>
|
2016-08-19 05:15:04 +00:00
|
|
|
#include <linux/acpi.h>
|
2015-06-17 21:23:32 +00:00
|
|
|
#include <linux/io.h>
|
2015-12-11 19:20:16 +00:00
|
|
|
#include <linux/mm.h>
|
2015-06-17 21:23:32 +00:00
|
|
|
#include "nfit_test.h"
|
|
|
|
|
|
|
|
static LIST_HEAD(iomap_head);
|
|
|
|
|
|
|
|
static struct iomap_ops {
|
|
|
|
nfit_test_lookup_fn nfit_test_lookup;
|
2016-12-05 21:43:25 +00:00
|
|
|
nfit_test_evaluate_dsm_fn evaluate_dsm;
|
2015-06-17 21:23:32 +00:00
|
|
|
struct list_head list;
|
|
|
|
} iomap_ops = {
|
|
|
|
.list = LIST_HEAD_INIT(iomap_ops.list),
|
|
|
|
};
|
|
|
|
|
2016-12-05 21:43:25 +00:00
|
|
|
void nfit_test_setup(nfit_test_lookup_fn lookup,
|
|
|
|
nfit_test_evaluate_dsm_fn evaluate)
|
2015-06-17 21:23:32 +00:00
|
|
|
{
|
|
|
|
iomap_ops.nfit_test_lookup = lookup;
|
2016-12-05 21:43:25 +00:00
|
|
|
iomap_ops.evaluate_dsm = evaluate;
|
2015-06-17 21:23:32 +00:00
|
|
|
list_add_rcu(&iomap_ops.list, &iomap_head);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(nfit_test_setup);
|
|
|
|
|
|
|
|
void nfit_test_teardown(void)
|
|
|
|
{
|
|
|
|
list_del_rcu(&iomap_ops.list);
|
|
|
|
synchronize_rcu();
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(nfit_test_teardown);
|
|
|
|
|
2015-12-11 19:20:16 +00:00
|
|
|
static struct nfit_test_resource *__get_nfit_res(resource_size_t resource)
|
2015-06-17 21:23:32 +00:00
|
|
|
{
|
|
|
|
struct iomap_ops *ops;
|
|
|
|
|
|
|
|
ops = list_first_or_null_rcu(&iomap_head, typeof(*ops), list);
|
|
|
|
if (ops)
|
|
|
|
return ops->nfit_test_lookup(resource);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-06-17 18:08:06 +00:00
|
|
|
struct nfit_test_resource *get_nfit_res(resource_size_t resource)
|
2015-06-17 21:23:32 +00:00
|
|
|
{
|
2015-12-11 19:20:16 +00:00
|
|
|
struct nfit_test_resource *res;
|
2015-06-17 21:23:32 +00:00
|
|
|
|
|
|
|
rcu_read_lock();
|
2015-12-11 19:20:16 +00:00
|
|
|
res = __get_nfit_res(resource);
|
2015-06-17 21:23:32 +00:00
|
|
|
rcu_read_unlock();
|
2015-12-11 19:20:16 +00:00
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
2016-06-17 18:08:06 +00:00
|
|
|
EXPORT_SYMBOL(get_nfit_res);
|
2015-12-11 19:20:16 +00:00
|
|
|
|
|
|
|
void __iomem *__nfit_test_ioremap(resource_size_t offset, unsigned long size,
|
|
|
|
void __iomem *(*fallback_fn)(resource_size_t, unsigned long))
|
|
|
|
{
|
|
|
|
struct nfit_test_resource *nfit_res = get_nfit_res(offset);
|
|
|
|
|
2015-06-17 21:23:32 +00:00
|
|
|
if (nfit_res)
|
|
|
|
return (void __iomem *) nfit_res->buf + offset
|
2016-10-06 18:22:37 +00:00
|
|
|
- nfit_res->res.start;
|
2015-06-17 21:23:32 +00:00
|
|
|
return fallback_fn(offset, size);
|
|
|
|
}
|
|
|
|
|
2020-01-06 08:43:50 +00:00
|
|
|
void __iomem *__wrap_devm_ioremap(struct device *dev,
|
2015-07-10 18:07:03 +00:00
|
|
|
resource_size_t offset, unsigned long size)
|
|
|
|
{
|
2015-12-11 19:20:16 +00:00
|
|
|
struct nfit_test_resource *nfit_res = get_nfit_res(offset);
|
2015-07-10 18:07:03 +00:00
|
|
|
|
|
|
|
if (nfit_res)
|
|
|
|
return (void __iomem *) nfit_res->buf + offset
|
2016-10-06 18:22:37 +00:00
|
|
|
- nfit_res->res.start;
|
2020-01-06 08:43:50 +00:00
|
|
|
return devm_ioremap(dev, offset, size);
|
2015-07-10 18:07:03 +00:00
|
|
|
}
|
2020-01-06 08:43:50 +00:00
|
|
|
EXPORT_SYMBOL(__wrap_devm_ioremap);
|
2015-07-10 18:07:03 +00:00
|
|
|
|
2015-08-11 03:07:08 +00:00
|
|
|
void *__wrap_devm_memremap(struct device *dev, resource_size_t offset,
|
|
|
|
size_t size, unsigned long flags)
|
2015-06-17 21:23:32 +00:00
|
|
|
{
|
2015-12-11 19:20:16 +00:00
|
|
|
struct nfit_test_resource *nfit_res = get_nfit_res(offset);
|
2015-08-12 22:42:56 +00:00
|
|
|
|
|
|
|
if (nfit_res)
|
2016-10-06 18:22:37 +00:00
|
|
|
return nfit_res->buf + offset - nfit_res->res.start;
|
2015-08-11 03:07:08 +00:00
|
|
|
return devm_memremap(dev, offset, size, flags);
|
2015-06-17 21:23:32 +00:00
|
|
|
}
|
2015-08-11 03:07:08 +00:00
|
|
|
EXPORT_SYMBOL(__wrap_devm_memremap);
|
2015-06-17 21:23:32 +00:00
|
|
|
|
mm, devm_memremap_pages: fix shutdown handling
The last step before devm_memremap_pages() returns success is to allocate
a release action, devm_memremap_pages_release(), to tear the entire setup
down. However, the result from devm_add_action() is not checked.
Checking the error from devm_add_action() is not enough. The api
currently relies on the fact that the percpu_ref it is using is killed by
the time the devm_memremap_pages_release() is run. Rather than continue
this awkward situation, offload the responsibility of killing the
percpu_ref to devm_memremap_pages_release() directly. This allows
devm_memremap_pages() to do the right thing relative to init failures and
shutdown.
Without this change we could fail to register the teardown of
devm_memremap_pages(). The likelihood of hitting this failure is tiny as
small memory allocations almost always succeed. However, the impact of
the failure is large given any future reconfiguration, or disable/enable,
of an nvdimm namespace will fail forever as subsequent calls to
devm_memremap_pages() will fail to setup the pgmap_radix since there will
be stale entries for the physical address range.
An argument could be made to require that the ->kill() operation be set in
the @pgmap arg rather than passed in separately. However, it helps code
readability, tracking the lifetime of a given instance, to be able to grep
the kill routine directly at the devm_memremap_pages() call site.
Link: http://lkml.kernel.org/r/154275558526.76910.7535251937849268605.stgit@dwillia2-desk3.amr.corp.intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Fixes: e8d513483300 ("memremap: change devm_memremap_pages interface...")
Reviewed-by: "Jérôme Glisse" <jglisse@redhat.com>
Reported-by: Logan Gunthorpe <logang@deltatee.com>
Reviewed-by: Logan Gunthorpe <logang@deltatee.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2018-12-28 08:34:57 +00:00
|
|
|
static void nfit_test_kill(void *_pgmap)
|
|
|
|
{
|
|
|
|
struct dev_pagemap *pgmap = _pgmap;
|
|
|
|
|
2019-06-26 12:27:14 +00:00
|
|
|
WARN_ON(!pgmap || !pgmap->ref);
|
|
|
|
|
|
|
|
if (pgmap->ops && pgmap->ops->kill)
|
|
|
|
pgmap->ops->kill(pgmap);
|
|
|
|
else
|
|
|
|
percpu_ref_kill(pgmap->ref);
|
|
|
|
|
|
|
|
if (pgmap->ops && pgmap->ops->cleanup) {
|
|
|
|
pgmap->ops->cleanup(pgmap);
|
|
|
|
} else {
|
|
|
|
wait_for_completion(&pgmap->done);
|
|
|
|
percpu_ref_exit(pgmap->ref);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dev_pagemap_percpu_release(struct percpu_ref *ref)
|
|
|
|
{
|
|
|
|
struct dev_pagemap *pgmap =
|
|
|
|
container_of(ref, struct dev_pagemap, internal_ref);
|
|
|
|
|
|
|
|
complete(&pgmap->done);
|
mm, devm_memremap_pages: fix shutdown handling
The last step before devm_memremap_pages() returns success is to allocate
a release action, devm_memremap_pages_release(), to tear the entire setup
down. However, the result from devm_add_action() is not checked.
Checking the error from devm_add_action() is not enough. The api
currently relies on the fact that the percpu_ref it is using is killed by
the time the devm_memremap_pages_release() is run. Rather than continue
this awkward situation, offload the responsibility of killing the
percpu_ref to devm_memremap_pages_release() directly. This allows
devm_memremap_pages() to do the right thing relative to init failures and
shutdown.
Without this change we could fail to register the teardown of
devm_memremap_pages(). The likelihood of hitting this failure is tiny as
small memory allocations almost always succeed. However, the impact of
the failure is large given any future reconfiguration, or disable/enable,
of an nvdimm namespace will fail forever as subsequent calls to
devm_memremap_pages() will fail to setup the pgmap_radix since there will
be stale entries for the physical address range.
An argument could be made to require that the ->kill() operation be set in
the @pgmap arg rather than passed in separately. However, it helps code
readability, tracking the lifetime of a given instance, to be able to grep
the kill routine directly at the devm_memremap_pages() call site.
Link: http://lkml.kernel.org/r/154275558526.76910.7535251937849268605.stgit@dwillia2-desk3.amr.corp.intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Fixes: e8d513483300 ("memremap: change devm_memremap_pages interface...")
Reviewed-by: "Jérôme Glisse" <jglisse@redhat.com>
Reported-by: Logan Gunthorpe <logang@deltatee.com>
Reviewed-by: Logan Gunthorpe <logang@deltatee.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2018-12-28 08:34:57 +00:00
|
|
|
}
|
|
|
|
|
2017-12-29 07:54:05 +00:00
|
|
|
void *__wrap_devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap)
|
2015-12-15 08:34:21 +00:00
|
|
|
{
|
2019-06-26 12:27:14 +00:00
|
|
|
int error;
|
2017-12-29 07:54:05 +00:00
|
|
|
resource_size_t offset = pgmap->res.start;
|
2015-12-11 19:20:16 +00:00
|
|
|
struct nfit_test_resource *nfit_res = get_nfit_res(offset);
|
2015-12-15 08:34:21 +00:00
|
|
|
|
2019-06-26 12:27:14 +00:00
|
|
|
if (!nfit_res)
|
|
|
|
return devm_memremap_pages(dev, pgmap);
|
|
|
|
|
|
|
|
if (!pgmap->ref) {
|
|
|
|
if (pgmap->ops && (pgmap->ops->kill || pgmap->ops->cleanup))
|
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
|
|
|
|
init_completion(&pgmap->done);
|
|
|
|
error = percpu_ref_init(&pgmap->internal_ref,
|
|
|
|
dev_pagemap_percpu_release, 0, GFP_KERNEL);
|
|
|
|
if (error)
|
|
|
|
return ERR_PTR(error);
|
|
|
|
pgmap->ref = &pgmap->internal_ref;
|
|
|
|
} else {
|
|
|
|
if (!pgmap->ops || !pgmap->ops->kill || !pgmap->ops->cleanup) {
|
|
|
|
WARN(1, "Missing reference count teardown definition\n");
|
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
}
|
mm, devm_memremap_pages: fix shutdown handling
The last step before devm_memremap_pages() returns success is to allocate
a release action, devm_memremap_pages_release(), to tear the entire setup
down. However, the result from devm_add_action() is not checked.
Checking the error from devm_add_action() is not enough. The api
currently relies on the fact that the percpu_ref it is using is killed by
the time the devm_memremap_pages_release() is run. Rather than continue
this awkward situation, offload the responsibility of killing the
percpu_ref to devm_memremap_pages_release() directly. This allows
devm_memremap_pages() to do the right thing relative to init failures and
shutdown.
Without this change we could fail to register the teardown of
devm_memremap_pages(). The likelihood of hitting this failure is tiny as
small memory allocations almost always succeed. However, the impact of
the failure is large given any future reconfiguration, or disable/enable,
of an nvdimm namespace will fail forever as subsequent calls to
devm_memremap_pages() will fail to setup the pgmap_radix since there will
be stale entries for the physical address range.
An argument could be made to require that the ->kill() operation be set in
the @pgmap arg rather than passed in separately. However, it helps code
readability, tracking the lifetime of a given instance, to be able to grep
the kill routine directly at the devm_memremap_pages() call site.
Link: http://lkml.kernel.org/r/154275558526.76910.7535251937849268605.stgit@dwillia2-desk3.amr.corp.intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Fixes: e8d513483300 ("memremap: change devm_memremap_pages interface...")
Reviewed-by: "Jérôme Glisse" <jglisse@redhat.com>
Reported-by: Logan Gunthorpe <logang@deltatee.com>
Reviewed-by: Logan Gunthorpe <logang@deltatee.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2018-12-28 08:34:57 +00:00
|
|
|
}
|
2019-06-26 12:27:14 +00:00
|
|
|
|
|
|
|
error = devm_add_action_or_reset(dev, nfit_test_kill, pgmap);
|
|
|
|
if (error)
|
|
|
|
return ERR_PTR(error);
|
|
|
|
return nfit_res->buf + offset - nfit_res->res.start;
|
2015-12-15 08:34:21 +00:00
|
|
|
}
|
2018-12-28 08:34:50 +00:00
|
|
|
EXPORT_SYMBOL_GPL(__wrap_devm_memremap_pages);
|
2015-12-15 08:34:21 +00:00
|
|
|
|
2016-01-22 17:43:28 +00:00
|
|
|
pfn_t __wrap_phys_to_pfn_t(phys_addr_t addr, unsigned long flags)
|
2015-12-15 08:34:21 +00:00
|
|
|
{
|
2015-12-11 19:20:16 +00:00
|
|
|
struct nfit_test_resource *nfit_res = get_nfit_res(addr);
|
2015-12-15 08:34:21 +00:00
|
|
|
|
|
|
|
if (nfit_res)
|
|
|
|
flags &= ~PFN_MAP;
|
|
|
|
return phys_to_pfn_t(addr, flags);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(__wrap_phys_to_pfn_t);
|
|
|
|
|
nd_blk: change aperture mapping from WC to WB
This should result in a pretty sizeable performance gain for reads. For
rough comparison I did some simple read testing using PMEM to compare
reads of write combining (WC) mappings vs write-back (WB). This was
done on a random lab machine.
PMEM reads from a write combining mapping:
# dd of=/dev/null if=/dev/pmem0 bs=4096 count=100000
100000+0 records in
100000+0 records out
409600000 bytes (410 MB) copied, 9.2855 s, 44.1 MB/s
PMEM reads from a write-back mapping:
# dd of=/dev/null if=/dev/pmem0 bs=4096 count=1000000
1000000+0 records in
1000000+0 records out
4096000000 bytes (4.1 GB) copied, 3.44034 s, 1.2 GB/s
To be able to safely support a write-back aperture I needed to add
support for the "read flush" _DSM flag, as outlined in the DSM spec:
http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf
This flag tells the ND BLK driver that it needs to flush the cache lines
associated with the aperture after the aperture is moved but before any
new data is read. This ensures that any stale cache lines from the
previous contents of the aperture will be discarded from the processor
cache, and the new data will be read properly from the DIMM. We know
that the cache lines are clean and will be discarded without any
writeback because either a) the previous aperture operation was a read,
and we never modified the contents of the aperture, or b) the previous
aperture operation was a write and we must have written back the dirtied
contents of the aperture to the DIMM before the I/O was completed.
In order to add support for the "read flush" flag I needed to add a
generic routine to invalidate cache lines, mmio_flush_range(). This is
protected by the ARCH_HAS_MMIO_FLUSH Kconfig variable, and is currently
only supported on x86.
Signed-off-by: Ross Zwisler <ross.zwisler@linux.intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2015-08-27 19:14:20 +00:00
|
|
|
void *__wrap_memremap(resource_size_t offset, size_t size,
|
|
|
|
unsigned long flags)
|
|
|
|
{
|
2015-12-11 19:20:16 +00:00
|
|
|
struct nfit_test_resource *nfit_res = get_nfit_res(offset);
|
nd_blk: change aperture mapping from WC to WB
This should result in a pretty sizeable performance gain for reads. For
rough comparison I did some simple read testing using PMEM to compare
reads of write combining (WC) mappings vs write-back (WB). This was
done on a random lab machine.
PMEM reads from a write combining mapping:
# dd of=/dev/null if=/dev/pmem0 bs=4096 count=100000
100000+0 records in
100000+0 records out
409600000 bytes (410 MB) copied, 9.2855 s, 44.1 MB/s
PMEM reads from a write-back mapping:
# dd of=/dev/null if=/dev/pmem0 bs=4096 count=1000000
1000000+0 records in
1000000+0 records out
4096000000 bytes (4.1 GB) copied, 3.44034 s, 1.2 GB/s
To be able to safely support a write-back aperture I needed to add
support for the "read flush" _DSM flag, as outlined in the DSM spec:
http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf
This flag tells the ND BLK driver that it needs to flush the cache lines
associated with the aperture after the aperture is moved but before any
new data is read. This ensures that any stale cache lines from the
previous contents of the aperture will be discarded from the processor
cache, and the new data will be read properly from the DIMM. We know
that the cache lines are clean and will be discarded without any
writeback because either a) the previous aperture operation was a read,
and we never modified the contents of the aperture, or b) the previous
aperture operation was a write and we must have written back the dirtied
contents of the aperture to the DIMM before the I/O was completed.
In order to add support for the "read flush" flag I needed to add a
generic routine to invalidate cache lines, mmio_flush_range(). This is
protected by the ARCH_HAS_MMIO_FLUSH Kconfig variable, and is currently
only supported on x86.
Signed-off-by: Ross Zwisler <ross.zwisler@linux.intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2015-08-27 19:14:20 +00:00
|
|
|
|
|
|
|
if (nfit_res)
|
2016-10-06 18:22:37 +00:00
|
|
|
return nfit_res->buf + offset - nfit_res->res.start;
|
nd_blk: change aperture mapping from WC to WB
This should result in a pretty sizeable performance gain for reads. For
rough comparison I did some simple read testing using PMEM to compare
reads of write combining (WC) mappings vs write-back (WB). This was
done on a random lab machine.
PMEM reads from a write combining mapping:
# dd of=/dev/null if=/dev/pmem0 bs=4096 count=100000
100000+0 records in
100000+0 records out
409600000 bytes (410 MB) copied, 9.2855 s, 44.1 MB/s
PMEM reads from a write-back mapping:
# dd of=/dev/null if=/dev/pmem0 bs=4096 count=1000000
1000000+0 records in
1000000+0 records out
4096000000 bytes (4.1 GB) copied, 3.44034 s, 1.2 GB/s
To be able to safely support a write-back aperture I needed to add
support for the "read flush" _DSM flag, as outlined in the DSM spec:
http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf
This flag tells the ND BLK driver that it needs to flush the cache lines
associated with the aperture after the aperture is moved but before any
new data is read. This ensures that any stale cache lines from the
previous contents of the aperture will be discarded from the processor
cache, and the new data will be read properly from the DIMM. We know
that the cache lines are clean and will be discarded without any
writeback because either a) the previous aperture operation was a read,
and we never modified the contents of the aperture, or b) the previous
aperture operation was a write and we must have written back the dirtied
contents of the aperture to the DIMM before the I/O was completed.
In order to add support for the "read flush" flag I needed to add a
generic routine to invalidate cache lines, mmio_flush_range(). This is
protected by the ARCH_HAS_MMIO_FLUSH Kconfig variable, and is currently
only supported on x86.
Signed-off-by: Ross Zwisler <ross.zwisler@linux.intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2015-08-27 19:14:20 +00:00
|
|
|
return memremap(offset, size, flags);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(__wrap_memremap);
|
|
|
|
|
2015-08-01 06:16:37 +00:00
|
|
|
void __wrap_devm_memunmap(struct device *dev, void *addr)
|
|
|
|
{
|
2015-12-11 19:20:16 +00:00
|
|
|
struct nfit_test_resource *nfit_res = get_nfit_res((long) addr);
|
2015-08-01 06:16:37 +00:00
|
|
|
|
|
|
|
if (nfit_res)
|
|
|
|
return;
|
|
|
|
return devm_memunmap(dev, addr);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(__wrap_devm_memunmap);
|
|
|
|
|
2019-11-14 00:22:06 +00:00
|
|
|
void __iomem *__wrap_ioremap(resource_size_t offset, unsigned long size)
|
|
|
|
{
|
|
|
|
return __nfit_test_ioremap(offset, size, ioremap);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(__wrap_ioremap);
|
|
|
|
|
2015-07-10 18:07:03 +00:00
|
|
|
void __iomem *__wrap_ioremap_wc(resource_size_t offset, unsigned long size)
|
|
|
|
{
|
|
|
|
return __nfit_test_ioremap(offset, size, ioremap_wc);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(__wrap_ioremap_wc);
|
|
|
|
|
2015-06-17 21:23:32 +00:00
|
|
|
void __wrap_iounmap(volatile void __iomem *addr)
|
|
|
|
{
|
2015-12-11 19:20:16 +00:00
|
|
|
struct nfit_test_resource *nfit_res = get_nfit_res((long) addr);
|
2015-06-17 21:23:32 +00:00
|
|
|
if (nfit_res)
|
|
|
|
return;
|
|
|
|
return iounmap(addr);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(__wrap_iounmap);
|
|
|
|
|
nd_blk: change aperture mapping from WC to WB
This should result in a pretty sizeable performance gain for reads. For
rough comparison I did some simple read testing using PMEM to compare
reads of write combining (WC) mappings vs write-back (WB). This was
done on a random lab machine.
PMEM reads from a write combining mapping:
# dd of=/dev/null if=/dev/pmem0 bs=4096 count=100000
100000+0 records in
100000+0 records out
409600000 bytes (410 MB) copied, 9.2855 s, 44.1 MB/s
PMEM reads from a write-back mapping:
# dd of=/dev/null if=/dev/pmem0 bs=4096 count=1000000
1000000+0 records in
1000000+0 records out
4096000000 bytes (4.1 GB) copied, 3.44034 s, 1.2 GB/s
To be able to safely support a write-back aperture I needed to add
support for the "read flush" _DSM flag, as outlined in the DSM spec:
http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf
This flag tells the ND BLK driver that it needs to flush the cache lines
associated with the aperture after the aperture is moved but before any
new data is read. This ensures that any stale cache lines from the
previous contents of the aperture will be discarded from the processor
cache, and the new data will be read properly from the DIMM. We know
that the cache lines are clean and will be discarded without any
writeback because either a) the previous aperture operation was a read,
and we never modified the contents of the aperture, or b) the previous
aperture operation was a write and we must have written back the dirtied
contents of the aperture to the DIMM before the I/O was completed.
In order to add support for the "read flush" flag I needed to add a
generic routine to invalidate cache lines, mmio_flush_range(). This is
protected by the ARCH_HAS_MMIO_FLUSH Kconfig variable, and is currently
only supported on x86.
Signed-off-by: Ross Zwisler <ross.zwisler@linux.intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2015-08-27 19:14:20 +00:00
|
|
|
void __wrap_memunmap(void *addr)
|
|
|
|
{
|
2015-12-11 19:20:16 +00:00
|
|
|
struct nfit_test_resource *nfit_res = get_nfit_res((long) addr);
|
nd_blk: change aperture mapping from WC to WB
This should result in a pretty sizeable performance gain for reads. For
rough comparison I did some simple read testing using PMEM to compare
reads of write combining (WC) mappings vs write-back (WB). This was
done on a random lab machine.
PMEM reads from a write combining mapping:
# dd of=/dev/null if=/dev/pmem0 bs=4096 count=100000
100000+0 records in
100000+0 records out
409600000 bytes (410 MB) copied, 9.2855 s, 44.1 MB/s
PMEM reads from a write-back mapping:
# dd of=/dev/null if=/dev/pmem0 bs=4096 count=1000000
1000000+0 records in
1000000+0 records out
4096000000 bytes (4.1 GB) copied, 3.44034 s, 1.2 GB/s
To be able to safely support a write-back aperture I needed to add
support for the "read flush" _DSM flag, as outlined in the DSM spec:
http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf
This flag tells the ND BLK driver that it needs to flush the cache lines
associated with the aperture after the aperture is moved but before any
new data is read. This ensures that any stale cache lines from the
previous contents of the aperture will be discarded from the processor
cache, and the new data will be read properly from the DIMM. We know
that the cache lines are clean and will be discarded without any
writeback because either a) the previous aperture operation was a read,
and we never modified the contents of the aperture, or b) the previous
aperture operation was a write and we must have written back the dirtied
contents of the aperture to the DIMM before the I/O was completed.
In order to add support for the "read flush" flag I needed to add a
generic routine to invalidate cache lines, mmio_flush_range(). This is
protected by the ARCH_HAS_MMIO_FLUSH Kconfig variable, and is currently
only supported on x86.
Signed-off-by: Ross Zwisler <ross.zwisler@linux.intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2015-08-27 19:14:20 +00:00
|
|
|
|
|
|
|
if (nfit_res)
|
|
|
|
return;
|
|
|
|
return memunmap(addr);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(__wrap_memunmap);
|
|
|
|
|
2016-10-06 18:22:37 +00:00
|
|
|
static bool nfit_test_release_region(struct device *dev,
|
|
|
|
struct resource *parent, resource_size_t start,
|
|
|
|
resource_size_t n);
|
|
|
|
|
|
|
|
static void nfit_devres_release(struct device *dev, void *data)
|
|
|
|
{
|
|
|
|
struct resource *res = *((struct resource **) data);
|
|
|
|
|
|
|
|
WARN_ON(!nfit_test_release_region(NULL, &iomem_resource, res->start,
|
|
|
|
resource_size(res)));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int match(struct device *dev, void *__res, void *match_data)
|
|
|
|
{
|
|
|
|
struct resource *res = *((struct resource **) __res);
|
|
|
|
resource_size_t start = *((resource_size_t *) match_data);
|
|
|
|
|
|
|
|
return res->start == start;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool nfit_test_release_region(struct device *dev,
|
|
|
|
struct resource *parent, resource_size_t start,
|
|
|
|
resource_size_t n)
|
|
|
|
{
|
|
|
|
if (parent == &iomem_resource) {
|
|
|
|
struct nfit_test_resource *nfit_res = get_nfit_res(start);
|
|
|
|
|
|
|
|
if (nfit_res) {
|
|
|
|
struct nfit_test_request *req;
|
|
|
|
struct resource *res = NULL;
|
|
|
|
|
|
|
|
if (dev) {
|
|
|
|
devres_release(dev, nfit_devres_release, match,
|
|
|
|
&start);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock(&nfit_res->lock);
|
|
|
|
list_for_each_entry(req, &nfit_res->requests, list)
|
|
|
|
if (req->res.start == start) {
|
|
|
|
res = &req->res;
|
|
|
|
list_del(&req->list);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
spin_unlock(&nfit_res->lock);
|
|
|
|
|
|
|
|
WARN(!res || resource_size(res) != n,
|
|
|
|
"%s: start: %llx n: %llx mismatch: %pr\n",
|
|
|
|
__func__, start, n, res);
|
|
|
|
if (res)
|
|
|
|
kfree(req);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-08-11 03:07:08 +00:00
|
|
|
static struct resource *nfit_test_request_region(struct device *dev,
|
|
|
|
struct resource *parent, resource_size_t start,
|
|
|
|
resource_size_t n, const char *name, int flags)
|
2015-06-17 21:23:32 +00:00
|
|
|
{
|
|
|
|
struct nfit_test_resource *nfit_res;
|
|
|
|
|
|
|
|
if (parent == &iomem_resource) {
|
|
|
|
nfit_res = get_nfit_res(start);
|
|
|
|
if (nfit_res) {
|
2016-10-06 18:22:37 +00:00
|
|
|
struct nfit_test_request *req;
|
|
|
|
struct resource *res = NULL;
|
2015-06-17 21:23:32 +00:00
|
|
|
|
2016-10-06 18:22:37 +00:00
|
|
|
if (start + n > nfit_res->res.start
|
|
|
|
+ resource_size(&nfit_res->res)) {
|
2015-06-17 21:23:32 +00:00
|
|
|
pr_debug("%s: start: %llx n: %llx overflow: %pr\n",
|
|
|
|
__func__, start, n,
|
2016-10-06 18:22:37 +00:00
|
|
|
&nfit_res->res);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock(&nfit_res->lock);
|
|
|
|
list_for_each_entry(req, &nfit_res->requests, list)
|
|
|
|
if (start == req->res.start) {
|
|
|
|
res = &req->res;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
spin_unlock(&nfit_res->lock);
|
|
|
|
|
|
|
|
if (res) {
|
|
|
|
WARN(1, "%pr already busy\n", res);
|
2015-06-17 21:23:32 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-10-06 18:22:37 +00:00
|
|
|
req = kzalloc(sizeof(*req), GFP_KERNEL);
|
|
|
|
if (!req)
|
|
|
|
return NULL;
|
|
|
|
INIT_LIST_HEAD(&req->list);
|
|
|
|
res = &req->res;
|
|
|
|
|
2015-06-17 21:23:32 +00:00
|
|
|
res->start = start;
|
|
|
|
res->end = start + n - 1;
|
|
|
|
res->name = name;
|
|
|
|
res->flags = resource_type(parent);
|
|
|
|
res->flags |= IORESOURCE_BUSY | flags;
|
2016-10-06 18:22:37 +00:00
|
|
|
spin_lock(&nfit_res->lock);
|
|
|
|
list_add(&req->list, &nfit_res->requests);
|
|
|
|
spin_unlock(&nfit_res->lock);
|
|
|
|
|
|
|
|
if (dev) {
|
|
|
|
struct resource **d;
|
|
|
|
|
|
|
|
d = devres_alloc(nfit_devres_release,
|
|
|
|
sizeof(struct resource *),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!d)
|
|
|
|
return NULL;
|
|
|
|
*d = res;
|
|
|
|
devres_add(dev, d);
|
|
|
|
}
|
|
|
|
|
2015-06-17 21:23:32 +00:00
|
|
|
pr_debug("%s: %pr\n", __func__, res);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
}
|
2015-08-11 03:07:08 +00:00
|
|
|
if (dev)
|
|
|
|
return __devm_request_region(dev, parent, start, n, name);
|
2015-06-17 21:23:32 +00:00
|
|
|
return __request_region(parent, start, n, name, flags);
|
|
|
|
}
|
2015-08-11 03:07:08 +00:00
|
|
|
|
|
|
|
struct resource *__wrap___request_region(struct resource *parent,
|
|
|
|
resource_size_t start, resource_size_t n, const char *name,
|
|
|
|
int flags)
|
|
|
|
{
|
|
|
|
return nfit_test_request_region(NULL, parent, start, n, name, flags);
|
|
|
|
}
|
2015-06-17 21:23:32 +00:00
|
|
|
EXPORT_SYMBOL(__wrap___request_region);
|
|
|
|
|
2016-06-16 03:34:17 +00:00
|
|
|
int __wrap_insert_resource(struct resource *parent, struct resource *res)
|
|
|
|
{
|
|
|
|
if (get_nfit_res(res->start))
|
|
|
|
return 0;
|
|
|
|
return insert_resource(parent, res);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(__wrap_insert_resource);
|
|
|
|
|
|
|
|
int __wrap_remove_resource(struct resource *res)
|
|
|
|
{
|
|
|
|
if (get_nfit_res(res->start))
|
|
|
|
return 0;
|
|
|
|
return remove_resource(res);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(__wrap_remove_resource);
|
|
|
|
|
2015-08-11 03:07:08 +00:00
|
|
|
struct resource *__wrap___devm_request_region(struct device *dev,
|
|
|
|
struct resource *parent, resource_size_t start,
|
|
|
|
resource_size_t n, const char *name)
|
|
|
|
{
|
|
|
|
if (!dev)
|
|
|
|
return NULL;
|
|
|
|
return nfit_test_request_region(dev, parent, start, n, name, 0);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(__wrap___devm_request_region);
|
|
|
|
|
2016-03-22 07:22:16 +00:00
|
|
|
void __wrap___release_region(struct resource *parent, resource_size_t start,
|
|
|
|
resource_size_t n)
|
|
|
|
{
|
2016-10-06 18:22:37 +00:00
|
|
|
if (!nfit_test_release_region(NULL, parent, start, n))
|
2016-03-22 07:22:16 +00:00
|
|
|
__release_region(parent, start, n);
|
2015-06-17 21:23:32 +00:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(__wrap___release_region);
|
|
|
|
|
2016-03-22 07:22:16 +00:00
|
|
|
void __wrap___devm_release_region(struct device *dev, struct resource *parent,
|
|
|
|
resource_size_t start, resource_size_t n)
|
|
|
|
{
|
2016-10-06 18:22:37 +00:00
|
|
|
if (!nfit_test_release_region(dev, parent, start, n))
|
2016-03-22 07:22:16 +00:00
|
|
|
__devm_release_region(dev, parent, start, n);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(__wrap___devm_release_region);
|
|
|
|
|
2016-08-19 05:15:04 +00:00
|
|
|
acpi_status __wrap_acpi_evaluate_object(acpi_handle handle, acpi_string path,
|
|
|
|
struct acpi_object_list *p, struct acpi_buffer *buf)
|
|
|
|
{
|
|
|
|
struct nfit_test_resource *nfit_res = get_nfit_res((long) handle);
|
|
|
|
union acpi_object **obj;
|
|
|
|
|
|
|
|
if (!nfit_res || strcmp(path, "_FIT") || !buf)
|
|
|
|
return acpi_evaluate_object(handle, path, p, buf);
|
|
|
|
|
|
|
|
obj = nfit_res->buf;
|
|
|
|
buf->length = sizeof(union acpi_object);
|
|
|
|
buf->pointer = *obj;
|
|
|
|
return AE_OK;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(__wrap_acpi_evaluate_object);
|
|
|
|
|
2017-06-05 16:40:46 +00:00
|
|
|
union acpi_object * __wrap_acpi_evaluate_dsm(acpi_handle handle, const guid_t *guid,
|
2016-12-05 21:43:25 +00:00
|
|
|
u64 rev, u64 func, union acpi_object *argv4)
|
|
|
|
{
|
|
|
|
union acpi_object *obj = ERR_PTR(-ENXIO);
|
|
|
|
struct iomap_ops *ops;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
ops = list_first_or_null_rcu(&iomap_head, typeof(*ops), list);
|
|
|
|
if (ops)
|
2017-06-05 16:40:46 +00:00
|
|
|
obj = ops->evaluate_dsm(handle, guid, rev, func, argv4);
|
2016-12-05 21:43:25 +00:00
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
if (IS_ERR(obj))
|
2017-06-05 16:40:46 +00:00
|
|
|
return acpi_evaluate_dsm(handle, guid, rev, func, argv4);
|
2016-12-05 21:43:25 +00:00
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(__wrap_acpi_evaluate_dsm);
|
|
|
|
|
2015-06-17 21:23:32 +00:00
|
|
|
MODULE_LICENSE("GPL v2");
|