[Ocfs2-tools-devel] [PATCH 1/1] Add truncate for "clear sparse" function in tunefs,

tao.ma tao.ma at oracle.com
Tue Nov 13 17:53:40 PST 2007


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)

Agree?



More information about the Ocfs2-tools-devel mailing list