@@ -1742,3 +1742,75 @@ vm_fault_t dax_finish_sync_fault(struct vm_fault *vmf,
return dax_insert_pfn_mkwrite(vmf, pfn, order);
}
EXPORT_SYMBOL_GPL(dax_finish_sync_fault);
+
+static loff_t
+dax_zeroinit_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
+ struct iomap *iomap, struct iomap *srcmap)
+{
+ sector_t sector = iomap_sector(iomap, pos);
+ int ret;
+
+ if (!iomap->bdev)
+ return -ECANCELED;
+
+ /* Must be able to zero storage directly without fs intervention. */
+ if (iomap->flags & IOMAP_F_SHARED)
+ return -ECANCELED;
+ if (srcmap != iomap)
+ return -ECANCELED;
+
+ switch (iomap->type) {
+ case IOMAP_MAPPED:
+ ret = blkdev_issue_zeroout(iomap->bdev, sector,
+ length >> SECTOR_SHIFT, GFP_KERNEL, 0);
+ if (ret)
+ return ret;
+ fallthrough;
+ case IOMAP_UNWRITTEN:
+ return length;
+ }
+
+ /* Reject holes, inline data, or delalloc extents. */
+ return -ECANCELED;
+}
+
+/*
+ * Initialize storage mapped to a DAX-mode file to a known value and ensure the
+ * media are ready to accept read and write commands. This requires the use of
+ * the block layer's zeroout function (with zero-page fallback enabled) to
+ * write zeroes to a pmem region and to reset any hardware media error state.
+ *
+ * The range arguments must be aligned to sector size. The file must be backed
+ * by a block device. The extents returned must not require copy on write (or
+ * any other mapping interventions from the filesystem) and must be contiguous.
+ * @done will be set to true if the reset succeeded.
+ */
+int
+dax_zeroinit_range(struct inode *inode, loff_t pos, loff_t len, bool *done,
+ const struct iomap_ops *ops)
+{
+ loff_t ret;
+
+ if (!IS_DAX(inode))
+ return -EINVAL;
+ if ((pos | len) & (SECTOR_SIZE - 1))
+ return -EINVAL;
+ if (pos + len > i_size_read(inode))
+ return -EINVAL;
+
+ while (len > 0) {
+ ret = iomap_apply(inode, pos, len, IOMAP_REPORT, ops, NULL,
+ dax_zeroinit_actor);
+ if (ret == -ECANCELED)
+ return 0;
+ if (ret < 0)
+ return ret;
+
+ pos += ret;
+ len -= ret;
+ }
+
+ *done = true;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dax_zeroinit_range);
@@ -956,6 +956,25 @@ xfs_file_fallocate(
goto out_unlock;
}
+ /*
+ * If the file is in DAX mode, try to use a DAX-specific function to
+ * zero the region. We can fall back to punch-and-realloc if necessary.
+ */
+ if ((mode & FALLOC_FL_ZERO_RANGE) && IS_DAX(inode)) {
+ bool did_zeroout = false;
+
+ trace_xfs_zero_file_space(ip);
+
+ error = dax_zeroinit_range(inode, offset, len, &did_zeroout,
+ &xfs_read_iomap_ops);
+ if (error == -EINVAL)
+ error = 0;
+ if (error)
+ goto out_unlock;
+ if (did_zeroout)
+ goto done;
+ }
+
if (mode & FALLOC_FL_PUNCH_HOLE) {
error = xfs_free_file_space(ip, offset, len);
if (error)
@@ -1059,6 +1078,7 @@ xfs_file_fallocate(
}
}
+done:
if (file->f_flags & O_DSYNC)
flags |= XFS_PREALLOC_SYNC;
@@ -152,6 +152,8 @@ struct page *dax_layout_busy_page(struct address_space *mapping);
struct page *dax_layout_busy_page_range(struct address_space *mapping, loff_t start, loff_t end);
dax_entry_t dax_lock_page(struct page *page);
void dax_unlock_page(struct page *page, dax_entry_t cookie);
+int dax_zeroinit_range(struct inode *inode, loff_t pos, loff_t len, bool *done,
+ const struct iomap_ops *ops);
#else
static inline bool bdev_dax_supported(struct block_device *bdev,
int blocksize)
@@ -201,6 +203,11 @@ static inline dax_entry_t dax_lock_page(struct page *page)
static inline void dax_unlock_page(struct page *page, dax_entry_t cookie)
{
}
+static inline int dax_zeroinit_range(struct inode *inode, loff_t pos, loff_t len,
+ bool *done, const struct iomap_ops *ops)
+{
+ return 0;
+}
#endif
#if IS_ENABLED(CONFIG_DAX)