Skip to content

Commit f86f403

Browse files
committed
xfs: teach get_bmapx about shared extents and the CoW fork
Teach xfs_getbmapx how to report shared extents and CoW fork contents accurately in the bmap output by querying the refcount btree appropriately. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Christoph Hellwig <hch@lst.de>
1 parent cc71466 commit f86f403

File tree

2 files changed

+143
-15
lines changed

2 files changed

+143
-15
lines changed

fs/xfs/libxfs/xfs_fs.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,16 @@ struct getbmapx {
8181
#define BMV_IF_PREALLOC 0x4 /* rtn status BMV_OF_PREALLOC if req */
8282
#define BMV_IF_DELALLOC 0x8 /* rtn status BMV_OF_DELALLOC if req */
8383
#define BMV_IF_NO_HOLES 0x10 /* Do not return holes */
84+
#define BMV_IF_COWFORK 0x20 /* return CoW fork rather than data */
8485
#define BMV_IF_VALID \
8586
(BMV_IF_ATTRFORK|BMV_IF_NO_DMAPI_READ|BMV_IF_PREALLOC| \
86-
BMV_IF_DELALLOC|BMV_IF_NO_HOLES)
87+
BMV_IF_DELALLOC|BMV_IF_NO_HOLES|BMV_IF_COWFORK)
8788

8889
/* bmv_oflags values - returned for each non-header segment */
8990
#define BMV_OF_PREALLOC 0x1 /* segment = unwritten pre-allocation */
9091
#define BMV_OF_DELALLOC 0x2 /* segment = delayed allocation */
9192
#define BMV_OF_LAST 0x4 /* segment is the last in the file */
93+
#define BMV_OF_SHARED 0x8 /* segment shared with another file */
9294

9395
/*
9496
* Structure for XFS_IOC_FSSETDM.

fs/xfs/xfs_bmap_util.c

Lines changed: 140 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@
4242
#include "xfs_icache.h"
4343
#include "xfs_log.h"
4444
#include "xfs_rmap_btree.h"
45+
#include "xfs_iomap.h"
46+
#include "xfs_reflink.h"
47+
#include "xfs_refcount.h"
4548

4649
/* Kernel only BMAP related definitions and functions */
4750

@@ -389,11 +392,13 @@ xfs_bmap_count_blocks(
389392
STATIC int
390393
xfs_getbmapx_fix_eof_hole(
391394
xfs_inode_t *ip, /* xfs incore inode pointer */
395+
int whichfork,
392396
struct getbmapx *out, /* output structure */
393397
int prealloced, /* this is a file with
394398
* preallocated data space */
395399
__int64_t end, /* last block requested */
396-
xfs_fsblock_t startblock)
400+
xfs_fsblock_t startblock,
401+
bool moretocome)
397402
{
398403
__int64_t fixlen;
399404
xfs_mount_t *mp; /* file system mount point */
@@ -418,15 +423,91 @@ xfs_getbmapx_fix_eof_hole(
418423
else
419424
out->bmv_block = xfs_fsb_to_db(ip, startblock);
420425
fileblock = XFS_BB_TO_FSB(ip->i_mount, out->bmv_offset);
421-
ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
422-
if (xfs_iext_bno_to_ext(ifp, fileblock, &lastx) &&
426+
ifp = XFS_IFORK_PTR(ip, whichfork);
427+
if (!moretocome &&
428+
xfs_iext_bno_to_ext(ifp, fileblock, &lastx) &&
423429
(lastx == (ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t))-1))
424430
out->bmv_oflags |= BMV_OF_LAST;
425431
}
426432

427433
return 1;
428434
}
429435

