On Tue, 12 Oct 2021, Christoph Hellwig wrote: > On Mon, Oct 04, 2021 at 09:01:33AM -0400, Mikulas Patocka wrote: > > Do you want this patch? > > Yes, this looks like what I want. Minor nitpicks below: > > > + .fallocate_flags = BLKDEV_FALLOC_FL_SUPPORTED, > > I'd probably call this fallocate_supported_flags. > > > + .fallocate_flags = FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE, > > Please avoid over 80 lines for a plain list of flags. OK. Here I'm sending a new version of the patch. BTW. for some filesystems (cifs, ext4, fuse, ...), the supported falloc flags vary dynamically - for example, ext4 can do COLLAPSE_RANGE and INSERT_RANGE operations only if the filesystem is not ext2 or ext3 and if the file is not encrypted. Should we add a new flag FALLOC_FL_RETURN_SUPORTED_FLAGS that will return the supported flags instead of using a static field in the file_operations structure? Mikulas From: Mikulas Patocka <mpatocka@xxxxxxxxxx> The loop driver checks for the fallocate method and if it is present, it assumes that the filesystem can do FALLOC_FL_ZERO_RANGE and FALLOC_FL_PUNCH_HOLE requests. However, some filesystems (such as fat, or tmpfs) have the fallocate method, but lack the capability to do FALLOC_FL_ZERO_RANGE and/or FALLOC_FL_PUNCH_HOLE. This results in syslog warnings "blk_update_request: operation not supported error, dev loop0, sector 0 op 0x9:(WRITE_ZEROES) flags 0x800800 phys_seg 0 prio class 0". The error can be reproduced with this command: "truncate -s 1GiB /tmp/file; losetup /dev/loop0 /tmp/file; blkdiscard -z /dev/loop0" This patch introduces a field "fallocate_supported_flags" in struct file_operations that specifies the flags that are supported by the fallocate methods. The loopback driver will check this field to determine if FALLOC_FL_PUNCH_HOLE or FALLOC_FL_ZERO_RANGE is supported Signed-off-by: Mikulas Patocka <mpatocka@xxxxxxxxxx> Index: linux-2.6/block/fops.c =================================================================== --- linux-2.6.orig/block/fops.c +++ linux-2.6/block/fops.c @@ -628,6 +628,7 @@ const struct file_operations def_blk_fop .splice_read = generic_file_splice_read, .splice_write = iter_file_splice_write, .fallocate = blkdev_fallocate, + .fallocate_supported_flags = BLKDEV_FALLOC_FL_SUPPORTED, }; static __init int blkdev_init(void) Index: linux-2.6/fs/btrfs/file.c =================================================================== --- linux-2.6.orig/fs/btrfs/file.c +++ linux-2.6/fs/btrfs/file.c @@ -3688,6 +3688,8 @@ const struct file_operations btrfs_file_ .release = btrfs_release_file, .fsync = btrfs_sync_file, .fallocate = btrfs_fallocate, + .fallocate_supported_flags = FALLOC_FL_KEEP_SIZE | + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE, .unlocked_ioctl = btrfs_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = btrfs_compat_ioctl, Index: linux-2.6/fs/ceph/file.c =================================================================== --- linux-2.6.orig/fs/ceph/file.c +++ linux-2.6/fs/ceph/file.c @@ -2492,5 +2492,6 @@ const struct file_operations ceph_file_f .unlocked_ioctl = ceph_ioctl, .compat_ioctl = compat_ptr_ioctl, .fallocate = ceph_fallocate, + .fallocate_supported_flags = FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, .copy_file_range = ceph_copy_file_range, }; Index: linux-2.6/fs/cifs/cifsfs.c =================================================================== --- linux-2.6.orig/fs/cifs/cifsfs.c +++ linux-2.6/fs/cifs/cifsfs.c @@ -1281,6 +1281,9 @@ const struct file_operations cifs_file_o .remap_file_range = cifs_remap_file_range, .setlease = cifs_setlease, .fallocate = cifs_fallocate, + .fallocate_supported_flags = FALLOC_FL_PUNCH_HOLE | + FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE | + FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_INSERT_RANGE, }; const struct file_operations cifs_file_strict_ops = { @@ -1301,6 +1304,9 @@ const struct file_operations cifs_file_s .remap_file_range = cifs_remap_file_range, .setlease = cifs_setlease, .fallocate = cifs_fallocate, + .fallocate_supported_flags = FALLOC_FL_PUNCH_HOLE | + FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE | + FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_INSERT_RANGE, }; const struct file_operations cifs_file_direct_ops = { @@ -1321,6 +1327,9 @@ const struct file_operations cifs_file_d .llseek = cifs_llseek, .setlease = cifs_setlease, .fallocate = cifs_fallocate, + .fallocate_supported_flags = FALLOC_FL_PUNCH_HOLE | + FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE | + FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_INSERT_RANGE, }; const struct file_operations cifs_file_nobrl_ops = { @@ -1339,6 +1348,9 @@ const struct file_operations cifs_file_n .remap_file_range = cifs_remap_file_range, .setlease = cifs_setlease, .fallocate = cifs_fallocate, + .fallocate_supported_flags = FALLOC_FL_PUNCH_HOLE | + FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE | + FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_INSERT_RANGE, }; const struct file_operations cifs_file_strict_nobrl_ops = { @@ -1357,6 +1369,9 @@ const struct file_operations cifs_file_s .remap_file_range = cifs_remap_file_range, .setlease = cifs_setlease, .fallocate = cifs_fallocate, + .fallocate_supported_flags = FALLOC_FL_PUNCH_HOLE | + FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE | + FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_INSERT_RANGE, }; const struct file_operations cifs_file_direct_nobrl_ops = { @@ -1375,6 +1390,9 @@ const struct file_operations cifs_file_d .llseek = cifs_llseek, .setlease = cifs_setlease, .fallocate = cifs_fallocate, + .fallocate_supported_flags = FALLOC_FL_PUNCH_HOLE | + FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE | + FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_INSERT_RANGE, }; const struct file_operations cifs_dir_ops = { Index: linux-2.6/fs/ext4/file.c =================================================================== --- linux-2.6.orig/fs/ext4/file.c +++ linux-2.6/fs/ext4/file.c @@ -929,6 +929,9 @@ const struct file_operations ext4_file_o .splice_read = generic_file_splice_read, .splice_write = iter_file_splice_write, .fallocate = ext4_fallocate, + .fallocate_supported_flags = FALLOC_FL_KEEP_SIZE | + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_COLLAPSE_RANGE | + FALLOC_FL_ZERO_RANGE | FALLOC_FL_INSERT_RANGE, }; const struct inode_operations ext4_file_inode_operations = { Index: linux-2.6/fs/f2fs/file.c =================================================================== --- linux-2.6.orig/fs/f2fs/file.c +++ linux-2.6/fs/f2fs/file.c @@ -4499,6 +4499,9 @@ const struct file_operations f2fs_file_o .flush = f2fs_file_flush, .fsync = f2fs_sync_file, .fallocate = f2fs_fallocate, + .fallocate_supported_flags = FALLOC_FL_KEEP_SIZE | + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_COLLAPSE_RANGE | + FALLOC_FL_ZERO_RANGE | FALLOC_FL_INSERT_RANGE, .unlocked_ioctl = f2fs_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = f2fs_compat_ioctl, Index: linux-2.6/fs/fat/file.c =================================================================== --- linux-2.6.orig/fs/fat/file.c +++ linux-2.6/fs/fat/file.c @@ -211,6 +211,7 @@ const struct file_operations fat_file_op .splice_read = generic_file_splice_read, .splice_write = iter_file_splice_write, .fallocate = fat_fallocate, + .fallocate_supported_flags = FALLOC_FL_KEEP_SIZE, }; static int fat_cont_expand(struct inode *inode, loff_t size) Index: linux-2.6/fs/fuse/file.c =================================================================== --- linux-2.6.orig/fs/fuse/file.c +++ linux-2.6/fs/fuse/file.c @@ -3147,6 +3147,8 @@ static const struct file_operations fuse .compat_ioctl = fuse_file_compat_ioctl, .poll = fuse_file_poll, .fallocate = fuse_file_fallocate, + .fallocate_supported_flags = FALLOC_FL_KEEP_SIZE | + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE, .copy_file_range = fuse_copy_file_range, }; Index: linux-2.6/fs/gfs2/file.c =================================================================== --- linux-2.6.orig/fs/gfs2/file.c +++ linux-2.6/fs/gfs2/file.c @@ -1366,6 +1366,7 @@ const struct file_operations gfs2_file_f .splice_write = gfs2_file_splice_write, .setlease = simple_nosetlease, .fallocate = gfs2_fallocate, + .fallocate_supported_flags = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, }; const struct file_operations gfs2_dir_fops = { Index: linux-2.6/fs/hugetlbfs/inode.c =================================================================== --- linux-2.6.orig/fs/hugetlbfs/inode.c +++ linux-2.6/fs/hugetlbfs/inode.c @@ -1163,6 +1163,7 @@ const struct file_operations hugetlbfs_f .get_unmapped_area = hugetlb_get_unmapped_area, .llseek = default_llseek, .fallocate = hugetlbfs_fallocate, + .fallocate_supported_flags = FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, }; static const struct inode_operations hugetlbfs_dir_inode_operations = { Index: linux-2.6/fs/nfs/nfs4file.c =================================================================== --- linux-2.6.orig/fs/nfs/nfs4file.c +++ linux-2.6/fs/nfs/nfs4file.c @@ -457,6 +457,7 @@ const struct file_operations nfs4_file_o .copy_file_range = nfs4_copy_file_range, .llseek = nfs4_file_llseek, .fallocate = nfs42_fallocate, + .fallocate_supported_flags = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, .remap_file_range = nfs42_remap_file_range, #else .llseek = nfs_file_llseek, Index: linux-2.6/fs/ntfs3/file.c =================================================================== --- linux-2.6.orig/fs/ntfs3/file.c +++ linux-2.6/fs/ntfs3/file.c @@ -1246,6 +1246,8 @@ const struct file_operations ntfs_file_o .fsync = generic_file_fsync, .splice_write = iter_file_splice_write, .fallocate = ntfs_fallocate, + .fallocate_supported_flags = FALLOC_FL_KEEP_SIZE | + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_COLLAPSE_RANGE, .release = ntfs_file_release, }; // clang-format on Index: linux-2.6/fs/ocfs2/file.c =================================================================== --- linux-2.6.orig/fs/ocfs2/file.c +++ linux-2.6/fs/ocfs2/file.c @@ -2746,6 +2746,7 @@ const struct file_operations ocfs2_fops .splice_read = generic_file_splice_read, .splice_write = iter_file_splice_write, .fallocate = ocfs2_fallocate, + .fallocate_supported_flags = FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, .remap_file_range = ocfs2_remap_file_range, }; @@ -2792,6 +2793,7 @@ const struct file_operations ocfs2_fops_ .splice_read = generic_file_splice_read, .splice_write = iter_file_splice_write, .fallocate = ocfs2_fallocate, + .fallocate_supported_flags = FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, .remap_file_range = ocfs2_remap_file_range, }; Index: linux-2.6/fs/overlayfs/file.c =================================================================== --- linux-2.6.orig/fs/overlayfs/file.c +++ linux-2.6/fs/overlayfs/file.c @@ -658,6 +658,7 @@ const struct file_operations ovl_file_op .fsync = ovl_fsync, .mmap = ovl_mmap, .fallocate = ovl_fallocate, + .fallocate_supported_flags = FALLOC_FL_SUPPORTED_MASK, .fadvise = ovl_fadvise, .flush = ovl_flush, .splice_read = generic_file_splice_read, Index: linux-2.6/fs/xfs/xfs_file.c =================================================================== --- linux-2.6.orig/fs/xfs/xfs_file.c +++ linux-2.6/fs/xfs/xfs_file.c @@ -1464,6 +1464,7 @@ const struct file_operations xfs_file_op .fsync = xfs_file_fsync, .get_unmapped_area = thp_get_unmapped_area, .fallocate = xfs_file_fallocate, + .fallocate_supported_flags = XFS_FALLOC_FL_SUPPORTED, .fadvise = xfs_file_fadvise, .remap_file_range = xfs_file_remap_range, }; Index: linux-2.6/include/linux/fs.h =================================================================== --- linux-2.6.orig/include/linux/fs.h +++ linux-2.6/include/linux/fs.h @@ -2098,6 +2098,7 @@ struct file_operations { int (*setlease)(struct file *, long, struct file_lock **, void **); long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len); + unsigned fallocate_supported_flags; void (*show_fdinfo)(struct seq_file *m, struct file *f); #ifndef CONFIG_MMU unsigned (*mmap_capabilities)(struct file *); Index: linux-2.6/ipc/shm.c =================================================================== --- linux-2.6.orig/ipc/shm.c +++ linux-2.6/ipc/shm.c @@ -44,6 +44,7 @@ #include <linux/mount.h> #include <linux/ipc_namespace.h> #include <linux/rhashtable.h> +#include <linux/falloc.h> #include <linux/uaccess.h> @@ -558,6 +559,7 @@ static const struct file_operations shm_ .get_unmapped_area = shm_get_unmapped_area, .llseek = noop_llseek, .fallocate = shm_fallocate, + .fallocate_supported_flags = FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, }; /* @@ -571,6 +573,7 @@ static const struct file_operations shm_ .get_unmapped_area = shm_get_unmapped_area, .llseek = noop_llseek, .fallocate = shm_fallocate, + .fallocate_supported_flags = FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, }; bool is_file_shm_hugepages(struct file *file) Index: linux-2.6/mm/shmem.c =================================================================== --- linux-2.6.orig/mm/shmem.c +++ linux-2.6/mm/shmem.c @@ -3797,6 +3797,7 @@ static const struct file_operations shme .splice_read = generic_file_splice_read, .splice_write = iter_file_splice_write, .fallocate = shmem_fallocate, + .fallocate_supported_flags = FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, #endif }; Index: linux-2.6/drivers/block/loop.c =================================================================== --- linux-2.6.orig/drivers/block/loop.c +++ linux-2.6/drivers/block/loop.c @@ -947,7 +947,10 @@ static void loop_config_discard(struct l * encryption is enabled, because it may give an attacker * useful information. */ - } else if (!file->f_op->fallocate || lo->lo_encrypt_key_size) { + } else if ((file->f_op->fallocate_supported_flags & + (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE)) != + (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE) || + lo->lo_encrypt_key_size) { max_discard_sectors = 0; granularity = 0; @@ -959,7 +962,10 @@ static void loop_config_discard(struct l if (max_discard_sectors) { q->limits.discard_granularity = granularity; blk_queue_max_discard_sectors(q, max_discard_sectors); - blk_queue_max_write_zeroes_sectors(q, max_discard_sectors); + if (file->f_op->fallocate_supported_flags & FALLOC_FL_ZERO_RANGE) + blk_queue_max_write_zeroes_sectors(q, max_discard_sectors); + else + blk_queue_max_write_zeroes_sectors(q, 0); blk_queue_flag_set(QUEUE_FLAG_DISCARD, q); } else { q->limits.discard_granularity = 0;