[Ocfs2-tools-devel] [PATCH 1/1] Add truncate for "clear
sparse" function in tunefs,
Mark Fasheh
mark.fasheh at oracle.com
Tue Nov 13 21:22:48 PST 2007
On Wed, Nov 14, 2007 at 09:53:40AM +0800, tao.ma wrote:
> Mark Fasheh wrote:
>> This patch fails the following scenario:
>>
>> $ ./reserve_space foo resv 0 0 $[10*1024*1024]File:
>> foo
>> cmd = foo
>> l_whence = 0
>> l_start = 0
>> l_len = 10485760
>>
>> $ ./truncate foo 65536
>> Truncating foo to 65536
>>
>> [reserve_space and truncate are taken from ocfs2-test]
>>
>> $ debugfs.ocfs2 /dev/sde2
>> debugfs: stat foo
>> Inode: 72890 Mode: 0644 Generation: 4289956910 (0xffb38c2e)
>> FS Generation: 2776784650 (0xa5825f0a)
>> Type: Regular Attr: 0x0 Flags: Valid User: 0 (root)
>> Group: 0 (root) Size: 65536
>> Links: 1 Clusters: 2560
>> ctime: 0x4739f672 -- Wed Dec 31 16:00:00 1969
>> atime: 0x4739f671 -- Wed Dec 31 16:00:00 1969
>> mtime: 0x4739f672 -- Wed Dec 31 16:00:00 1969
>> dtime: 0x0 -- Wed Dec 31 16:00:00 1969
>> ctime_nsec: 0x3897a5e6 -- 949462502
>> atime_nsec: 0x010a910e -- 17469710
>> mtime_nsec: 0x3897a5e6 -- 949462502
>> Last Extblk: 0
>> Sub Alloc Slot: 0 Sub Alloc Bit: 185
>> Tree Depth: 0 Count: 243 Next Free Rec: 1
>> ## Offset Clusters Block# Flags
>> 0 0 2560 96769 0x0
>>
>> $ tunefs.ocfs2 --fs-features=nounwritten /dev/sde2
>> tunefs.ocfs2 1.4.0
>> We have 9688495 clusters free and need 0 clusters for sparse files, 0
>> clusters for more extent blocks and will need to clear 0 clusters of
>> unwritten extents.
>> Modify feature "nounwritten" for the volume
>> Proceed (y/N): y
>> Wrote Superblock
>>
>> $ debugfs.ocfs2 /dev/sde2debugfs.ocfs2 1.4.0
>> debugfs: stat foo
>> Inode: 72890 Mode: 0644 Generation: 4289956910 (0xffb38c2e)
>> FS Generation: 2776784650 (0xa5825f0a)
>> Type: Regular Attr: 0x0 Flags: Valid User: 0 (root)
>> Group: 0 (root) Size: 65536
>> Links: 1 Clusters: 2560
>> ctime: 0x4739f672 -- Wed Dec 31 16:00:00 1969
>> atime: 0x4739f671 -- Wed Dec 31 16:00:00 1969
>> mtime: 0x4739f672 -- Wed Dec 31 16:00:00 1969
>> dtime: 0x0 -- Wed Dec 31 16:00:00 1969
>> ctime_nsec: 0x3897a5e6 -- 949462502
>> atime_nsec: 0x010a910e -- 17469710
>> mtime_nsec: 0x3897a5e6 -- 949462502
>> Last Extblk: 0
>> Sub Alloc Slot: 0 Sub Alloc Bit: 185
>> Tree Depth: 0 Count: 243 Next Free Rec: 1
>> ## Offset Clusters Block# Flags
>> 0 0 2560 96769 0x0
>>
>> So, the existing unwritten extents code zero's the entire extent because
>> it's straddling i_size. Otherwise, the file isn't getting truncate which
>> is
>> not what I think you expected to happen.
>>
>> I found the problem, it's noted below...
>>
>>
>> On Tue, Nov 13, 2007 at 04:41:26PM +0800, tao.ma wrote:
>>
>>> take 2
>>>
>>> Modification from V1 to V2:
>>> Move truncate process to the end of operation.
>>> We may need to truncate an unwritten extent sometime. So if we truncate
>>> the file
>>> first, the unwritten extent information stored in clear_ctxt may be
>>> outdated.
>>> So move it to the end.
>>>
>>> Signed-off-by: Tao Ma <tao.ma at oracle.com>
>>> ---
>>> tunefs.ocfs2/sparse_file.c | 65
>>> +++++++++++++++++++++++++++++++++++++++-----
>>> 1 files changed, 58 insertions(+), 7 deletions(-)
>>>
>>> diff --git a/tunefs.ocfs2/sparse_file.c b/tunefs.ocfs2/sparse_file.c
>>> index 8ef6710..06782f6 100644
>>> --- a/tunefs.ocfs2/sparse_file.c
>>> +++ b/tunefs.ocfs2/sparse_file.c
>>> @@ -67,10 +67,14 @@ struct unwritten_list {
>>> * unwritten_list. Since filling up a hole may need a new extent record
>>> and
>>> * lead to some new extent block, the total hole number in the sparse
>>> file
>>> * will also be recorded.
>>> + *
>>> + * Some sparse file may also have some clusters which exceed the limit
>>> of
>>> + * i_size, and they should be truncated.
>>> */
>>> struct sparse_file {
>>> uint64_t blkno;
>>> uint32_t holes_num;
>>> + int truncate;
>>> struct hole_list *holes;
>>> struct unwritten_list *unwritten;
>>> struct sparse_file *next;
>>> @@ -208,6 +212,7 @@ static void list_sparse_iterate(void *priv_data,
>>> * Iterate a file.
>>> * Call "func" when we meet with a hole.
>>> * Call "unwritten_func" when we meet with unwritten clusters.
>>> + * Call "seen_exceed" when we see some clusters exceed i_size.
>>> */
>>> static errcode_t iterate_file(ocfs2_filesys *fs,
>>> struct ocfs2_dinode *di,
>>> @@ -218,10 +223,12 @@ static errcode_t iterate_file(ocfs2_filesys *fs,
>>> uint32_t start,
>>> uint32_t len,
>>> uint64_t p_start),
>>> + void (*seen_exceed)(void *priv_data),
>>> void *priv_data)
>>> {
>>> errcode_t ret;
>>> uint32_t clusters, v_cluster = 0, p_cluster, num_clusters;
>>> + uint32_t get_clusters = 0;
>>> uint64_t p_blkno;
>>> uint16_t extent_flags;
>>> ocfs2_cached_inode *ci = NULL;
>>> @@ -250,7 +257,8 @@ static errcode_t iterate_file(ocfs2_filesys *fs,
>>> if (func)
>>> func(priv_data, v_cluster, num_clusters);
>>> - }
>>> + } else
>>> + get_clusters += num_clusters;
>>> if ((extent_flags & OCFS2_EXT_UNWRITTEN) && unwritten_func) {
>>> p_blkno = ocfs2_clusters_to_blocks(fs, p_cluster);
>>> @@ -261,6 +269,17 @@ static errcode_t iterate_file(ocfs2_filesys *fs,
>>> v_cluster += num_clusters;
>>> }
>>> + /*
>>> + * If there are some extra clusters we need to record them.
>>> + * There are 2 scenarios:
>>> + * 1. there are more cotinguous clusters allocated in which case
>>> + * get_clusters > di->i_clusters
>>> + * 2. there are more clusters allocated after i_size in which case
>>> + * get_clusters < di->i_clusters
>>> + */
>>> + if (get_clusters != di->i_clusters && seen_exceed)
>>> + seen_exceed(priv_data);
>>>
>>
>> This needs to compare against 'clusters' (as computed above in that
>> function), not 'i_clusters'. i_clusters will always equal the total number
>> of clusters in the inode, irregardless of i_size, so you won't hit this in
>> the case that the last extent includes i_size, but goes too far.
>>
> Yeah, this fails in some scenario, but "clusters" computed above also
> doesn't work since for a file with holes, get_clusters(calculated from the
> allocated clusters) may be equal to "clusters". e.g, there is a 1 cluster
> hole before i_size and 1 cluster allocated after i_size.
> So let me generalize all the situation we may meet with for a truncate.
> 1. Non sparse file: Unwritten extent and written extent are calculated the
> same process.
> the check should be get_clusters > clusters or v_cluster > clusters
> 2. Sparse file:
> 1) We have iterate to the end of 'clusters' but there are still allocated
> clusters after i_size, in which case
> get_clusters < di->i_clusters.
> 2) the i_size resides in the last extent rec and it has been allocated with
> more clusters: v_cluster > clusters
>
>
> So the general check should be:
> if ((get_clusters <di->i_clusters || v_cluster > clusters) && seen_exceed)
This is getting a bit confusing... What if we just provide a helper function
in libocfs2 to return the rightmost extent? It should be pretty easy to get
it from i_list in the tree_depth = 0 case, or just use i_last_eb_blk to read
the rightmost extent block in the tree_depth > 0 case.
You'd just compare the end of that extent with 'clusters' to figure out
whether we need to truncate...
--Mark
--
Mark Fasheh
Senior Software Developer, Oracle
mark.fasheh at oracle.com
More information about the Ocfs2-tools-devel
mailing list