Skip to content

Commit a3315d1

Browse files
author
Darrick J. Wong
committed
xfs: use rtgroup busy extent list for FITRIM
For filesystems that have rtgroups and hence use the busy extent list for freed rt space, use that busy extent list so that FITRIM can issue discard commands asynchronously without worrying about other callers accidentally allocating and using space that is being discarded. Signed-off-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de>
1 parent 7e85fc2 commit a3315d1

File tree

1 file changed

+144
-2
lines changed

1 file changed

+144
-2
lines changed

fs/xfs/xfs_discard.c

Lines changed: 144 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@
7373
* extent search so that it overlaps in flight discard IO.
7474
*/
7575

76+
#define XFS_DISCARD_MAX_EXAMINE (100)
77+
7678
struct workqueue_struct *xfs_discard_wq;
7779

7880
static void
@@ -185,7 +187,7 @@ xfs_trim_gather_extents(
185187
struct xfs_buf *agbp;
186188
int error;
187189
int i;
188-
int batch = 100;
190+
int batch = XFS_DISCARD_MAX_EXAMINE;
189191

190192
/*
191193
* Force out the log. This means any transactions that might have freed
@@ -565,6 +567,7 @@ xfs_trim_gather_rtextent(
565567
return 0;
566568
}
567569

570+
/* Trim extents on an !rtgroups realtime device */
568571
static int
569572
xfs_trim_rtextents(
570573
struct xfs_rtgroup *rtg,
@@ -619,6 +622,140 @@ xfs_trim_rtextents(
619622
return error;
620623
}
621624

625+
struct xfs_trim_rtgroup {
626+
/* list of rtgroup extents to free */
627+
struct xfs_busy_extents *extents;
628+
629+
/* minimum length that caller allows us to trim */
630+
xfs_rtblock_t minlen_fsb;
631+
632+
/* restart point for the rtbitmap walk */
633+
xfs_rtxnum_t restart_rtx;
634+
635+
/* number of extents to examine before stopping to issue discard ios */
636+
int batch;
637+
638+
/* number of extents queued for discard */
639+
int queued;
640+
};
641+
642+
static int
643+
xfs_trim_gather_rtgroup_extent(
644+
struct xfs_rtgroup *rtg,
645+
struct xfs_trans *tp,
646+
const struct xfs_rtalloc_rec *rec,
647+
void *priv)
648+
{
649+
struct xfs_trim_rtgroup *tr = priv;
650+
xfs_rgblock_t rgbno;
651+
xfs_extlen_t len;
652+
653+
if (--tr->batch <= 0) {
654+
/*
655+
* If we've checked a large number of extents, update the
656+
* cursor to point at this extent so we restart the next batch
657+
* from this extent.
658+
*/
659+
tr->restart_rtx = rec->ar_startext;
660+
return -ECANCELED;
661+
}
662+
663+
rgbno = xfs_rtx_to_rgbno(rtg, rec->ar_startext);
664+
len = xfs_rtxlen_to_extlen(rtg_mount(rtg), rec->ar_extcount);
665+
666+
/* Ignore too small. */
667+
if (len < tr->minlen_fsb) {
668+
trace_xfs_discard_toosmall(rtg_group(rtg), rgbno, len);
669+
return 0;
670+
}
671+
672+
/*
673+
* If any blocks in the range are still busy, skip the discard and try
674+
* again the next time.
675+
*/
676+
if (xfs_extent_busy_search(rtg_group(rtg), rgbno, len)) {
677+
trace_xfs_discard_busy(rtg_group(rtg), rgbno, len);
678+
return 0;
679+
}
680+
681+
xfs_extent_busy_insert_discard(rtg_group(rtg), rgbno, len,
682+
&tr->extents->extent_list);
683+
684+
tr->queued++;
685+
tr->restart_rtx = rec->ar_startext + rec->ar_extcount;
686+
return 0;
687+
}
688+
689+
/* Trim extents in this rtgroup using the busy extent machinery. */
690+
static int
691+
xfs_trim_rtgroup_extents(
692+
struct xfs_rtgroup *rtg,
693+
xfs_rtxnum_t low,
694+
xfs_rtxnum_t high,
695+
xfs_daddr_t minlen)
696+
{
697+
struct xfs_mount *mp = rtg_mount(rtg);
698+
struct xfs_trim_rtgroup tr = {
699+
.minlen_fsb = XFS_BB_TO_FSB(mp, minlen),
700+
};
701+
struct xfs_trans *tp;
702+
int error;
703+
704+
error = xfs_trans_alloc_empty(mp, &tp);
705+
if (error)
706+
return error;
707+
708+
/*
709+
* Walk the free ranges between low and high. The query_range function
710+
* trims the extents returned.
711+
*/
712+
do {
713+
tr.extents = kzalloc(sizeof(*tr.extents), GFP_KERNEL);
714+
if (!tr.extents) {
715+
error = -ENOMEM;
716+
break;
717+
}
718+
719+
tr.queued = 0;
720+
tr.batch = XFS_DISCARD_MAX_EXAMINE;
721+
tr.extents->owner = tr.extents;
722+
INIT_LIST_HEAD(&tr.extents->extent_list);
723+
724+
xfs_rtgroup_lock(rtg, XFS_RTGLOCK_BITMAP_SHARED);
725+
error = xfs_rtalloc_query_range(rtg, tp, low, high,
726+
xfs_trim_gather_rtgroup_extent, &tr);
727+
xfs_rtgroup_unlock(rtg, XFS_RTGLOCK_BITMAP_SHARED);
728+
if (error == -ECANCELED)
729+
error = 0;
730+
if (error) {
731+
kfree(tr.extents);
732+
break;
733+
}
734+
735+
if (!tr.queued)
736+
break;
737+
738+
/*
739+
* We hand the extent list to the discard function here so the
740+
* discarded extents can be removed from the busy extent list.
741+
* This allows the discards to run asynchronously with
742+
* gathering the next round of extents to discard.
743+
*
744+
* However, we must ensure that we do not reference the extent
745+
* list after this function call, as it may have been freed by
746+
* the time control returns to us.
747+
*/
748+
error = xfs_discard_extents(rtg_mount(rtg), tr.extents);
749+
if (error)
750+
break;
751+
752+
low = tr.restart_rtx;
753+
} while (!xfs_trim_should_stop() && low <= high);
754+
755+
xfs_trans_cancel(tp);
756+
return error;
757+
}
758+
622759
static int
623760
xfs_trim_rtdev_extents(
624761
struct xfs_mount *mp,
@@ -657,7 +794,12 @@ xfs_trim_rtdev_extents(
657794
if (rtg_rgno(rtg) == end_rgno)
658795
rtg_end = min(rtg_end, end_rtx);
659796

660-
error = xfs_trim_rtextents(rtg, start_rtx, rtg_end, minlen);
797+
if (xfs_has_rtgroups(mp))
798+
error = xfs_trim_rtgroup_extents(rtg, start_rtx,
799+
rtg_end, minlen);
800+
else
801+
error = xfs_trim_rtextents(rtg, start_rtx, rtg_end,
802+
minlen);
661803
if (error)
662804
last_error = error;
663805

0 commit comments

Comments
 (0)