• D
    xfs: only set IOMAP_F_SHARED when providing a srcmap to a write · 72a048c1
    Darrick J. Wong 提交于
    While prototyping a free space defragmentation tool, I observed an
    unexpected IO error while running a sequence of commands that can be
    recreated by the following sequence of commands:
    
    # xfs_io -f -c "pwrite -S 0x58 -b 10m 0 10m" file1
    # cp --reflink=always file1 file2
    # punch-alternating -o 1 file2
    # xfs_io -c "funshare 0 10m" file2
    fallocate: Input/output error
    
    I then scraped this (abbreviated) stack trace from dmesg:
    
    WARNING: CPU: 0 PID: 30788 at fs/iomap/buffered-io.c:577 iomap_write_begin+0x376/0x450
    CPU: 0 PID: 30788 Comm: xfs_io Not tainted 5.14.0-rc6-xfsx #rc6 5ef57b62a900814b3e4d885c755e9014541c8732
    Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.13.0-1ubuntu1.1 04/01/2014
    RIP: 0010:iomap_write_begin+0x376/0x450
    RSP: 0018:ffffc90000c0fc20 EFLAGS: 00010297
    RAX: 0000000000000001 RBX: ffffc90000c0fd10 RCX: 0000000000001000
    RDX: ffffc90000c0fc54 RSI: 000000000000000c RDI: 000000000000000c
    RBP: ffff888005d5dbd8 R08: 0000000000102000 R09: ffffc90000c0fc50
    R10: 0000000000b00000 R11: 0000000000101000 R12: ffffea0000336c40
    R13: 0000000000001000 R14: ffffc90000c0fd10 R15: 0000000000101000
    FS:  00007f4b8f62fe40(0000) GS:ffff88803ec00000(0000) knlGS:0000000000000000
    CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
    CR2: 000056361c554108 CR3: 000000000524e004 CR4: 00000000001706f0
    Call Trace:
     iomap_unshare_actor+0x95/0x140
     iomap_apply+0xfa/0x300
     iomap_file_unshare+0x44/0x60
     xfs_reflink_unshare+0x50/0x140 [xfs 61947ea9b3a73e79d747dbc1b90205e7987e4195]
     xfs_file_fallocate+0x27c/0x610 [xfs 61947ea9b3a73e79d747dbc1b90205e7987e4195]
     vfs_fallocate+0x133/0x330
     __x64_sys_fallocate+0x3e/0x70
     do_syscall_64+0x35/0x80
     entry_SYSCALL_64_after_hwframe+0x44/0xae
    RIP: 0033:0x7f4b8f79140a
    
    Looking at the iomap tracepoints, I saw this:
    
    iomap_iter:           dev 8:64 ino 0x100 pos 0 length 0 flags WRITE|0x80 (0x81) ops xfs_buffered_write_iomap_ops caller iomap_file_unshare
    iomap_iter_dstmap:    dev 8:64 ino 0x100 bdev 8:64 addr -1 offset 0 length 131072 type DELALLOC flags SHARED
    iomap_iter_srcmap:    dev 8:64 ino 0x100 bdev 8:64 addr 147456 offset 0 length 4096 type MAPPED flags
    iomap_iter:           dev 8:64 ino 0x100 pos 0 length 4096 flags WRITE|0x80 (0x81) ops xfs_buffered_write_iomap_ops caller iomap_file_unshare
    iomap_iter_dstmap:    dev 8:64 ino 0x100 bdev 8:64 addr -1 offset 4096 length 4096 type DELALLOC flags SHARED
    console:              WARNING: CPU: 0 PID: 30788 at fs/iomap/buffered-io.c:577 iomap_write_begin+0x376/0x450
    
    The first time funshare calls ->iomap_begin, xfs sees that the first
    block is shared and creates a 128k delalloc reservation in the COW fork.
    The delalloc reservation is returned as dstmap, and the shared block is
    returned as srcmap.  So far so good.
    
    funshare calls ->iomap_begin to try the second block.  This time there's
    no srcmap (punch-alternating punched it out!) but we still have the
    delalloc reservation in the COW fork.  Therefore, we again return the
    reservation as dstmap and the hole as srcmap.  iomap_unshare_iter
    incorrectly tries to unshare the hole, which __iomap_write_begin rejects
    because shared regions must be fully written and therefore cannot
    require zeroing.
    
    Therefore, change the buffered write iomap_begin function not to set
    IOMAP_F_SHARED when there isn't a source mapping to read from for the
    unsharing.
    Signed-off-by: NDarrick J. Wong <djwong@kernel.org>
    Reviewed-by: NChristoph Hellwig <hch@lst.de>
    Reviewed-by: NChandan Babu R <chandanrlinux@gmail.com>
    72a048c1
xfs_iomap.c 35.2 KB