• Qu Wenruo's avatar
    btrfs: qgroup: Fix qgroup reserved space underflow by only freeing reserved ranges · bc42bda2
    Qu Wenruo authored
    [BUG]
    For the following case, btrfs can underflow qgroup reserved space
    at an error path:
    (Page size 4K, function name without "btrfs_" prefix)
    
             Task A                  |             Task B
    ----------------------------------------------------------------------
    Buffered_write [0, 2K)           |
    |- check_data_free_space()       |
    |  |- qgroup_reserve_data()      |
    |     Range aligned to page      |
    |     range [0, 4K)          <<< |
    |     4K bytes reserved      <<< |
    |- copy pages to page cache      |
                                     | Buffered_write [2K, 4K)
                                     | |- check_data_free_space()
                                     | |  |- qgroup_reserved_data()
                                     | |     Range alinged to page
                                     | |     range [0, 4K)
                                     | |     Already reserved by A <<<
                                     | |     0 bytes reserved      <<<
                                     | |- delalloc_reserve_metadata()
                                     | |  And it *FAILED* (Maybe EQUOTA)
                                     | |- free_reserved_data_space()
                                          |- qgroup_free_data()
                                             Range aligned to page range
                                             [0, 4K)
                                             Freeing 4K
    (Special thanks to Chandan for the detailed report and analyse)
    
    [CAUSE]
    Above Task B is freeing reserved data range [0, 4K) which is actually
    reserved by Task A.
    
    And at writeback time, page dirty by Task A will go through writeback
    routine, which will free 4K reserved data space at file extent insert
    time, causing the qgroup underflow.
    
    [FIX]
    For btrfs_qgroup_free_data(), add @reserved parameter to only free
    data ranges reserved by previous btrfs_qgroup_reserve_data().
    So in above case, Task B will try to free 0 byte, so no underflow.
    Reported-by: default avatarChandan Rajendra <chandan@linux.vnet.ibm.com>
    Signed-off-by: default avatarQu Wenruo <quwenruo@cn.fujitsu.com>
    Reviewed-by: default avatarChandan Rajendra <chandan@linux.vnet.ibm.com>
    Tested-by: default avatarChandan Rajendra <chandan@linux.vnet.ibm.com>
    Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
    bc42bda2
inode.c 290 KB