Re: Introduce SEEK_DATA/SEEK_HOLE to XFS V5

Jeff Liu
2012-01-12T13:22:04Z

Hi Ben,

Thanks a lot for your so much detailed info!

On 01/12/2012 06:28 AM, Ben Myers wrote:

> Hey Jeff,
>
> On Wed, Jan 11, 2012 at 09:43:18AM -0600, Ben Myers wrote:
>> Here are a few additional minor comments from yesterday.
>>
>> I'm looking forward to seeing your next version, and I'm still working
>> through this one.
>>
>> I would like to suggest that you split this into two patches. The first
>> patch should be the 'simple' implementation that that you began with
>> that only looks at extents, and assumes that unwritten extents contain
>> data. The second patch can remove the assumption that unwritten extents
>> contain data, and go over pages over the extent to determine if it is
>> clean. I feel we have a better chance of coming to consensus that the
>> first patch is correct in the near term, and then we can move on to the
>> more complicated matter of whether the unwritten extent can be treated
>> as a hole safe in the knowledge that the initial implementation was
>> awesome.
>
> Ok, since I'm the jackass who is asking you to do the extra work I'll
> try to be of assistance. Understand that at this point I'm trying to
> make sure that I understand your code fully. I'm not trying to give you
> a hard time or make your life miserable.

>
> Here I am assuming that we'll treat unwritten extents as containing data
> and leaving the enhancement of probing unwritten extents for later.

Do you means I only need to post a patch to treat unwritten extents as
data next time, and then try to work out another patch for probing
unwritten extents until the first one became stable?

>
> This is a table of some of the results from xfs_bmapi_read, and what
> should be done in each situation.
>
> SEEK_DATA:
>
> Where nmap = 0:
> return ENXIO. * maybe not possible, unless len = 0?

Per my previous tryout, this situation can be triggered when no extent
behind the seek offset for SEEK_HOLE; for SEEK_DATA, it will be caught
by the following checking:
if (start >= isize)
return -ENXIO;

>
> Where nmap = 1:
> map[0]
> written data @ offset
> delay data @ offset
> unwritten data @ offset
> hole return ENXIO? * empty file?
>
> Where nmap = 2:
> map[0] map[1]
> written written data @ offset
> written delay data @ offset
> written unwritten data @ offset
> written hole data @ offset
> delay written data @ offset
> delay delay data @ offset * maybe not possible?

Hmm, maybe we can design a case to trigger it out later. :-P.

I'm going to write the patch by referring to the following codes.

> delay unwritten data @ offset
> delay hole data @ offset
> unwritten written data @ offset
> unwritten delay data @ offset
> unwritten unwritten data @ offset
> unwritten hole data @ offset
> hole written data @ map[1].br_startoff
> hole delay data @ map[1].br_startoff
> hole unwritten data @ map[1].br_startoff
> hole hole * not possible
>
> (DELAYSTARTBLOCK and HOLESTARTBLOCK are both 'isnullstartblock')
>
> written:
> (!isnullstartblock(map.br_startblock) && map.br_state == XFS_EXT_NORMAL)
> delay:
> map.br_startblock == DELAYSTARTBLOCK
>
> unwritten:
> map.br_state == XFS_EXT_UNWRITTEN
>
> hole:
> map.br_startblock == HOLESTARTBLOCK
>
> xfs_seek_data(file, startoff)
> {
> loff_t offset;
> int error;
>
> take ilock
>
> isize = i_size_read
>
> start_fsb = XFS_B_TO_FSBT(startoff)
> end_fsb = XFS_B_TO_FSB(i_size) # inode size
>
> error = xfs_bmapi_read(map, &nmap)
> if (error)
> goto out_unlock;
>
> if (nmap == 0) {
> /*
> * return an error. I'm not sure that this necessarily
> * means we're reading after EOF, since it looks like
> * xfs_bmapi_read would return one hole in that case.
> */
>
> error = ERROR /* EIO? */
> goto out_unlock
> }
>
> /* check map[0] first */
> if (map[0].br_state == XFS_EXT_NORMAL &&
> !isnullstartblock(map[0].br_startblock) {
> /*
> * startoff is already within data. remember
> * that it can anywhere within start_fsb
> */
> offset = startoff
> } else if (map[0].br_startblock == DELAYSTARTBLOCK) {
> offset = startoff
> } else if (map[0].br_state == XFS_EXT_UNWRITTEN) {
> offset = startoff;
> } else if (map[0].br_startblock == HOLESTARTBLOCK) {
> if (nmap == 1) {
> /*
> * finding a hole in map[0] and nothing in
> * map[1] probably means that we are reading
> * after eof
> */
> ASSERT(startoff >= isize)
> error = ENXIO
> goto out_unlock
> }
>
> /*
> * we have two mappings, and need to check map[1] to see
> * if there is data.
> */
> if (map[1].br_state == XFS_EXT_NORMAL &&
> !isnullstartblock(map[1].br_startblock)) {
> offset = XFS_FSB_TO_B(map[1].br_startoff);
> } else if (map[1].br_startblock == DELAYSTARTBLOCK) {
> offset = XFS_FSB_TO_B(map[1].br_startoff);
> } else if (map[1].br_state == XFS_EXT_UNWRITTEN) {
> offset = XFS_FSB_TO_B(map[1].br_startoff);
> } else if (map[1].br_startblock == HOLESTARTBLOCK) {
> /*
> * this should never happen, but we could
> */
> ASSERT(startoff >= isize);
> error = ENXIO
> /* BUG(); */
> } else {
> offset = startoff
> /* BUG(); */
> }
> } else {
> offset = startoff
> /* BUG(); */
> }
> out_unlock:
> drop ilock
> if (error)
> return -error;
>
> return offset;
> }
>
> I think that is sufficiently straightforward that even I can understand
> it, or am I off my rocker? IMO it's not that bad that we have to write
> the if/else to determine extent type twice and that there is some
> duplication when setting the offset. When you come back to enhance it
> further by probing unwritten extents I think a goto would probably be
> more readable than trying to shoehorn this into a for/do, but that's
> just me.
>
> Jeff, I hope that doesn't ruffle any feathers. I know I came to the
> party a bit late. After a break I am going to go look at your code for
> xfs_seek_data again. I think I'll understand it better now. After that
> I am going to look into SEEK_HOLE...

Thanks you!
-Jeff

>
> Regards,
> Ben
>
> _______________________________________________
> xfs mailing list
> xfs@oss.sgi.com
> http://oss.sgi.com/mailman/listinfo/xfs