• Chandan Babu R's avatar
    xfs: Fix false ENOSPC when performing direct write on a delalloc extent in cow fork · d6211330
    Chandan Babu R authored
    On a higly fragmented filesystem a Direct IO write can fail with -ENOSPC error
    even though the filesystem has sufficient number of free blocks.
    
    This occurs if the file offset range on which the write operation is being
    performed has a delalloc extent in the cow fork and this delalloc extent
    begins much before the Direct IO range.
    
    In such a scenario, xfs_reflink_allocate_cow() invokes xfs_bmapi_write() to
    allocate the blocks mapped by the delalloc extent. The extent thus allocated
    may not cover the beginning of file offset range on which the Direct IO write
    was issued. Hence xfs_reflink_allocate_cow() ends up returning -ENOSPC.
    
    The following script reliably recreates the bug described above.
    
      #!/usr/bin/bash
    
      device=/dev/loop0
      shortdev=$(basename $device)
    
      mntpnt=/mnt/
      file1=${mntpnt}/file1
      file2=${mntpnt}/file2
      fragmentedfile=${mntpnt}/fragmentedfile
      punchprog=/root/repos/xfstests-dev/src/punch-alternating
    
      errortag=/sys/fs/xfs/${shortdev}/errortag/bmap_alloc_minlen_extent
    
      umount $device > /dev/null 2>&1
    
      echo "Create FS"
      mkfs.xfs -f -m reflink=1 $device > /dev/null 2>&1
      if [[ $? != 0 ]]; then
      	echo "mkfs failed."
      	exit 1
      fi
    
      echo "Mount FS"
      mount $device $mntpnt > /dev/null 2>&1
      if [[ $? != 0 ]]; then
      	echo "mount failed."
      	exit 1
      fi
    
      echo "Create source file"
      xfs_io -f -c "pwrite 0 32M" $file1 > /dev/null 2>&1
    
      sync
    
      echo "Create Reflinked file"
      xfs_io -f -c "reflink $file1" $file2 &>/dev/null
    
      echo "Set cowextsize"
      xfs_io -c "cowextsize 16M" $file1 > /dev/null 2>&1
    
      echo "Fragment FS"
      xfs_io -f -c "pwrite 0 64M" $fragmentedfile > /dev/null 2>&1
      sync
      $punchprog $fragmentedfile
    
      echo "Allocate block sized extent from now onwards"
      echo -n 1 > $errortag
    
      echo "Create 16MiB delalloc extent in CoW fork"
      xfs_io -c "pwrite 0 4k" $file1 > /dev/null 2>&1
    
      sync
    
      echo "Direct I/O write at offset 12k"
      xfs_io -d -c "pwrite 12k 8k" $file1
    
    This commit fixes the bug by invoking xfs_bmapi_write() in a loop until disk
    blocks are allocated for atleast the starting file offset of the Direct IO
    write range.
    
    Fixes: 3c68d44a ("xfs: allocate direct I/O COW blocks in iomap_begin")
    Reported-and-Root-caused-by: default avatarWengang Wang <wen.gang.wang@oracle.com>
    Signed-off-by: default avatarChandan Babu R <chandan.babu@oracle.com>
    Reviewed-by: default avatarDarrick J. Wong <djwong@kernel.org>
    [djwong: slight editing to make the locking less grody, and fix some style things]
    Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
    d6211330
xfs_reflink.c 47.9 KB