• Brian Foster's avatar
    xfs: refactor xfs_reserve_blocks() to handle ENOSPC correctly · 408fd484
    Brian Foster authored
    xfs_reserve_blocks() is responsible to update the XFS reserved block
    pool count at mount time or based on user request. When the caller
    requests to increase the reserve pool, blocks must be allocated from
    the global counters such that they are no longer available for
    general purpose use. If the requested reserve pool size is too
    large, XFS reserves what blocks are available. The implementation
    requires looking at the percpu counters and making an educated guess
    as to how many blocks to try and allocate from xfs_mod_fdblocks(),
    which can return -ENOSPC if the guess was not accurate due to
    counters being modified in parallel.
    
    xfs_reserve_blocks() retries the guess in this scenario until the
    allocation succeeds or it is determined that there is no space
    available in the fs. While not easily reproducible in the current
    form, the retry code doesn't actually work correctly if
    xfs_mod_fdblocks() actually fails. The problem is that the percpu
    calculations use the m_resblks counter to determine how many blocks
    to allocate, but unconditionally update m_resblks before the block
    allocation has actually succeeded.  Therefore, if xfs_mod_fdblocks()
    fails, the code jumps to the retry label and uses the already
    updated m_resblks value to determine how many blocks to try and
    allocate. If the percpu counters previously suggested that the
    entire request was available, fdblocks_delta could end up set to 0.
    In that case, m_resblks is updated to the requested value, yet no
    blocks have been reserved at all.
    
    Refactor xfs_reserve_blocks() to use an explicit loop and make the
    code easier to follow. Since we have to drop the spinlock across the
    xfs_mod_fdblocks() call, use a delta value for m_resblks as well and
    only apply the delta once allocation succeeds.
    
    [dchinner: convert to do {} while() loop]
    Signed-off-by: default avatarBrian Foster <bfoster@redhat.com>
    Reviewed-by: default avatarDave Chinner <dchinner@redhat.com>
    Signed-off-by: default avatarDave Chinner <david@fromorbit.com>
    
    408fd484
xfs_fsops.c 22.6 KB