436+
/* Adjust the reported bmap around shared/unshared extent transitions. */
437+
STATIC int
438+
xfs_getbmap_adjust_shared(
439+
struct xfs_inode *ip,
440+
int whichfork,
441+
struct xfs_bmbt_irec *map,
442+
struct getbmapx *out,
443+
struct xfs_bmbt_irec *next_map)
444+
{
445+
struct xfs_mount *mp = ip->i_mount;
446+
xfs_agnumber_t agno;
447+
xfs_agblock_t agbno;
448+
xfs_agblock_t ebno;
449+
xfs_extlen_t elen;
450+
xfs_extlen_t nlen;
451+
int error;
452+
453+
next_map->br_startblock = NULLFSBLOCK;
454+
next_map->br_startoff = NULLFILEOFF;
455+
next_map->br_blockcount = 0;
456+
457+
/* Only written data blocks can be shared. */
458+
if (!xfs_is_reflink_inode(ip) || whichfork != XFS_DATA_FORK ||
459+
map->br_startblock == DELAYSTARTBLOCK ||
460+
map->br_startblock == HOLESTARTBLOCK ||
461+
ISUNWRITTEN(map))
462+
return 0;
463+
464+
agno = XFS_FSB_TO_AGNO(mp, map->br_startblock);
465+
agbno = XFS_FSB_TO_AGBNO(mp, map->br_startblock);
466+
error = xfs_reflink_find_shared(mp, agno, agbno, map->br_blockcount,
467+
&ebno, &elen, true);
468+
if (error)
469+
return error;
470+
471+
if (ebno == NULLAGBLOCK) {
472+
/* No shared blocks at all. */
473+
return 0;
474+
} else if (agbno == ebno) {
475+
/*
476+
* Shared extent at (agbno, elen). Shrink the reported
477+
* extent length and prepare to move the start of map[i]
478+
* to agbno+elen, with the aim of (re)formatting the new
479+
* map[i] the next time through the inner loop.
480+
*/
481+
out->bmv_length = XFS_FSB_TO_BB(mp, elen);
482+
out->bmv_oflags |= BMV_OF_SHARED;
483+
if (elen != map->br_blockcount) {
484+
*next_map = *map;
485+
next_map->br_startblock += elen;
486+
next_map->br_startoff += elen;
487+
next_map->br_blockcount -= elen;
488+
}
489+
map->br_blockcount -= elen;
490+
} else {
491+
/*
492+
* There's an unshared extent (agbno, ebno - agbno)
493+
* followed by shared extent at (ebno, elen). Shrink
494+
* the reported extent length to cover only the unshared
495+
* extent and prepare to move up the start of map[i] to
496+
* ebno, with the aim of (re)formatting the new map[i]
497+
* the next time through the inner loop.
498+
*/
499+
*next_map = *map;
500+
nlen = ebno - agbno;
501+
out->bmv_length = XFS_FSB_TO_BB(mp, nlen);
502+
next_map->br_startblock += nlen;
503+
next_map->br_startoff += nlen;
504+
next_map->br_blockcount -= nlen;
505+
map->br_blockcount -= nlen;
506+
}
507+
508+
return 0;
509+
}
510+
430511
/*
431512
* Get inode's extents as described in bmv, and format for output.
432513
* Calls formatter to fill the user's buffer until all extents
@@ -459,12 +540,28 @@ xfs_getbmap(
459540
int iflags; /* interface flags */
460541
int bmapi_flags; /* flags for xfs_bmapi */
461542
int cur_ext = 0;
543+
struct xfs_bmbt_irec inject_map;
462544

463545
mp = ip->i_mount;
464546
iflags = bmv->bmv_iflags;
465-
whichfork = iflags & BMV_IF_ATTRFORK ? XFS_ATTR_FORK : XFS_DATA_FORK;
466547

