summaryrefslogtreecommitdiff
path: root/fs/xfs/scrub/xfile.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/xfs/scrub/xfile.c')
-rw-r--r--fs/xfs/scrub/xfile.c83
1 files changed, 83 insertions, 0 deletions
diff --git a/fs/xfs/scrub/xfile.c b/fs/xfs/scrub/xfile.c
index 8abf72eea917..d6a0c2c0f174 100644
--- a/fs/xfs/scrub/xfile.c
+++ b/fs/xfs/scrub/xfile.c
@@ -285,6 +285,89 @@ xfile_pwrite(
return error;
}
+/* Discard pages backing a range of the xfile. */
+void
+xfile_discard(
+ struct xfile *xf,
+ loff_t pos,
+ u64 count)
+{
+ trace_xfile_discard(xf, pos, count);
+ shmem_truncate_range(file_inode(xf->file), pos, pos + count - 1);
+}
+
+/* Ensure that there is storage backing the given range. */
+int
+xfile_prealloc(
+ struct xfile *xf,
+ loff_t pos,
+ u64 count)
+{
+ struct inode *inode = file_inode(xf->file);
+ struct address_space *mapping = inode->i_mapping;
+ const struct address_space_operations *aops = mapping->a_ops;
+ struct page *page = NULL;
+ unsigned int pflags;
+ int error = 0;
+
+ if (count > MAX_RW_COUNT)
+ return -E2BIG;
+ if (inode->i_sb->s_maxbytes - pos < count)
+ return -EFBIG;
+
+ trace_xfile_prealloc(xf, pos, count);
+
+ pflags = memalloc_nofs_save();
+ while (count > 0) {
+ void *fsdata = NULL;
+ unsigned int len;
+ int ret;
+
+ len = min_t(ssize_t, count, PAGE_SIZE - offset_in_page(pos));
+
+ /*
+ * We call write_begin directly here to avoid all the freezer
+ * protection lock-taking that happens in the normal path.
+ * shmem doesn't support fs freeze, but lockdep doesn't know
+ * that and will trip over that.
+ */
+ error = aops->write_begin(NULL, mapping, pos, len, &page,
+ &fsdata);
+ if (error)
+ break;
+
+ /*
+ * xfile pages must never be mapped into userspace, so we skip
+ * the dcache flush. If the page is not uptodate, zero it to
+ * ensure we never go lacking for space here.
+ */
+ if (!PageUptodate(page)) {
+ void *kaddr = kmap_local_page(page);
+
+ memset(kaddr, 0, PAGE_SIZE);
+ SetPageUptodate(page);
+ kunmap_local(kaddr);
+ }
+
+ ret = aops->write_end(NULL, mapping, pos, len, len, page,
+ fsdata);
+ if (ret < 0) {
+ error = ret;
+ break;
+ }
+ if (ret != len) {
+ error = -EIO;
+ break;
+ }
+
+ count -= len;
+ pos += len;
+ }
+ memalloc_nofs_restore(pflags);
+
+ return error;
+}
+
/* Find the next written area in the xfile data for a given offset. */
loff_t
xfile_seek_data(