There's two logical holes in how we deal with delalloc reservations right now
[ ] Csum reservations are calculated assuming contiguous allocations. This was done partly because assuming 1 leaf change per 1 csum would be drastically over taxing on the ENOSPC infrastructure, and partly because generally speaking we're going to allocate contiguously, even if logically the file is fragmented. This obviously isn't true, but is close enough to the real world that it works, until it doesn't obviously. We have no idea how fragmented the file system will be at page dirtying time, only at cow_file_range time. A nice compromise would be to do like we do for ->outstanding_extents, which is anything logically separated gets counted as it's own leaf. Then we do the normal csum calculation based on the size of the DELALLOC extent in the io_tree. This at least will then scale up as we write, because contiguous areas will be split as necessary due to the fragmentation on disk.
[ ] The other problem is what do we do when we have a growing block rsv for a file that's no longer being written to. Consider a 256mib contiguous extent, this takes up 1 item for the file extent, 1 for the inode, and then X for the csums. However if we have 1mib chunks free on disk, we're going to end up with 256 file extents, and X + 256 csums (ish) usage for our metadata. But we have no mechanism to refill at this point. We will simply see our ->reserved drop and our ->size increase, but with no more writes we will not refill our reservation. This problem has existed since the dawn of time for us, which is partly why the global_rsv exists, to save us from these situations.
For now using the global_rsv to save us from both scenarios is valid and ok, but in the future we're going to want to rework this so it's a little less dependent on the global_rsv. The csum calculation should be straightforward, but actually makes the second problem worse because we're growing ->size even more in the face of fragmentation.
One thought I have is bringing back the delalloc_block_rsv and using that as the global pile for all delalloc reservations. Then at least we can refill it the same way we refill the global rsv and the delayed refs rsv. The only hole we'll have at that point is the user writing and then doing literally nothing else to the file system, but again this is what the global_rsv is for.
There's two logical holes in how we deal with delalloc reservations right now
->outstanding_extents
, which is anything logically separated gets counted as it's own leaf. Then we do the normal csum calculation based on the size of the DELALLOC extent in the io_tree. This at least will then scale up as we write, because contiguous areas will be split as necessary due to the fragmentation on disk.->reserved
drop and our->size
increase, but with no more writes we will not refill our reservation. This problem has existed since the dawn of time for us, which is partly why theglobal_rsv
exists, to save us from these situations.For now using the
global_rsv
to save us from both scenarios is valid and ok, but in the future we're going to want to rework this so it's a little less dependent on theglobal_rsv
. The csum calculation should be straightforward, but actually makes the second problem worse because we're growing->size
even more in the face of fragmentation.One thought I have is bringing back the
delalloc_block_rsv
and using that as the global pile for all delalloc reservations. Then at least we can refill it the same way we refill the global rsv and the delayed refs rsv. The only hole we'll have at that point is the user writing and then doing literally nothing else to the file system, but again this is what theglobal_rsv
is for.