mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-30 23:59:33 -04:00
Merge tag 'libnvdimm-for-7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm
Pull NVDIMM and DAX updates from Alison Schofield: "Most are DAX preparatory patches for FAMFS support, along with a few NVDIMM fixes and documentation cleanups. - Documentation cleanup, removing kernel-doc warnings - preparing DAX for FAMFS - misc NVDIMM fixups with cleanups for issues reported by Coccinelle" * tag 'libnvdimm-for-7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm: nvdimm-btt: clean up kernel-doc warnings libnvdimm: nd.h: clean up kernel-doc warnings dax: fsdev.c minor formatting cleanup dax: fix holder_ops race in fs_put_dax() dax: read holder_ops once in dax_holder_notify_failure() dax/fsdev: fail probe on invalid pgmap offset dax/fsdev: use __va(phys) for kaddr in direct_access dax/fsdev: clear pgmap ops and owner on unbind dax/fsdev: don't leave a dangling dev_dax->pgmap on probe failure dax/fsdev: clear vmemmap_shift when binding static pgmap dax/fsdev: fix multi-range offset in memory_failure handler dax: fix misleading comment about share/index union in dax_folio_reset_order() nvdimm/btt: reject an arena whose nfree is below the lane count libnvdimm/labels: Bound the on-media label size before the shift libnvdimm/labels: Prevent integer overflow in __nd_label_validate() nvdimm: ndtest: remove redundant NULL check before vfree() nvdimm: nfit: remove redundant NULL check before vfree()
This commit is contained in:
@@ -69,7 +69,6 @@ struct dev_dax_range {
|
||||
* data while the device is activated in the driver.
|
||||
* @region: parent region
|
||||
* @dax_dev: core dax functionality
|
||||
* @virt_addr: kva from memremap; used by fsdev_dax
|
||||
* @cached_size: size of daxdev cached by fsdev_dax
|
||||
* @align: alignment of this instance
|
||||
* @target_node: effective numa node if dev_dax memory range is onlined
|
||||
@@ -85,7 +84,6 @@ struct dev_dax_range {
|
||||
struct dev_dax {
|
||||
struct dax_region *region;
|
||||
struct dax_device *dax_dev;
|
||||
void *virt_addr;
|
||||
u64 cached_size;
|
||||
unsigned int align;
|
||||
int target_node;
|
||||
|
||||
@@ -45,15 +45,13 @@ static void fsdev_write_dax(void *addr, struct page *page,
|
||||
}
|
||||
|
||||
static long __fsdev_dax_direct_access(struct dax_device *dax_dev, pgoff_t pgoff,
|
||||
long nr_pages, enum dax_access_mode mode, void **kaddr,
|
||||
unsigned long *pfn)
|
||||
long nr_pages, enum dax_access_mode mode, void **kaddr,
|
||||
unsigned long *pfn)
|
||||
{
|
||||
struct dev_dax *dev_dax = dax_get_private(dax_dev);
|
||||
size_t size = nr_pages << PAGE_SHIFT;
|
||||
size_t offset = pgoff << PAGE_SHIFT;
|
||||
void *virt_addr = dev_dax->virt_addr + offset;
|
||||
phys_addr_t phys;
|
||||
unsigned long local_pfn;
|
||||
|
||||
phys = dax_pgoff_to_phys(dev_dax, pgoff, size);
|
||||
if (phys == -1) {
|
||||
@@ -63,11 +61,10 @@ static long __fsdev_dax_direct_access(struct dax_device *dax_dev, pgoff_t pgoff,
|
||||
}
|
||||
|
||||
if (kaddr)
|
||||
*kaddr = virt_addr;
|
||||
*kaddr = __va(phys);
|
||||
|
||||
local_pfn = PHYS_PFN(phys);
|
||||
if (pfn)
|
||||
*pfn = local_pfn;
|
||||
*pfn = PHYS_PFN(phys);
|
||||
|
||||
/*
|
||||
* Use cached_size which was computed at probe time. The size cannot
|
||||
@@ -83,7 +80,8 @@ static int fsdev_dax_zero_page_range(struct dax_device *dax_dev,
|
||||
long rc;
|
||||
|
||||
WARN_ONCE(nr_pages > 1, "%s: nr_pages > 1\n", __func__);
|
||||
rc = __fsdev_dax_direct_access(dax_dev, pgoff, 1, DAX_ACCESS, &kaddr, NULL);
|
||||
rc = __fsdev_dax_direct_access(dax_dev, pgoff, 1, DAX_ACCESS,
|
||||
&kaddr, NULL);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
fsdev_write_dax(kaddr, ZERO_PAGE(0), 0, PAGE_SIZE);
|
||||
@@ -91,15 +89,15 @@ static int fsdev_dax_zero_page_range(struct dax_device *dax_dev,
|
||||
}
|
||||
|
||||
static long fsdev_dax_direct_access(struct dax_device *dax_dev,
|
||||
pgoff_t pgoff, long nr_pages, enum dax_access_mode mode,
|
||||
void **kaddr, unsigned long *pfn)
|
||||
pgoff_t pgoff, long nr_pages, enum dax_access_mode mode,
|
||||
void **kaddr, unsigned long *pfn)
|
||||
{
|
||||
return __fsdev_dax_direct_access(dax_dev, pgoff, nr_pages, mode,
|
||||
kaddr, pfn);
|
||||
}
|
||||
|
||||
static size_t fsdev_dax_recovery_write(struct dax_device *dax_dev, pgoff_t pgoff,
|
||||
void *addr, size_t bytes, struct iov_iter *i)
|
||||
static size_t fsdev_dax_recovery_write(struct dax_device *dax_dev,
|
||||
pgoff_t pgoff, void *addr, size_t bytes, struct iov_iter *i)
|
||||
{
|
||||
return _copy_from_iter_flushcache(addr, bytes, i);
|
||||
}
|
||||
@@ -127,6 +125,23 @@ static void fsdev_clear_ops(void *data)
|
||||
dax_set_ops(dev_dax->dax_dev, NULL);
|
||||
}
|
||||
|
||||
static void fsdev_clear_pgmap_ops(void *data)
|
||||
{
|
||||
struct dev_pagemap *pgmap = data;
|
||||
|
||||
/*
|
||||
* fsdev installs pgmap->ops and ->owner at probe. For a static device
|
||||
* the pgmap is shared and long-lived (owned by the dax bus), so
|
||||
* leaving fsdev's ops behind on unbind would let a later
|
||||
* memory_failure -- after rebind to another driver, or after this
|
||||
* module is unloaded -- dispatch through a stale or freed
|
||||
* ->memory_failure handler. Clear them so the pgmap carries no fsdev
|
||||
* state once we are unbound.
|
||||
*/
|
||||
pgmap->ops = NULL;
|
||||
pgmap->owner = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Page map operations for FS-DAX mode
|
||||
* Similar to fsdax_pagemap_ops in drivers/nvdimm/pmem.c
|
||||
@@ -135,11 +150,26 @@ static void fsdev_clear_ops(void *data)
|
||||
* The core mm code in free_zone_device_folio() handles the wake_up_var()
|
||||
* directly for this memory type.
|
||||
*/
|
||||
static u64 fsdev_pfn_to_offset(struct dev_dax *dev_dax, unsigned long pfn)
|
||||
{
|
||||
phys_addr_t phys = PFN_PHYS(pfn);
|
||||
u64 offset = 0;
|
||||
|
||||
for (int i = 0; i < dev_dax->nr_range; i++) {
|
||||
struct range *range = &dev_dax->ranges[i].range;
|
||||
|
||||
if (phys >= range->start && phys <= range->end)
|
||||
return offset + (phys - range->start);
|
||||
offset += range_len(range);
|
||||
}
|
||||
return -1ULL;
|
||||
}
|
||||
|
||||
static int fsdev_pagemap_memory_failure(struct dev_pagemap *pgmap,
|
||||
unsigned long pfn, unsigned long nr_pages, int mf_flags)
|
||||
{
|
||||
struct dev_dax *dev_dax = pgmap->owner;
|
||||
u64 offset = PFN_PHYS(pfn) - dev_dax->ranges[0].range.start;
|
||||
u64 offset = fsdev_pfn_to_offset(dev_dax, pfn);
|
||||
u64 len = nr_pages << PAGE_SHIFT;
|
||||
|
||||
return dax_holder_notify_failure(dev_dax->dax_dev, offset,
|
||||
@@ -204,6 +234,48 @@ static const struct file_operations fsdev_fops = {
|
||||
.release = fsdev_release,
|
||||
};
|
||||
|
||||
/*
|
||||
* Acquire the dev_pagemap for probe: the static (pre-populated) one if
|
||||
* present, or a devm-allocated one for the dynamic case. Note that
|
||||
* dev_dax->pgmap is not set here; fsdev_dax_probe() sets it only once
|
||||
* probe succeeds, so a failed probe never leaves a dangling pointer
|
||||
* to a devres-freed pgmap.
|
||||
*/
|
||||
static struct dev_pagemap *fsdev_acquire_pgmap(struct dev_dax *dev_dax)
|
||||
{
|
||||
struct device *dev = &dev_dax->dev;
|
||||
struct dev_pagemap *pgmap;
|
||||
size_t pgmap_size;
|
||||
|
||||
if (static_dev_dax(dev_dax)) {
|
||||
if (dev_dax->nr_range > 1) {
|
||||
dev_warn(dev,
|
||||
"static pgmap / multi-range device conflict\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
pgmap = dev_dax->pgmap;
|
||||
pgmap->vmemmap_shift = 0;
|
||||
return pgmap;
|
||||
}
|
||||
|
||||
if (dev_dax->pgmap) {
|
||||
dev_warn(dev, "dynamic-dax with pre-populated page map\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
pgmap_size = struct_size(pgmap, ranges, dev_dax->nr_range - 1);
|
||||
pgmap = devm_kzalloc(dev, pgmap_size, GFP_KERNEL);
|
||||
if (!pgmap)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pgmap->nr_range = dev_dax->nr_range;
|
||||
for (int i = 0; i < dev_dax->nr_range; i++)
|
||||
pgmap->ranges[i] = dev_dax->ranges[i].range;
|
||||
|
||||
return pgmap;
|
||||
}
|
||||
|
||||
static int fsdev_dax_probe(struct dev_dax *dev_dax)
|
||||
{
|
||||
struct dax_device *dax_dev = dev_dax->dax_dev;
|
||||
@@ -215,35 +287,9 @@ static int fsdev_dax_probe(struct dev_dax *dev_dax)
|
||||
void *addr;
|
||||
int rc, i;
|
||||
|
||||
if (static_dev_dax(dev_dax)) {
|
||||
if (dev_dax->nr_range > 1) {
|
||||
dev_warn(dev, "static pgmap / multi-range device conflict\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pgmap = dev_dax->pgmap;
|
||||
} else {
|
||||
size_t pgmap_size;
|
||||
|
||||
if (dev_dax->pgmap) {
|
||||
dev_warn(dev, "dynamic-dax with pre-populated page map\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pgmap_size = struct_size(pgmap, ranges, dev_dax->nr_range - 1);
|
||||
pgmap = devm_kzalloc(dev, pgmap_size, GFP_KERNEL);
|
||||
if (!pgmap)
|
||||
return -ENOMEM;
|
||||
|
||||
pgmap->nr_range = dev_dax->nr_range;
|
||||
dev_dax->pgmap = pgmap;
|
||||
|
||||
for (i = 0; i < dev_dax->nr_range; i++) {
|
||||
struct range *range = &dev_dax->ranges[i].range;
|
||||
|
||||
pgmap->ranges[i] = *range;
|
||||
}
|
||||
}
|
||||
pgmap = fsdev_acquire_pgmap(dev_dax);
|
||||
if (IS_ERR(pgmap))
|
||||
return PTR_ERR(pgmap);
|
||||
|
||||
for (i = 0; i < dev_dax->nr_range; i++) {
|
||||
struct range *range = &dev_dax->ranges[i].range;
|
||||
@@ -275,6 +321,11 @@ static int fsdev_dax_probe(struct dev_dax *dev_dax)
|
||||
if (IS_ERR(addr))
|
||||
return PTR_ERR(addr);
|
||||
|
||||
/* Drop fsdev's pgmap->ops/owner on unbind so no stale ops survive. */
|
||||
rc = devm_add_action_or_reset(dev, fsdev_clear_pgmap_ops, pgmap);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* Clear any stale compound folio state left over from a previous
|
||||
* driver (e.g., device_dax with vmemmap_shift). Also register this
|
||||
@@ -290,15 +341,18 @@ static int fsdev_dax_probe(struct dev_dax *dev_dax)
|
||||
/* Detect whether the data is at a non-zero offset into the memory */
|
||||
if (pgmap->range.start != dev_dax->ranges[0].range.start) {
|
||||
u64 phys = dev_dax->ranges[0].range.start;
|
||||
u64 pgmap_phys = dev_dax->pgmap[0].range.start;
|
||||
u64 pgmap_phys = pgmap[0].range.start;
|
||||
|
||||
if (!WARN_ON(pgmap_phys > phys))
|
||||
data_offset = phys - pgmap_phys;
|
||||
if (pgmap_phys > phys) {
|
||||
dev_err(dev, "pgmap start %#llx exceeds data start %#llx\n",
|
||||
pgmap_phys, phys);
|
||||
return -EINVAL;
|
||||
}
|
||||
data_offset = phys - pgmap_phys;
|
||||
|
||||
pr_debug("%s: offset detected phys=%llx pgmap_phys=%llx offset=%llx\n",
|
||||
__func__, phys, pgmap_phys, data_offset);
|
||||
}
|
||||
dev_dax->virt_addr = addr + data_offset;
|
||||
|
||||
inode = dax_inode(dax_dev);
|
||||
cdev = inode->i_cdev;
|
||||
@@ -323,7 +377,13 @@ static int fsdev_dax_probe(struct dev_dax *dev_dax)
|
||||
return rc;
|
||||
|
||||
run_dax(dax_dev);
|
||||
return devm_add_action_or_reset(dev, fsdev_kill, dev_dax);
|
||||
rc = devm_add_action_or_reset(dev, fsdev_kill, dev_dax);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Probe can no longer fail; expose the pgmap via dev_dax */
|
||||
dev_dax->pgmap = pgmap;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dax_device_driver fsdev_dax_driver = {
|
||||
|
||||
@@ -116,11 +116,47 @@ EXPORT_SYMBOL_GPL(fs_dax_get_by_bdev);
|
||||
|
||||
#if IS_ENABLED(CONFIG_FS_DAX)
|
||||
|
||||
/**
|
||||
* fs_put_dax() - release holder ownership of a dax_device
|
||||
* @dax_dev: dax device to release (may be NULL)
|
||||
* @holder: the holder pointer previously passed to fs_dax_get() or
|
||||
* fs_dax_get_by_bdev(); must match exactly, as it is used
|
||||
* in a cmpxchg to atomically release ownership
|
||||
*
|
||||
* Must only be called by the current holder. Clears holder_ops before
|
||||
* holder_data to avoid a race where a concurrent fs_dax_get() could have
|
||||
* its newly installed holder_ops overwritten.
|
||||
*/
|
||||
void fs_put_dax(struct dax_device *dax_dev, void *holder)
|
||||
{
|
||||
if (dax_dev && holder &&
|
||||
cmpxchg(&dax_dev->holder_data, holder, NULL) == holder)
|
||||
dax_dev->holder_ops = NULL;
|
||||
if (dax_dev && holder) {
|
||||
void *prev;
|
||||
|
||||
/*
|
||||
* Clear holder_ops before releasing holder_data. A concurrent
|
||||
* dax_holder_notify_failure() that sees NULL ops returns
|
||||
* -EOPNOTSUPP cleanly. A concurrent fs_dax_get() that acquires
|
||||
* holder_data after the cmpxchg below is guaranteed to observe
|
||||
* holder_ops=NULL first (cmpxchg provides release ordering), so
|
||||
* its subsequent store of new ops will not be overwritten.
|
||||
*/
|
||||
WRITE_ONCE(dax_dev->holder_ops, NULL);
|
||||
prev = cmpxchg(&dax_dev->holder_data, holder, NULL);
|
||||
|
||||
/*
|
||||
* prev == holder: normal release.
|
||||
* prev == NULL: already released by kill_dax() when the
|
||||
* device was removed under a live holder;
|
||||
* not a bug.
|
||||
* prev != holder (non-NULL): fs_put_dax() called by something
|
||||
* that is not the current holder; an API
|
||||
* contract violation. A lock would be needed
|
||||
* to guard against this, but we WARN_ON()
|
||||
* instead since violating the contract is
|
||||
* a bug.
|
||||
*/
|
||||
WARN_ON(prev && prev != holder);
|
||||
}
|
||||
put_dax(dax_dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fs_put_dax);
|
||||
@@ -303,6 +339,7 @@ EXPORT_SYMBOL_GPL(dax_recovery_write);
|
||||
int dax_holder_notify_failure(struct dax_device *dax_dev, u64 off,
|
||||
u64 len, int mf_flags)
|
||||
{
|
||||
const struct dax_holder_operations *ops;
|
||||
int rc, id;
|
||||
|
||||
id = dax_read_lock();
|
||||
@@ -311,12 +348,19 @@ int dax_holder_notify_failure(struct dax_device *dax_dev, u64 off,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!dax_dev->holder_ops) {
|
||||
/*
|
||||
* Read holder_ops once: a concurrent fs_put_dax() can clear it without
|
||||
* synchronizing against readers. Without the single fetch the compiler
|
||||
* could reload between the NULL check and the call and dereference a
|
||||
* NULL ops.
|
||||
*/
|
||||
ops = READ_ONCE(dax_dev->holder_ops);
|
||||
if (!ops) {
|
||||
rc = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = dax_dev->holder_ops->notify_failure(dax_dev, off, len, mf_flags);
|
||||
rc = ops->notify_failure(dax_dev, off, len, mf_flags);
|
||||
out:
|
||||
dax_read_unlock(id);
|
||||
return rc;
|
||||
|
||||
@@ -883,6 +883,14 @@ static int discover_arenas(struct btt *btt)
|
||||
arena->external_lba_start = cur_nlba;
|
||||
parse_arena_meta(arena, super, cur_off);
|
||||
|
||||
if (arena->nfree < btt->nd_region->num_lanes) {
|
||||
dev_err(to_dev(arena),
|
||||
"nfree %u smaller than lane count %d\n",
|
||||
arena->nfree, btt->nd_region->num_lanes);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = log_set_indices(arena);
|
||||
if (ret) {
|
||||
dev_err(to_dev(arena),
|
||||
|
||||
@@ -210,7 +210,7 @@ struct badblocks;
|
||||
* @lbasize: LBA size as requested and presented to upper layers.
|
||||
* This is sector_size + size of any metadata.
|
||||
* @sector_size: The Linux sector size - 512 or 4096
|
||||
* @lanes: Per-lane spinlocks
|
||||
* @nd_region: &struct nd_region pointer
|
||||
* @init_lock: Mutex used for the BTT initialization
|
||||
* @init_state: Flag describing the initialization state for the BTT
|
||||
* @num_arenas: Number of arenas in the BTT instance
|
||||
|
||||
@@ -145,10 +145,21 @@ static int __nd_label_validate(struct nvdimm_drvdata *ndd)
|
||||
/* label sizes larger than 128 arrived with v1.2 */
|
||||
version = __le16_to_cpu(nsindex[i]->major) * 100
|
||||
+ __le16_to_cpu(nsindex[i]->minor);
|
||||
if (version >= 102)
|
||||
if (version >= 102) {
|
||||
/*
|
||||
* labelsize feeds the shift below; only 0 (128-byte)
|
||||
* and 1 (256-byte) are valid -- a larger value would
|
||||
* overflow or exceed the width of int.
|
||||
*/
|
||||
if (nsindex[i]->labelsize > 1) {
|
||||
dev_dbg(dev, "nsindex%d labelsize: %d invalid\n",
|
||||
i, nsindex[i]->labelsize);
|
||||
continue;
|
||||
}
|
||||
labelsize = 1 << (7 + nsindex[i]->labelsize);
|
||||
else
|
||||
} else {
|
||||
labelsize = 128;
|
||||
}
|
||||
|
||||
if (labelsize != sizeof_namespace_label(ndd)) {
|
||||
dev_dbg(dev, "nsindex%d labelsize %d invalid\n",
|
||||
@@ -202,7 +213,7 @@ static int __nd_label_validate(struct nvdimm_drvdata *ndd)
|
||||
}
|
||||
|
||||
nslot = __le32_to_cpu(nsindex[i]->nslot);
|
||||
if (nslot * sizeof_namespace_label(ndd)
|
||||
if ((u64)nslot * sizeof_namespace_label(ndd)
|
||||
+ 2 * sizeof_namespace_index(ndd)
|
||||
> ndd->nsarea.config_size) {
|
||||
dev_dbg(dev, "nsindex%d nslot: %u invalid, config_size: %#x\n",
|
||||
|
||||
12
fs/dax.c
12
fs/dax.c
@@ -392,12 +392,12 @@ int dax_folio_reset_order(struct folio *folio)
|
||||
int order = folio_order(folio);
|
||||
|
||||
/*
|
||||
* DAX maintains the invariant that folio->share != 0 only when
|
||||
* folio->mapping == NULL (enforced by dax_folio_make_shared()).
|
||||
* Equivalently: folio->mapping != NULL implies folio->share == 0.
|
||||
* Callers ensure share has been decremented to zero before
|
||||
* calling here, so unconditionally clearing both fields is
|
||||
* correct.
|
||||
* Clear the mapping and the index/share union word. folio->share
|
||||
* and folio->index occupy the same union in struct folio. For
|
||||
* non-shared folios (mapping != NULL), the union holds folio->index
|
||||
* (file page offset); for shared folios (mapping == NULL), it holds
|
||||
* folio->share (reference count). Either way, we are releasing the
|
||||
* folio and both fields should be zeroed.
|
||||
*/
|
||||
folio->mapping = NULL;
|
||||
folio->share = 0;
|
||||
|
||||
@@ -110,7 +110,7 @@ static inline struct nd_namespace_common *to_ndns(struct device *dev)
|
||||
|
||||
/**
|
||||
* struct nd_namespace_io - device representation of a persistent memory range
|
||||
* @dev: namespace device created by the nd region driver
|
||||
* @common: namespace device core infrastructure created by the nd region driver
|
||||
* @res: struct resource conversion of a NFIT SPA table
|
||||
* @size: cached resource_size(@res) for fast path size checks
|
||||
* @addr: virtual address to access the namespace range
|
||||
@@ -158,8 +158,11 @@ static inline struct nd_namespace_pmem *to_nd_namespace_pmem(const struct device
|
||||
* @offset: namespace-relative starting offset
|
||||
* @buf: buffer to fill
|
||||
* @size: transfer length
|
||||
* @flags: process (0) or atomic (1) context
|
||||
*
|
||||
* @buf is up-to-date upon return from this routine.
|
||||
*
|
||||
* Returns: %0 on success or a negative error code on failure
|
||||
*/
|
||||
static inline int nvdimm_read_bytes(struct nd_namespace_common *ndns,
|
||||
resource_size_t offset, void *buf, size_t size,
|
||||
@@ -174,11 +177,14 @@ static inline int nvdimm_read_bytes(struct nd_namespace_common *ndns,
|
||||
* @offset: namespace-relative starting offset
|
||||
* @buf: buffer to drain
|
||||
* @size: transfer length
|
||||
* @flags: process (0) or atomic (1) context
|
||||
*
|
||||
* NVDIMM Namepaces disks do not implement sectors internally. Depending on
|
||||
* the @ndns, the contents of @buf may be in cpu cache, platform buffers,
|
||||
* or on backing memory media upon return from this routine. Flushing
|
||||
* to media is handled internal to the @ndns driver, if at all.
|
||||
*
|
||||
* Returns: %0 on success or a negative error code on failure
|
||||
*/
|
||||
static inline int nvdimm_write_bytes(struct nd_namespace_common *ndns,
|
||||
resource_size_t offset, void *buf, size_t size,
|
||||
|
||||
@@ -376,8 +376,7 @@ static void *ndtest_alloc_resource(struct ndtest_priv *p, size_t size,
|
||||
buf_err:
|
||||
if (__dma && size >= DIMM_SIZE)
|
||||
gen_pool_free(ndtest_pool, __dma, size);
|
||||
if (buf)
|
||||
vfree(buf);
|
||||
vfree(buf);
|
||||
kfree(res);
|
||||
|
||||
return NULL;
|
||||
|
||||
@@ -1644,8 +1644,7 @@ static void *__test_alloc(struct nfit_test *t, size_t size, dma_addr_t *dma,
|
||||
err:
|
||||
if (*dma && size >= DIMM_SIZE)
|
||||
gen_pool_free(nfit_pool, *dma, size);
|
||||
if (buf)
|
||||
vfree(buf);
|
||||
vfree(buf);
|
||||
kfree(nfit_res);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user