From: Filipe Manana fdmanana@suse.com
[ Upstream commit f8da41de0bff9eb1d774a7253da0c9f637c4470a ]
If we fail to create an inline extent due to -ENOSPC, we will attempt to go through the normal COW path, reserve an extent, create an ordered extent, etc. However we were always freeing the reserved qgroup data, which is wrong since we will use data. Fix this by freeing the reserved qgroup data in __cow_file_range_inline() only if we are not doing the fallback (ret is <= 0).
Reviewed-by: Qu Wenruo wqu@suse.com Signed-off-by: Filipe Manana fdmanana@suse.com Reviewed-by: David Sterba dsterba@suse.com Signed-off-by: David Sterba dsterba@suse.com Signed-off-by: Sasha Levin sashal@kernel.org ---
LLM Generated explanations, may be completely bogus:
## Commit Analysis: btrfs qgroup data reservation fix
### 1. COMMIT MESSAGE ANALYSIS
The commit message clearly describes a **bug fix** for btrfs qgroup handling: - When inline extent creation fails due to -ENOSPC, btrfs falls back to the normal COW (copy-on-write) path - The bug: qgroup data reservation was being freed unconditionally before return - This is incorrect because in the fallback case (ret == 1), the data will still be used through the COW path - Fix: only free the reservation when NOT doing fallback (`ret <= 0`)
The commit has strong review credentials: - Reviewed-by: Qu Wenruo (btrfs developer) - Reviewed-by: David Sterba (btrfs maintainer) - Author: Filipe Manana (prolific btrfs developer)
### 2. CODE CHANGE ANALYSIS
The change is extremely minimal:
```c - btrfs_qgroup_free_data(inode, NULL, 0, fs_info->sectorsize, NULL); + if (ret <= 0) + btrfs_qgroup_free_data(inode, NULL, 0, fs_info->sectorsize, NULL); ```
**Technical mechanism of the bug:** 1. `__cow_file_range_inline()` attempts to create an inline extent 2. If this fails with -ENOSPC, `ret` is set to 1 (signaling fallback to normal COW) 3. Before the fix, `btrfs_qgroup_free_data()` was called unconditionally at the `out:` label 4. The COW path then executes expecting to use the reserved qgroup data 5. **Problem:** The reservation was prematurely freed, causing incorrect qgroup accounting
**The fix logic:** - `ret == 0`: Success → free the reservation (inline extent doesn't count as data extent) - `ret < 0`: Real error → free the reservation (operation failed) - `ret == 1`: Fallback to COW → **preserve** the reservation (data will be written)
### 3. CLASSIFICATION
- **Bug fix:** YES - fixes incorrect resource accounting - **Feature addition:** NO - **Security:** Not directly, but could affect quota enforcement integrity
### 4. SCOPE AND RISK ASSESSMENT
- **Lines changed:** ~3 lines effective change - **Files touched:** 1 (fs/btrfs/inode.c) - **Complexity:** Very low - simple conditional addition - **Risk:** LOW - surgical fix, doesn't change any logic paths, just prevents premature resource freeing
### 5. USER IMPACT
**Affected users:** - Anyone using btrfs with qgroups enabled - Qgroups are widely used for container quota management (e.g., Docker, LXC) and enterprise storage
**Consequences of the bug:** - Incorrect qgroup space accounting - Potential quota enforcement failures - Possible qgroup-related warnings or errors
**Trigger condition:** - Inline extent creation fails with ENOSPC and falls back to COW - Not uncommon when filesystems are near capacity
### 6. STABILITY INDICATORS
- Reviewed by two btrfs developers including the maintainer - Author (Filipe Manana) is highly experienced in btrfs - The fix is logically straightforward and obviously correct
### 7. DEPENDENCY CHECK
The fix is self-contained. Looking at the context: ```c } else if (ret == -ENOSPC) { ret = 1; goto out; } ```
The return value semantics (`ret == 1` for ENOSPC fallback) already exist in the code. This fix should apply cleanly to any stable tree containing this function.
## STABLE KERNEL CRITERIA EVALUATION
| Criterion | Assessment | |-----------|------------| | Obviously correct | ✅ Logic is clear and well-documented in comment | | Fixes real bug | ✅ Incorrect qgroup accounting | | Important issue | ✅ Affects quota management, data integrity | | Small and contained | ✅ 3-line change, single file | | No new features | ✅ Just a conditional guard | | Applies cleanly | ✅ Self-contained, no dependencies |
## CONCLUSION
This commit is an excellent candidate for stable backporting. It fixes a real bug in btrfs qgroup data reservation handling with a minimal, surgical change. The bug causes incorrect resource accounting when inline extent creation fails and falls back to COW - a scenario that can happen in normal operation when filesystems approach capacity. The fix is obviously correct, well-reviewed by btrfs maintainers, and has zero risk of regression.
**YES**
fs/btrfs/inode.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 51401d586a7b6..9e8be59ea3deb 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -670,8 +670,12 @@ static noinline int __cow_file_range_inline(struct btrfs_inode *inode, * it won't count as data extent, free them directly here. * And at reserve time, it's always aligned to page size, so * just free one page here. + * + * If we fallback to non-inline (ret == 1) due to -ENOSPC, then we need + * to keep the data reservation. */ - btrfs_qgroup_free_data(inode, NULL, 0, fs_info->sectorsize, NULL); + if (ret <= 0) + btrfs_qgroup_free_data(inode, NULL, 0, fs_info->sectorsize, NULL); btrfs_free_path(path); btrfs_end_transaction(trans); return ret;