Adding fallocate support to device-dax. This implements FALLOC_FL_PUNCH_HOLE in order to allow clearing of badblocks/poison list.
Signed-off-by: Dave Jiang <dave.ji...@intel.com> --- drivers/dax/dax-private.h | 1 + drivers/dax/dax.c | 24 +++++++++++++++- drivers/dax/dax.h | 4 ++- drivers/dax/pmem.c | 68 ++++++++++++++++++++++++++++++++++++++++++++- fs/open.c | 2 + 5 files changed, 95 insertions(+), 4 deletions(-) diff --git a/drivers/dax/dax-private.h b/drivers/dax/dax-private.h index a119ef9..aea927d 100644 --- a/drivers/dax/dax-private.h +++ b/drivers/dax/dax-private.h @@ -38,6 +38,7 @@ struct dax_dev { int num_resources; struct resource res[0]; void *private; + const struct file_operations *fops; }; #endif diff --git a/drivers/dax/dax.c b/drivers/dax/dax.c index f1857e6..b5cd73e 100644 --- a/drivers/dax/dax.c +++ b/drivers/dax/dax.c @@ -21,6 +21,7 @@ #include <linux/dax.h> #include <linux/fs.h> #include <linux/mm.h> +#include <linux/falloc.h> #include "dax.h" #include "dax-private.h" @@ -314,6 +315,12 @@ void *dax_dev_get_private(struct device *dev) } EXPORT_SYMBOL_GPL(dax_dev_get_private); +struct device *dax_dev_get_device(struct dax_dev *dax_dev) +{ + return &dax_dev->dev; +} +EXPORT_SYMBOL_GPL(dax_dev_get_device); + void dax_dev_set_private(struct dax_dev *dax_dev, void *priv) { dax_dev->private = priv; @@ -606,6 +613,17 @@ static int dax_release(struct inode *inode, struct file *filp) return 0; } +static long dax_fallocate(struct file *file, int mode, + loff_t offset, loff_t len) +{ + struct dax_dev *dax_dev = file->private_data; + + if (dax_dev->fops) + return dax_dev->fops->fallocate(file, mode, offset, len); + + return -EOPNOTSUPP; +} + static const struct file_operations dax_fops = { .llseek = noop_llseek, .owner = THIS_MODULE, @@ -613,6 +631,7 @@ static const struct file_operations dax_fops = { .release = dax_release, .get_unmapped_area = dax_get_unmapped_area, .mmap = dax_mmap, + .fallocate = dax_fallocate, }; static void dax_dev_release(struct device *dev) @@ -650,7 +669,8 @@ static void unregister_dax_dev(void *dev) struct dax_dev *devm_create_dax_dev(struct dax_region *dax_region, struct resource *res, int count, - const struct attribute_group **groups) + const struct attribute_group **groups, + const struct file_operations *fops) { struct device *parent = dax_region->dev; struct dax_dev *dax_dev; @@ -730,6 +750,8 @@ struct dax_dev *devm_create_dax_dev(struct dax_region *dax_region, if (rc) return ERR_PTR(rc); + dax_dev->fops = fops; + return dax_dev; err_cdev: diff --git a/drivers/dax/dax.h b/drivers/dax/dax.h index c23c7ac..6a31a74 100644 --- a/drivers/dax/dax.h +++ b/drivers/dax/dax.h @@ -24,8 +24,10 @@ struct dax_region *alloc_dax_region(struct device *parent, void *addr, unsigned long flags); struct dax_dev *devm_create_dax_dev(struct dax_region *dax_region, struct resource *res, int count, - const struct attribute_group **groups); + const struct attribute_group **groups, + const struct file_operations *fops); void *dax_dev_get_private(struct device *dev); void dax_dev_set_private(struct dax_dev *dax_dev, void *priv); +struct device *dax_dev_get_device(struct dax_dev *dax_dev); #endif /* __DAX_H__ */ diff --git a/drivers/dax/pmem.c b/drivers/dax/pmem.c index 5ffb2e9..5ab4900 100644 --- a/drivers/dax/pmem.c +++ b/drivers/dax/pmem.c @@ -15,6 +15,7 @@ #include <linux/module.h> #include <linux/pfn_t.h> #include <linux/badblocks.h> +#include <linux/falloc.h> #include "../nvdimm/pfn.h" #include "../nvdimm/nd.h" #include "dax.h" @@ -24,6 +25,69 @@ struct dax_pmem { struct percpu_ref ref; struct completion cmp; struct badblocks bb; + resource_size_t start; + resource_size_t end; +}; + +static void dax_pmem_clear_poison(struct dax_pmem *dax_pmem, + loff_t offset, loff_t len) +{ + sector_t sector; + long cleared; + + sector = offset >> 9; + cleared = nvdimm_clear_poison(dax_pmem->dev, + dax_pmem->start + offset, len); + + if ((cleared > 0) && (cleared >> 9)) { + dev_dbg(dax_pmem->dev, "%s: %#llx clear %ld sector%s\n", + __func__, (unsigned long long)sector, + cleared >> 9, cleared >> 9 > 1 ? "s" : ""); + badblocks_clear(&dax_pmem->bb, sector, cleared >> 9); + } +} + +static long dax_pmem_fallocate(struct file *file, int mode, + loff_t start, loff_t len) +{ + struct dax_dev *dax_dev = file->private_data; + struct device *dev; + struct dax_pmem *dax_pmem; + loff_t end = start + len - 1; + loff_t res_size; + + dev = dax_dev_get_device(dax_dev); + dax_pmem = dax_dev_get_private(dev); + res_size = dax_pmem->end - dax_pmem->start + 1; + + dev_dbg(dev, "fallocate mode: %#x, start: %#llx, len: %llu\n", + mode, start, len); + dev_dbg(dev, "res start: %#llx end: %#llx size: %llu\n", + dax_pmem->start, dax_pmem->end, res_size); + + /* check size boundares */ + if (start >= dax_pmem->end) + return -EINVAL; + if (end >= res_size) { + if (mode & FALLOC_FL_KEEP_SIZE) + len = res_size - start; + else + return -EINVAL; + } + + switch (mode) { + case FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE: + dax_pmem_clear_poison(dax_pmem, start, len); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static const struct file_operations pmem_fops = { + .fallocate = dax_pmem_fallocate, }; static ssize_t dax_pmem_badblocks_show(struct device *dev, @@ -158,8 +222,10 @@ static int dax_pmem_probe(struct device *dev) /* TODO: support for subdividing a dax region... */ dax_dev = devm_create_dax_dev(dax_region, &res, 1, - dax_pmem_attribute_groups); + dax_pmem_attribute_groups, &pmem_fops); dax_dev_set_private(dax_dev, dax_pmem); + dax_pmem->start = res.start; + dax_pmem->end = res.end; rc = devm_init_badblocks(dev, &dax_pmem->bb); if (rc) diff --git a/fs/open.c b/fs/open.c index d3ed817..15325fd 100644 --- a/fs/open.c +++ b/fs/open.c @@ -306,7 +306,7 @@ int vfs_fallocate(struct file *file, int mode, loff_t offset, loff_t len) * for directories or not. */ if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode) && - !S_ISBLK(inode->i_mode)) + !S_ISBLK(inode->i_mode) && !S_ISCHR(inode->i_mode)) return -ENODEV; /* Check for wrap through zero too */ _______________________________________________ Linux-nvdimm mailing list Linux-nvdimm@lists.01.org https://lists.01.org/mailman/listinfo/linux-nvdimm