[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