467-
if (whichfork == XFS_ATTR_FORK) {
548+
#ifndef DEBUG
549+
/* Only allow CoW fork queries if we're debugging. */
550+
if (iflags & BMV_IF_COWFORK)
551+
return -EINVAL;
552+
#endif
553+
if ((iflags & BMV_IF_ATTRFORK) && (iflags & BMV_IF_COWFORK))
554+
return -EINVAL;
555+
556+
if (iflags & BMV_IF_ATTRFORK)
557+
whichfork = XFS_ATTR_FORK;
558+
else if (iflags & BMV_IF_COWFORK)
559+
whichfork = XFS_COW_FORK;
560+
else
561+
whichfork = XFS_DATA_FORK;
562+
563+
switch (whichfork) {
564+
case XFS_ATTR_FORK:
468565
if (XFS_IFORK_Q(ip)) {
469566
if (ip->i_d.di_aformat != XFS_DINODE_FMT_EXTENTS &&
470567
ip->i_d.di_aformat != XFS_DINODE_FMT_BTREE &&
@@ -480,7 +577,15 @@ xfs_getbmap(
480577

481578
prealloced = 0;
482579
fixlen = 1LL << 32;
483-
} else {
580+
break;
581+
case XFS_COW_FORK:
582+
if (ip->i_cformat != XFS_DINODE_FMT_EXTENTS)
583+
return -EINVAL;
584+
585+
prealloced = 0;
586+
fixlen = XFS_ISIZE(ip);
587+
break;
588+
default:
484589
if (ip->i_d.di_format != XFS_DINODE_FMT_EXTENTS &&
485590
ip->i_d.di_format != XFS_DINODE_FMT_BTREE &&
486591
ip->i_d.di_format != XFS_DINODE_FMT_LOCAL)
@@ -494,6 +599,7 @@ xfs_getbmap(
494599
prealloced = 0;
495600
fixlen = XFS_ISIZE(ip);
496601
}
602+
break;
497603
}
498604

499605
if (bmv->bmv_length == -1) {
@@ -520,7 +626,8 @@ xfs_getbmap(
520626
return -ENOMEM;
521627

522628
xfs_ilock(ip, XFS_IOLOCK_SHARED);
523-
if (whichfork == XFS_DATA_FORK) {
629+
switch (whichfork) {
630+
case XFS_DATA_FORK:
524631
if (!(iflags & BMV_IF_DELALLOC) &&
525632
(ip->i_delayed_blks || XFS_ISIZE(ip) > ip->i_d.di_size)) {
526633
error = filemap_write_and_wait(VFS_I(ip)->i_mapping);
@@ -538,8 +645,14 @@ xfs_getbmap(
538645
}
539646

540647
lock = xfs_ilock_data_map_shared(ip);
541-
} else {
648+
break;
649+
case XFS_COW_FORK:
650+
lock = XFS_ILOCK_SHARED;
651+
xfs_ilock(ip, lock);
652+
break;
653+
case XFS_ATTR_FORK:
542654
lock = xfs_ilock_attr_map_shared(ip);
655+
break;
543656
}
544657

545658
/*
@@ -581,7 +694,8 @@ xfs_getbmap(
581694
goto out_free_map;
582695
ASSERT(nmap <= subnex);
583696

584-
for (i = 0; i < nmap && nexleft && bmv->bmv_length; i++) {
697+
for (i = 0; i < nmap && nexleft && bmv->bmv_length &&
698+
cur_ext < bmv->bmv_count; i++) {
585699
out[cur_ext].bmv_oflags = 0;
586700
if (map[i].br_state == XFS_EXT_UNWRITTEN)
587701
out[cur_ext].bmv_oflags |= BMV_OF_PREALLOC;
@@ -614,9 +728,16 @@ xfs_getbmap(
614728
goto out_free_map;
615729
}
616730

617-
if (!xfs_getbmapx_fix_eof_hole(ip, &out[cur_ext],
618-
prealloced, bmvend,
619-
map[i].br_startblock))
731+
/* Is this a shared block? */
732+
error = xfs_getbmap_adjust_shared(ip, whichfork,
733+
&map[i], &out[cur_ext], &inject_map);
734+
if (error)
735+
goto out_free_map;
736+
737+
if (!xfs_getbmapx_fix_eof_hole(ip, whichfork,
738+
&out[cur_ext], prealloced, bmvend,
739+
map[i].br_startblock,
740+
inject_map.br_startblock != NULLFSBLOCK))
620741
goto out_free_map;
621742

622743
bmv->bmv_offset =
@@ -636,11 +757,16 @@ xfs_getbmap(
636757
continue;
637758
}
638759

639-
nexleft--;
760+
if (inject_map.br_startblock != NULLFSBLOCK) {
761+
map[i] = inject_map;
762+
i--;
763+
} else
764+
nexleft--;
640765
bmv->bmv_entries++;
641766
cur_ext++;
642767
}
643-
} while (nmap && nexleft && bmv->bmv_length);
768+
} while (nmap && nexleft && bmv->bmv_length &&
769+
cur_ext < bmv->bmv_count);
644770

645771
out_free_map:
646772
kmem_free(map);

0 commit comments

Comments
 (0)